111111import sys , os , re , time , math , warnings
112112from mlab import linspace
113113from matplotlib import verbose
114- from numerix import arange , array , asarray , ones , zeros , \
115- nonzero , take , Float , log , logical_and
114+ from numerix import absolute , arange , array , asarray , average , Float , floor , log , \
115+ logical_and , nonzero , ones , take , zeros
116+ from matplotlib .numerix .mlab import amin , amax , std
117+ from matplotlib .mlab import frange
118+ from cbook import strip_math
116119
117120class TickHelper :
118121
@@ -144,6 +147,12 @@ class Formatter(TickHelper):
144147 def __call__ (self , x , pos = 0 ):
145148 'Return the format for tick val x at position pos'
146149 raise NotImplementedError ('Derived must overide' )
150+
151+ def format_data (self ,value ):
152+ return self .__call__ (value )
153+
154+ def get_offset (self ):
155+ return ''
147156
148157 def set_locs (self , locs ):
149158 self .locs = locs
@@ -190,10 +199,8 @@ def __init__(self, fmt):
190199 def __call__ (self , x , pos = 0 ):
191200 'Return the format for tick val x at position pos'
192201 return self .fmt % x
193-
194-
195-
196-
202+
203+
197204class ScalarFormatter (Formatter ):
198205 """
199206 Tick location is a plain old number. If viewInterval is set, the
@@ -231,7 +238,116 @@ def pprint_val(self, x, d):
231238 s = s .rstrip ('0' ).rstrip ('.' )
232239 return s
233240
234- class LogFormatter (ScalarFormatter ):
241+
242+ class NewScalarFormatter (Formatter ):
243+ """
244+ Tick location is a plain old number. If useOffset==True and the data range
245+ <1e-4* the data average, then an offset will be determined such that the
246+ tick labels are meaningful. Scientific notation is used for data < 1e-4 or
247+ data >= 1e4. Scientific notation is presented once for each axis, in the
248+ last ticklabel.
249+ """
250+ def __init__ (self , useOffset = True ):
251+ """
252+ useOffset allows plotting small data ranges with large offsets:
253+ for example: [1+1e-9,1+2e-9,1+3e-9]
254+ """
255+ self ._useOffset = useOffset
256+ self .offset = 0
257+ self .orderOfMagnitude = 0
258+ self .format = ''
259+
260+ def __call__ (self , x , pos = 0 ):
261+ 'Return the format for tick val x at position pos'
262+ self .verify_intervals ()
263+ d = abs (self .viewInterval .span ())
264+ if self ._useOffset : self ._set_offset (d )
265+ self ._set_orderOfMagnitude (d )
266+ self ._set_format ()
267+ return self .pprint_val (x )
268+
269+ def format_data (self ,value ):
270+ 'return a formatted string representation of a number'
271+ s = '%1.5e' % value
272+ return self ._formatSciNotation (s )
273+
274+ def get_offset (self ):
275+ """Return scientific notation, plus offset"""
276+ if self .orderOfMagnitude or self .offset :
277+ offsetStr = ''
278+ sciNotStr = ''
279+ ## if self.offset:
280+ ## p = ('+%1.10e'% self.offset).replace('+-','-')
281+ ## offsetStr = self._formatSciNotation(p).replace('e',r'\times 10^{') + '}'
282+ ## if self.orderOfMagnitude:
283+ ## p = '%1.e'% 10**self.orderOfMagnitude
284+ ## sciNotStr = self._formatSciNotation(p).replace('1e',r'\times 10^{') + '}'
285+ ## return ''.join(('$',sciNotStr,offsetStr,'$'))
286+ if self .offset :
287+ p = ('+%1.10e' % self .offset ).replace ('+-' ,'-' )
288+ offsetStr = self ._formatSciNotation (p )
289+ if self .orderOfMagnitude :
290+ p = '%1.e' % 10 ** self .orderOfMagnitude
291+ sciNotStr = self ._formatSciNotation (p )
292+ return '' .join ((sciNotStr ,offsetStr ))
293+ else : return ''
294+
295+ def set_locs (self , locs ):
296+ self .locs = locs
297+
298+ def _set_offset (self , range ):
299+ # offset of 20,001 is 20,000, for example
300+ locs = self .locs
301+ ave_loc = average (locs )
302+ if ave_loc : # dont want to take log10(0)
303+ ave_oom = math .floor (math .log10 (absolute (ave_loc )))
304+ range_oom = math .ceil (math .log10 (range ))
305+ if absolute (ave_oom - range_oom ) >= 4 : # four sig-figs
306+ if ave_loc < 0 : self .offset = math .floor (amax (locs )/ 10 ** range_oom )* 10 ** range_oom
307+ else : self .offset = math .floor (amin (locs )/ 10 ** range_oom )* 10 ** range_oom
308+ else : self .offset = 0
309+
310+ def _set_orderOfMagnitude (self ,range ):
311+ # if scientific notation is to be used, find the appropriate exponent
312+ # if using an numerical offset, find the exponent after applying the offset
313+ locs = absolute (self .locs )
314+ if self ._useOffset : oom = math .floor (math .log10 (range ))
315+ else :
316+ if locs [0 ] > locs [- 1 ]: oom = math .floor (math .log10 (locs [0 ]))
317+ else : oom = math .floor (math .log10 (locs [- 1 ]))
318+ if oom <= - 3 :
319+ self .orderOfMagnitude = oom
320+ elif oom >= 4 :
321+ self .orderOfMagnitude = oom
322+ else :
323+ self .orderOfMagnitude = 0
324+
325+ def _set_format (self ):
326+ # set the format string to format all the ticklabels
327+ locs = (array (self .locs )- self .offset ) / 10 ** self .orderOfMagnitude + 1e-15
328+ sigfigs = [len (str ('%1.3f' % loc ).split ('.' )[1 ].rstrip ('0' )) \
329+ for loc in locs ]
330+ sigfigs .sort ()
331+ self .format = '%1.' + str (sigfigs [- 1 ]) + 'f'
332+
333+ def pprint_val (self , x ):
334+ xp = (x - self .offset )/ 10 ** self .orderOfMagnitude
335+ if closeto (xp ,0 ): return '0'
336+ else : return self .format % xp
337+
338+ def _formatSciNotation (self ,s ):
339+ # transform 1e+004 into 1e4, for example
340+ tup = s .split ('e' )
341+ try :
342+ mantissa = tup [0 ].rstrip ('0' ).rstrip ('.' )
343+ sign = tup [1 ][0 ].replace ('+' , '' )
344+ exponent = tup [1 ][1 :].lstrip ('0' )
345+ return ('%se%s%s' % (mantissa , sign , exponent )).rstrip ('e' )
346+ except IndexError ,msg :
347+ return s
348+
349+
350+ class LogFormatter (Formatter ):
235351 """
236352 Format values for log axis;
237353
@@ -270,6 +386,12 @@ def __call__(self, x, pos=0):
270386 else : s = self .pprint_val (x ,d )
271387 return s
272388
389+ def format_data (self ,value ):
390+ self .labelOnlyBase = False
391+ value = strip_math (self .__call__ (value ))
392+ self .labelOnlyBase = True
393+ return value
394+
273395 def is_decade (self , x ):
274396 n = self .nearest_long (x )
275397 return abs (x - n )< 1e-10
@@ -278,6 +400,29 @@ def nearest_long(self, x):
278400 if x == 0 : return 0L
279401 elif x > 0 : return long (x + 0.5 )
280402 else : return long (x - 0.5 )
403+
404+ def pprint_val (self , x , d ):
405+ #if the number is not too big and it's an int, format it as an
406+ #int
407+ if abs (x )< 1e4 and x == int (x ): return '%d' % x
408+
409+ if d < 1e-2 : fmt = '%1.3e'
410+ elif d < 1e-1 : fmt = '%1.3f'
411+ elif d > 1e5 : fmt = '%1.1e'
412+ elif d > 10 : fmt = '%1.1f'
413+ elif d > 1 : fmt = '%1.2f'
414+ else : fmt = '%1.3f'
415+ s = fmt % x
416+ #print d, x, fmt, s
417+ tup = s .split ('e' )
418+ if len (tup )== 2 :
419+ mantissa = tup [0 ].rstrip ('0' ).rstrip ('.' )
420+ sign = tup [1 ][0 ].replace ('+' , '' )
421+ exponent = tup [1 ][1 :].lstrip ('0' )
422+ s = '%se%s%s' % (mantissa , sign , exponent )
423+ else :
424+ s = s .rstrip ('0' ).rstrip ('.' )
425+ return s
281426
282427class LogFormatterExponent (LogFormatter ):
283428 """
@@ -542,7 +687,8 @@ def __call__(self):
542687 if vmax < vmin :
543688 vmin , vmax = vmax , vmin
544689 vmin = self ._base .ge (vmin )
545- locs = arange (vmin , vmax + 0.001 * self ._base .get_base (), self ._base .get_base ())
690+
691+ locs = frange (vmin , vmax + 0.001 * self ._base .get_base (), self ._base .get_base ())
546692
547693 return locs
548694
@@ -560,7 +706,6 @@ def autoscale(self):
560706 if vmin == vmax :
561707 vmin -= 1
562708 vmax += 1
563-
564709
565710 return self .nonsingular (vmin , vmax )
566711
0 commit comments