From: <he...@us...> - 2010-01-04 22:58:50
|
Revision: 8074 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=8074&view=rev Author: heeres Date: 2010-01-04 22:58:43 +0000 (Mon, 04 Jan 2010) Log Message: ----------- Add EngFormatter + example (Jason Heeris, Matthias Michler) Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/ticker.py Added Paths: ----------- trunk/matplotlib/examples/api/engineering_formatter.py Added: trunk/matplotlib/examples/api/engineering_formatter.py =================================================================== --- trunk/matplotlib/examples/api/engineering_formatter.py (rev 0) +++ trunk/matplotlib/examples/api/engineering_formatter.py 2010-01-04 22:58:43 UTC (rev 8074) @@ -0,0 +1,19 @@ +''' +Demo to show use of the engineering Formatter. +''' + +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.ticker import EngFormatter + +ax = plt.subplot(111) +ax.set_xscale('log') +formatter = EngFormatter(unit='Hz', places=1) +ax.xaxis.set_major_formatter(formatter) + +xs = np.logspace(1, 9, 100) +ys = (0.8 + 0.4 * np.random.uniform(size=100)) * np.log10(xs)**2 +ax.plot(xs, ys) + +plt.show() Modified: trunk/matplotlib/lib/matplotlib/ticker.py =================================================================== --- trunk/matplotlib/lib/matplotlib/ticker.py 2010-01-04 17:22:32 UTC (rev 8073) +++ trunk/matplotlib/lib/matplotlib/ticker.py 2010-01-04 22:58:43 UTC (rev 8074) @@ -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,97 @@ 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. + """ + # 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" % self.places) + + formatted = format_str % (mant, prefix) + + return formatted.strip() + class Locator(TickHelper): """ Determine the tick locations; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |