Skip to content

Commit 0cca80c

Browse files
committed
added some support for taking log of line plots with neg data
svn path=/trunk/matplotlib/; revision=925
1 parent 0a88696 commit 0cca80c

File tree

6 files changed

+133
-10
lines changed

6 files changed

+133
-10
lines changed

CHANGELOG

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
New entries should be added at the top
22

3-
3+
2005-02-05 Added some support for handling log switching for lines
4+
that have nonpos data - JDH
5+
46
2005-02-04 Added Nadia's contour patch - contour now has matlab
57
compatible syntax; this also fixed an unequal sized contour
68
array bug- JDH

boilerplate.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# wrap the plot commands defined in axes. The code generated by this
2-
# file is pasted into pylab.py
2+
# file is pasted into pylab.py. We did try to do this the smart way,
3+
# with callable functions and new.function, but could never get the
4+
# docstrings right for python2.2. See
5+
# http://groups-beta.google.com/group/comp.lang.python/messages/1b14640f3a4ad3dc,b3d7453af21e5f82,17739e70ac6f710c,9d5291fce29cbbb1,c5b578e4ffc6af28,056ff270daa2f414?thread_id=dcd63ec13096a0f6&mode=thread
6+
37

48

59
_fmtplot = """\

examples/date_demo2.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
mondays = WeekdayLocator(MONDAY) # every monday
1818
months = MonthLocator(range(1,13), bymonthday=1) # every month
19-
monthsFmt = DateFormatter('%b %d')
19+
monthsFmt = DateFormatter("%b '%y")
2020

2121

2222
quotes = quotes_historical_yahoo('INTC', date1, date2)
@@ -26,7 +26,7 @@
2626

2727
dates = [q[0] for q in quotes]
2828
opens = [q[1] for q in quotes]
29-
29+
3030
ax = subplot(111)
3131
plot_date(dates, opens, '-')
3232
ax.xaxis.set_major_locator(months)

lib/matplotlib/axes.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3045,6 +3045,24 @@ def set_xscale(self, value, basex = 10, subsx=None):
30453045
if subsx is None: subsx = range(2, basex)
30463046
assert(value.lower() in ('log', 'linear', ))
30473047
if value == 'log':
3048+
minx, maxx = self.get_xlim()
3049+
if minx<=0 or maxx<=0:
3050+
# find the min pos value in the data
3051+
xs = []
3052+
for line in self.lines:
3053+
xs.extend(line.get_xdata())
3054+
for patch in self.patches:
3055+
xs.extend([x for x,y in patch.get_verts()])
3056+
for collection in self.collections:
3057+
xs.extend([x for x,y in collection.get_verts()])
3058+
posx = [x for x in xs if x>0]
3059+
minx = min(posx)
3060+
maxx = max(posx)
3061+
# warning, probably breaks inverted axis
3062+
self.set_xlim((0.1*minx, maxx))
3063+
3064+
3065+
30483066
self.xaxis.set_major_locator(LogLocator(basex))
30493067
self.xaxis.set_major_formatter(LogFormatterMathtext(basex))
30503068
self.xaxis.set_minor_locator(LogLocator(basex,subsx))
@@ -3125,6 +3143,22 @@ def set_yscale(self, value, basey=10, subsy=None):
31253143
assert(value.lower() in ('log', 'linear', ))
31263144

31273145
if value == 'log':
3146+
miny, maxy = self.get_ylim()
3147+
if miny<=0 or maxy<=0:
3148+
# find the min pos value in the data
3149+
ys = []
3150+
for line in self.lines:
3151+
ys.extend(line.get_ydata())
3152+
for patch in self.patches:
3153+
ys.extend([y for x,y in patch.get_verts()])
3154+
for collection in self.collections:
3155+
ys.extend([y for x,y in collection.get_verts()])
3156+
posy = [y for y in ys if y>0]
3157+
miny = min(posy)
3158+
maxy = max(posy)
3159+
# warning, probably breaks inverted axis
3160+
self.set_ylim((0.1*miny, maxy))
3161+
31283162
self.yaxis.set_major_locator(LogLocator(basey))
31293163
self.yaxis.set_major_formatter(LogFormatterMathtext(basey))
31303164
self.yaxis.set_minor_locator(LogLocator(basey,subsy))

lib/matplotlib/collections.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ class Collection(Artist):
3535
def __init__(self):
3636
Artist.__init__(self)
3737

38+
39+
def get_verts(self):
40+
'return seq of (x,y) in collection'
41+
raise NotImplementedError('Derived must override')
42+
3843
def _get_color(self, c, N=1):
3944
if looks_like_color(c):
4045
return [colorConverter.to_rgba(c)]*N
@@ -98,6 +103,7 @@ def __init__(self,
98103
self._offsets = offsets
99104
self._transOffset = transOffset
100105

106+
101107
def set_linewidth(self, lw):
102108
"""
103109
Set the linewidth(s) for the collection. lw can be a scalar or a
@@ -186,6 +192,23 @@ def draw(self, renderer):
186192
self._transform.thaw()
187193
self._transOffset.thaw()
188194
renderer.close_group('polycollection')
195+
196+
197+
198+
def get_verts(self):
199+
'return seq of (x,y) in collection'
200+
if self._offsets is None:
201+
offsets = [(0,0)]
202+
else:
203+
offsets = self._offsets
204+
N = max(len(offsets), len(self._verts))
205+
vertsall = []
206+
for i in range(N):
207+
ox, oy = offsets[i%N]
208+
verts = self._verts[i%N]
209+
vertsall.extend([(x+ox, y+oy) for x,y in verts])
210+
return vertsall
211+
189212

190213
class RegularPolyCollection(PatchCollection):
191214
def __init__(self,
@@ -213,7 +236,7 @@ def __init__(self,
213236

214237
theta = (2*math.pi/numsides)*arange(numsides) + rotation
215238

216-
self._verts = zip( r*sin(theta), r*cos(theta) )
239+
self._verts = zip( r*cos(theta), r*sin(theta) )
217240

218241

219242

@@ -239,6 +262,17 @@ def draw(self, renderer):
239262
self._transOffset.thaw()
240263
renderer.close_group('regpolycollection')
241264

265+
266+
267+
def get_verts(self):
268+
'return seq of (x,y) in collection'
269+
if self._offsets is None:
270+
offsets = [(0,0)]
271+
else:
272+
offsets = self._offsets
273+
return [ (x+ox, y+oy) for x,y in self._verts for ox,oy in offsets]
274+
275+
242276
class LineCollection(Collection):
243277
"""
244278
All parameters must be sequences. The property of the ith line
@@ -364,3 +398,18 @@ def get_linewidths(self):
364398

365399
def get_colors(self):
366400
return self._colors
401+
402+
403+
def get_verts(self):
404+
'return seq of (x,y) in collection'
405+
if self._offsets is None:
406+
offsets = [(0,0)]
407+
else:
408+
offsets = self._offsets
409+
N = max(len(offsets), len(self._verts))
410+
vertsall = []
411+
for i in range(N):
412+
ox, oy = offsets[i%N]
413+
verts = self._segments[i%N]
414+
vertsall.extend([(x+ox, y+oy) for x,y in verts])
415+
return vertsall

lib/matplotlib/lines.py

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@
99
import sys
1010

1111
from numerix import Float, alltrue, arange, array, logical_and,\
12-
nonzero, searchsorted, take, asarray, ones, where, less, ravel
12+
nonzero, searchsorted, take, asarray, ones, where, less, ravel, \
13+
greater, logical_and
1314
from matplotlib import verbose
1415
from artist import Artist
1516
from cbook import iterable, is_string_like
1617
from collections import RegularPolyCollection, PolyCollection
1718
from colors import colorConverter
1819
from patches import bbox_artist
19-
from transforms import lbwh_to_bbox
20+
from transforms import lbwh_to_bbox, LOG10
2021
from matplotlib import rcParams
2122

2223
TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN = range(4)
@@ -140,6 +141,7 @@ def __init__(self, xdata, ydata,
140141
self._lineFunc = self._lineStyles.get(linestyle, self._draw_nothing)
141142
self._markerFunc = self._markers.get(marker, self._draw_nothing)
142143

144+
self._logcache = None
143145

144146
def get_window_extent(self, renderer):
145147
x, y = self._get_numeric_clipped_data_in_range()
@@ -193,6 +195,8 @@ def set_data(self, *args):
193195
raise RuntimeError('xdata and ydata must be the same length')
194196

195197
if self._useDataClipping: self._xsorted = self._is_sorted(self._x)
198+
199+
self._logcache = None
196200

197201
def set_data_clipping(self, b):
198202
"""
@@ -210,12 +214,37 @@ def _is_sorted(self, x):
210214

211215
def _get_numeric_clipped_data_in_range(self):
212216
# if the x or y clip is set, only plot the points in the
213-
# clipping region
217+
# clipping region. If log scale is set, only pos data will be
218+
# returned
214219
try: self._xc, self._yc
215220
except AttributeError: x, y = self._x, self._y
216221
else: x, y = self._xc, self._yc
217222

218-
223+
try: logx = self._transform.get_funcx().get_type()==LOG10
224+
except RuntimeError: logx = False # non-separable
225+
226+
try: logy = self._transform.get_funcy().get_type()==LOG10
227+
except RuntimeError: logy = False # non-separable
228+
229+
if not logx and not logy: return x, y
230+
231+
if self._logcache is not None:
232+
return self._logcache
233+
234+
Nx = len(x)
235+
Ny = len(y)
236+
237+
if logx: indx = greater(x, 0)
238+
else: indx = ones(len(x))
239+
240+
if logy: indy = greater(y, 0)
241+
else: indy = ones(len(y))
242+
243+
ind = nonzero(logical_and(indx, indy))
244+
x = take(x, ind)
245+
y = take(y, ind)
246+
247+
self._logcache = x, y
219248
return x, y
220249

221250
def draw(self, renderer):
@@ -267,8 +296,11 @@ def get_ydata(self): return self._y
267296

268297

269298
def _set_clip(self):
299+
270300

271301
if not self._useDataClipping: return
302+
#self._logcache = None
303+
272304
try: self._xmin, self._xmax
273305
except AttributeError: indx = arange(len(self._x))
274306
else:
@@ -402,7 +434,7 @@ def set_xdata(self, x):
402434
except AttributeError: pass
403435

404436
self.set_data(x, self._y)
405-
437+
406438
def set_ydata(self, y):
407439
"""
408440
Set the data array for y
@@ -427,6 +459,8 @@ def set_xclip(self, *args):
427459
self._xmin, self._xmax = xmin, xmax
428460
self._set_clip()
429461

462+
463+
430464
def set_yclip(self, *args):
431465
"""
432466
Set the y clipping range for data clipping to ymin, ymax

0 commit comments

Comments
 (0)