8000 Fixed 2 bugs in NewScalarFormatter, cleaned up some code in axis.py p… · matplotlib/matplotlib@7037b4c · GitHub
[go: up one dir, main page]

Skip to content
10BC0

Commit 7037b4c

Browse files
committed
Fixed 2 bugs in NewScalarFormatter, cleaned up some code in axis.py per JDH
request. Improved placement of offset_text, added support for mathtext rendering of offset_text. svn path=/trunk/matplotlib/; revision=1259
1 parent d39c206 commit 7037b4c

File tree

5 files changed

+112
-56
lines changed

5 files changed

+112
-56
lines changed

CHANGELOG

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
New entries should be added at the top
22

3+
2005-05-04 Added NewScalarFormatter. This can be renamed ScalarFormatter
4+
in order to be used as the default for linearscale plots.
5+
Improved formatting of ticklabels, scientific notation, and
6+
the ability to plot large large numbers with small ranges, by
7+
determining a numerical offset. See ticker.NewScalarFormatter
8+
for more details. -DSD
9+
310
2005-05-03 Added the option to specify a delimiter in pylab.load -DSD
411

512
2005-04-28 Added Darren's line collection example

lib/matplotlib/axis.py

Lines changed: 76 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ class Axis(Artist):
438438
439439
"""
440440
LABELPAD = 5
441+
OFFSETTEXTPAD = 3
441442

442443
def __init__(self, axes):
443444
"""
@@ -456,25 +457,9 @@ def __init__(self, axes):
456457
#self.minor = dummy()
457458

458459
self.label = self._get_label()
460+
self.offsetText = self._get_offset_text()
459461
self.majorTicks = []
460462
self.minorTicks = []
461-
462-
if self.__name__=='xaxis':
463-
ox,oy = 1, -0.06
464-
v,h = 'top','right'
465-
else:
466-
ox,oy = 0, 1.01
467-
v,h='bottom','left'
468-
self.offsetText = Text(x=ox, y=oy,
469-
fontproperties = FontProperties(size=rcParams['tick.labelsize']),
470-
color = rcParams['axes.labelcolor'],
471-
verticalalignment=v,
472-
horizontalalignment=h,
473-
)
474-
self.offsetText.set_transform(self.axes.transAxes)
475-
## self.offsetText.set_transform( blend_xy_sep_transform( self.axes.transAxes,
476-
## identity_transform() ))
477-
self._set_artist_props(self.offsetText)
478463

479464
self.cla()
480465

@@ -564,16 +549,17 @@ def draw(self, renderer, *args, **kwargs):
564549
if tick.label2On:
565550
extent = tick.label2.get_window_extent(renderer)
566551
ticklabelBoxes2.append(extent)
567-
568-
self.offsetText.set_text( self.major.formatter.get_offset() )
569-
self.offsetText.draw(renderer)
570552

571553
# scale up the axis label box to also find the neighbors, not
572554
# just the tick labels that actually overlap note we need a
573555
# *copy* of the axis label box because we don't wan't to scale
574556
# the actual bbox
575-
self._update_label_postion(ticklabelBoxes, ticklabelBoxes2)
557+
self._update_label_position(ticklabelBoxes, ticklabelBoxes2)
576558
self.label.draw(renderer) # memory leak here, vertical text
559+
560+
self._update_offset_text_position(ticklabelBoxes, ticklabelBoxes2)
561+
self.offsetText.set_text( self.major.formatter.get_offset() )
562+
self.offsetText.draw(renderer)
577563

578564
if 0: # draw the bounding boxes around the text for debug
579565
for tick in majorTicks:
@@ -585,14 +571,21 @@ def draw(self, renderer, *args, **kwargs):
585571

586572
def _get_label(self):
587573
raise NotImplementedError('Derived must override')
574+
575+
def _get_offset_text(self):
576+
raise NotImplementedError('Derived must override')
588577

589578
def get_gridlines(self):
590579
'Return the grid lines as a list of Line2D instance'
591580
return silent_list('Line2D gridline', [tick.gridline for tick in self.majorTicks])
592581

593582
def get_label(self):
594-
'Return the axis label as an Text instance'
583+
'Return the axis label as a Text instance'
595584
return self.label
585+
586+
def get_offset_text(self):
587+
'Return the axis offsetText as a Text instance'
588+
return self.offsetText
596589

597590
def get_ticklabels(self):
598591
'Return a list of Text instances for ticklabels'
@@ -768,7 +761,14 @@ def set_ticks(self, ticks):
768761
self.get_view_interval().update(ticks,0)
769762
return self.get_major_ticks()
770763

771-
def _update_label_postion(self, bboxes):
764+
def _update_label_position(self, bboxes, bboxes2):
765+
"""
766+
Update the label position based on the sequence of bounding
767+
boxes of all the ticklabels
768+
"""
769+
raise NotImplementedError('Derived must override')
770+
771+
def _update_offset_text_postion(self, bboxes, bboxes2):
772772
"""
773773
Update the label position based on the sequence of bounding
774774
boxes of all the ticklabels
@@ -791,7 +791,7 @@ def _get_tick(self, major):
791791

792792
def _get_label(self):
793793
# x in axes coords, y in display coords (to be updated at draw
794-
# time by _update_label_positions
794+
# time by _update_label_positions)
795795
label = Text(x=0.5, y=0,
796796
fontproperties = FontProperties(size=rcParams['axes.labelsize']),
797797
color = rcParams['axes.labelcolor'],
@@ -804,6 +804,20 @@ def _get_label(self):
804804
self._set_artist_props(label)
805805
self.label_position='bottom'
806806
return label
807+
808+
def _get_offset_text(self):
809+
# x in axes coords, y in display coords (to be updated at draw time)
810+
offsetText = Text(x=1, y=0,
811+
fontproperties = FontProperties(size=rcParams['tick.labelsize']),
812+
color = rcParams['tick.color'],
813+
verticalalignment='top',
814+
horizontalalignment='right',
815+
)
816+
offsetText.set_transform( blend_xy_sep_transform( self.axes.transAxes,
817+
identity_transform() ))
818+
self._set_artist_props(offsetText)
819+
self.offset_text_position='bottom'
820+
return offsetText
807821

808822
def get_label_position(self):
809823
"""
@@ -824,7 +838,7 @@ def set_label_position(self, position):
824838
self.label.set_verticalalignment('top')
825839
self.label_position=position
826840

827-
def _update_label_postion(self, bboxes, bboxes2):
841+
def _update_label_position(self, bboxes, bboxes2):
828842
"""
829843
Update the label position based on the sequence of bounding
830844
boxes of all the ticklabels
@@ -850,6 +864,19 @@ def _update_label_postion(self, bboxes, bboxes2):
850864
top = bbox.ymax()
851865

852866
self.label.set_position( (x, top+self.LABELPAD*self.figure.dpi.get()/72.0))
867+
868+
def _update_offset_text_position(self, bboxes, bboxes2):
869+
"""
870+
Update the offset_text position based on the sequence of bounding
871+
boxes of all the ticklabels
872+
"""
873+
x,y = self.offsetText.get_position()
874+
if not len(bboxes):
875+
bottom = self.axes.bbox.ymin()
876+
else:
877+
bbox = bbox_all(bboxes)
878+
bottom = bbox.ymin()
879+
self.offsetText.set_position((x, bottom-self.OFFSETTEXTPAD*self.figure.dpi.get()/72.0))
853880

854881
def tick_top(self):
855882
'use ticks only on top'
@@ -904,6 +931,20 @@ def _get_label(self):
904931
self._set_artist_props(label)
905932
self.label_position='left'
906933
return label
934+
935+
def _get_offset_text(self):
936+
# x in display coords, y in axes coords (to be updated at draw time)
937+
offsetText = Text(x=0, y=0.5,
938+
fontproperties = FontProperties(size=rcParams['tick.labelsize']),
939+
color = rcParams['tick.color'],
940+
verticalalignment='bottom',
941+
horizontalalignment='left',
942+
)
943+
offsetText.set_transform(blend_xy_sep_transform(self.axes.transAxes,
944+
identity_transform()) )
945+
self._set_artist_props(offsetText)
946+
self.offset_text_position='left'
947+
return offsetText
907948

908949
def get_label_position(self):
909950
"""
@@ -924,7 +965,7 @@ def set_label_position(self, position):
924965
self.label.set_horizontalalignment('right')
925966
self.label_position=position
926967

927-
def _update_label_postion(self, bboxes, bboxes2):
968+
def _update_label_position(self, bboxes, bboxes2):
928969
"""
929970
Update the label position based on the sequence of bounding
930971
boxes of all the ticklabels
@@ -950,6 +991,15 @@ def _update_label_postion(self, bboxes, bboxes2):
950991
right = bbox.xmax()
951992

952993
self.label.set_position( (right+self.LABELPAD*self.figure.dpi.get()/72.0, y))
994+
995+
def _update_offset_text_position(self, bboxes, bboxes2):
996+
"""
997+
Update the offset_text position based on the sequence of bounding
998+
boxes of all the ticklabels
999+
"""
1000+
x,y = self.offsetText.get_position()
1001+
top = self.axes.bbox.ymax()
1002+
self.offsetText.set_position((x, top+self.OFFSETTEXTPAD*self.figure.dpi.get()/72.0))
9531003

9541004
def tick_right(self):
9551005
'use ticks only on right'

lib/matplotlib/pylab.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -922,8 +922,7 @@ def load(fname,comments='%',delimiter=None):
922922
for line in fh:
923923
line = line[:line.find(comments)].strip()
924924
if not len(line): continue
925-
if delimiter: row = [float(val) for val in line.split(delimiter)]
926-
else: row = [float(val) for val in line.split()]
925+
row = [float(val) for val in line.split(delimiter)]
927926
thisLen = len(row)
928927
if numCols is not None and thisLen != numCols:
929928
raise ValueError('All rows must have the same number of columns')

lib/matplotlib/text.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class Text(Artist):
8181
8282
"""
8383
# special case superscripting to speedup logplots
84-
_rgxsuper = re.compile('\$([0-9]+)\^\{(-?[0-9]+)\}\$')
84+
_rgxsuper = re.compile('\$([\-+0-9]+)\^\{(-?[0-9]+)\}\$')
8585

8686
zorder = 3
8787
def __init__(self,
@@ -281,6 +281,7 @@ def draw(self, renderer):
281281
if angle==0:
282282
m = self._rgxsuper.match(self._text)
283283
if m is not None:
284+
print self._text
284285
bbox, info = self._get_layout_super(self._renderer, m)
285286
base, xt, yt = info[0]
286287
renderer.draw_text(gc, xt, yt, base,

lib/matplotlib/ticker.py

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -242,17 +242,18 @@ def pprint_val(self, x, d):
242242
class NewScalarFormatter(Formatter):
243243
"""
244244
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-
"""
245+
is much smaller than the data average, then an offset will be determined
246+
such that the tick labels are meaningful. Scientific notation is used for
247+
data < 1e-3 or data >= 1e4.
248+
"""
249+
# To use, rename ScalarFormatter to OldScalarFormatter,
250+
# and rename NewScalarFormatter to ScalarFormatter.
251+
def __init__(self, useOffset=True, useMathText=False):
252+
# useOffset allows plotting small data ranges with large offsets:
253+
# for example: [1+1e-9,1+2e-9,1+3e-9]
254+
# useMathText will render the offset an scientific notation in mathtext
255255
self._useOffset = useOffset
256+
self._useMathText = useMathText
256257
self.offset = 0
257258
self.orderOfMagnitude = 0
258259
self.format = ''
@@ -268,28 +269,22 @@ def __call__(self, x, pos=0):
268269

269270
def format_data(self,value):
270271
'return a formatted string representation of a number'
271-
s = '%1.5e'% value
272+
s = '%1.4e'% value
272273
return self._formatSciNotation(s)
273274

274275
def get_offset(self):
275276
"""Return scientific notation, plus offset"""
276277
if self.orderOfMagnitude or self.offset:
277278
offsetStr = ''
278279
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,'$'))
286280
if self.offset:
287281
p = ('+%1.10e'% self.offset).replace('+-','-')
288-
offsetStr = self._formatSciNotation(p)
282+
offsetStr = self._formatSciNotation(p,mathtext=self._useMathText)
289283
if self.orderOfMagnitude:
290-
p = '%1.e'% 10**self.orderOfMagnitude
291-
sciNotStr = self._formatSciNotation(p)
292-
return ''.join((sciNotStr,offsetStr))
284+
if self._useMathText: sciNotStr = r'{\times}10^{%d}'% self.orderOfMagnitude
285+
else: sciNotStr = 'x1e%d'% self.orderOfMagnitude
286+
if self._useMathText: return ''.join(('$',sciNotStr,offsetStr,'$'))
287+
else: return ''.join((sciNotStr,offsetStr))
293288
else: return ''
294289

295290
def set_locs(self, locs):
@@ -301,17 +296,17 @@ def _set_offset(self, range):
301296
ave_loc = average(locs)
302297
if ave_loc: # dont want to take log10(0)
303298
ave_oom = math.floor(math.log10(absolute(ave_loc)))
304-
range_oom = math.ceil(math.log10(range))
299+
range_oom = math.floor(math.log10(range))
305300
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
301+
if ave_loc < 0: self.offset = math.ceil(amax(locs)/10**range_oom)*10**range_oom
307302
else: self.offset = math.floor(amin(locs)/10**range_oom)*10**range_oom
308303
else: self.offset = 0
309304

310305
def _set_orderOfMagnitude(self,range):
311306
# if scientific notation is to be used, find the appropriate exponent
312307
# if using an numerical offset, find the exponent after applying the offset
313308
locs = absolute(self.locs)
314-
if self._useOffset: oom = math.floor(math.log10(range))
309+
if self.offset: oom = math.floor(math.log10(range))
315310
else:
316311
if locs[0] > locs[-1]: oom = math.floor(math.log10(locs[0]))
317312
else: oom = math.floor(math.log10(locs[-1]))
@@ -335,14 +330,18 @@ def pprint_val(self, x):
335330
if closeto(xp,< 36A2 span class=pl-c1>0): return '0'
336331
else: return self.format % xp
337332

338-
def _formatSciNotation(self,s):
333+
def _formatSciNotation(self,s,mathtext=False):
339334
# transform 1e+004 into 1e4, for example
340335
tup = s.split('e')
341336
try:
342337
mantissa = tup[0].rstrip('0').rstrip('.')
343338
sign = tup[1][0].replace('+', '')
344339
exponent = tup[1][1:].lstrip('0')
345-
return ('%se%s%s' %(mantissa, sign, exponent)).rstrip('e')
340+
if mathtext:
341+
res = '%se{%s%s}' %(mantissa, sign, exponent)
342+
return res.replace('e{}','').replace('e',r'{\times}10^')
343+
else:
344+
return ('%se%s%s' %(mantissa, sign, exponent)).rstrip('e')
346345
except IndexError,msg:
347346
return s
348347

0 commit comments

Comments
 (0)
0