Hi Jason, Hi list, First of all let me say I like the EngFormatter of Jason. Are there plans to incorparate it into matplotlib? I cannot find any indication for this in current svn, but I would like to see the EngFormatter in matplotlib. Therefore I tried to include Jasons proposal into the ticker.py as a new class EngFormatter including the method 'self.format_eng'. I made some changes, which might break some of Jasons ideas, but I cannot see (1) why we need the LogFormatter as base class instead of Formatter? (2) why one should decide for only decade-labels. Therefore I removed the corresponding if-statement. It causes errors () if you use an axes including '0', which could be useful for eng-formatting, too. (3) Could we always use format_str = "%g %s" instead of specifying 'places'? Because of successive zomming I don't want to specify 'places' before calling 'plt.show'. Therefore I introduced "places =None" to use "%g %s".
I attached the new ticker.py and a diff against current svn (I'm sorry I couldn't resist to add some white spaces). Any comments are welcome. Kind regards, Matthias Michler On Wednesday 25 November 2009 01:39:43 Jason Heeris wrote: > Hi, > > 2009/11/18 Jason Heeris <jason.hee...@gmail.com>: > > In gnuplot, I can do the following: > > > > set format x "%.0s %cHz" > > > > ...and this will set the x-axis labels (on a semilogx style plot) to > > be "10 Hz", "100 Hz", "1 kHz", "10 kHz", etc. > > I ended up implementing this myself, it wasn't too hard. I've attached > the code if anyone else is interested. I don't know matplotlib that > well, so I don't know if there's much duplication of code in there. > > I thought I'd CC the dev list in case others think it might be useful. > If not, sorry for the noise. > > Cheers, > Jason
Index: ticker.py =================================================================== --- ticker.py (revision 8000) +++ ticker.py (working copy) @@ -118,6 +118,7 @@ from __future__ import division +import decimal import math import numpy as np from matplotlib import rcParams @@ -507,23 +508,23 @@ is ``False`` """ self._base = base+0.0 - self.labelOnlyBase=labelOnlyBase + self.labelOnlyBase = labelOnlyBase self.decadeOnly = True - def base(self,base): + def base(self, base): 'change the *base* for labeling - warning: should always match the base used for :class:`LogLocator`' - self._base=base + self._base = base - def label_minor(self,labelOnlyBase): + def label_minor(self, labelOnlyBase): 'switch on/off minor ticks labeling' - self.labelOnlyBase=labelOnlyBase + self.labelOnlyBase = labelOnlyBase def __call__(self, x, pos=None): 'Return the format for tick val *x* at position *pos*' vmin, vmax = self.axis.get_view_interval() d = abs(vmax - vmin) - b=self._base + b = self._base if x == 0.0: return '0' sign = np.sign(x) @@ -533,13 +534,13 @@ if not isDecade and self.labelOnlyBase: s = '' elif x>10000: s= '%1.0e'%x elif x<1: s = '%1.0e'%x - else : s = self.pprint_val(x,d) + else : s = self.pprint_val(x, d) if sign == -1: s = '-%s' % s return self.fix_minus(s) - def format_data(self,value): + def format_data(self, value): self.labelOnlyBase = False value = cbook.strip_math(self.__call__(value)) self.labelOnlyBase = True @@ -554,14 +555,14 @@ return abs(x-n)<1e-10 def nearest_long(self, x): - if x==0: return 0L - elif x>0: return long(x+0.5) + if x == 0: return 0L + elif x > 0: return long(x+0.5) else: return long(x-0.5) def pprint_val(self, x, d): #if the number is not too big and it's an int, format it as an #int - if abs(x)<1e4 and x==int(x): return '%d' % x + if abs(x) < 1e4 and x == int(x): return '%d' % x if d < 1e-2: fmt = '%1.3e' elif d < 1e-1: fmt = '%1.3f' @@ -572,7 +573,7 @@ s = fmt % x #print d, x, fmt, s tup = s.split('e') - if len(tup)==2: + if len(tup) == 2: mantissa = tup[0].rstrip('0').rstrip('.') sign = tup[1][0].replace('+', '') exponent = tup[1][1:].lstrip('0') @@ -645,11 +646,106 @@ if usetex: s = r'$%s%d^{%d}$'% (sign_string, b, self.nearest_long(fx)) else: - s = r'$\mathdefault{%s%d^{%d}}$'% (sign_string, b, self.nearest_long(fx)) + s = r'$\mathdefault{%s%d^{%d}}$'% (sign_string, b, + self.nearest_long(fx)) return s +class EngFormatter(Formatter): + """ + Formats axis values using engineering prefixes to represent powers of 1000, + plus a specified unit, eg. 10 MHz instead of 1e7. + example: + from matplotlib.ticker import EngFormatter + formatter = EngFormatter(unit='Hz') + ax = plt.subplot(111) + ax.xaxis.set_major_formatter(formatter) + ax.plot(np.arange(100), np.random.uniform(size=100)) + + """ + # The SI engineering prefixes + ENG_PREFIXES = { + -24: "y", + -21: "z", + -18: "a", + -15: "f", + -12: "p", + -9: "n", + -6: u"\u03bc", # Greek letter mu + -3: "m", + 0: "", + 3: "k", + 6: "M", + 9: "G", + 12: "T", + 15: "P", + 18: "E", + 21: "Z", + 24: "Y" + } + + def __init__(self, unit="", places=None): + self.unit = unit + self.places = places + + def __call__(self, x, pos=None): + + s = "%s%s" % (self.format_eng(x), self.unit) + + return self.fix_minus(s) + + def format_eng(self, num): + """ Formats a number in engineering notation, appending a letter + representing the power of 1000 of the original number. Some examples: + + >>> format_eng(0) for self.places = 0 + '0' + + >>> format_eng(1000000) for self.places = 1 + '1.0 M' + + >>> format_eng("-1e-6") for self.places = 2 + u'-1.00 \u03bc' + + @param num: the value to represent + @type num: either a numeric value or a string that can be converted to a + numeric value (as per decimal.Decimal constructor) + + @return: engineering formatted string + """ + + dnum = decimal.Decimal(str(num)) + + sign = 1 + + if dnum < 0: + sign = -1 + dnum = -dnum + + if dnum != 0: + pow10 = decimal.Decimal(int(math.floor(dnum.log10()/3)*3)) + else: + pow10 = decimal.Decimal(0) + + pow10 = pow10.min(max(self.ENG_PREFIXES.keys())) + pow10 = pow10.max(min(self.ENG_PREFIXES.keys())) + + prefix = self.ENG_PREFIXES[int(pow10)] + + mant = sign*dnum/(10**pow10) + + if self.places is None: + format_str = u"%g %s" + elif self.places == 0: + format_str = u"%i %s" + elif self.places > 0: + format_str = (u"%%.%if %%s" % places) + + formatted = format_str % (mant, prefix) + + return formatted.strip() + class Locator(TickHelper): """ Determine the tick locations;
ticker.py
Description: application/python
------------------------------------------------------------------------------ Join us December 9, 2009 for the Red Hat Virtual Experience, a free event focused on virtualization and cloud computing. Attend in-depth sessions from your desk. Your couch. Anywhere. http://p.sf.net/sfu/redhat-sfdev2dev
_______________________________________________ Matplotlib-users mailing list Matplotlib-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-users