From: Peter G. <pgr...@ge...> - 2004-06-17 04:01:47
|
Thanks for the prompt response and the detailed explanation. The scaling is working now, and I have not encountered those old problems. There are a couple of things I should mention however: 1) The axes.auto_scale() seems to be broken. I get a following error: ------------ Traceback (most recent call last): File "./e3.py", line 97, in ? ax.autoscale_view() File "/usr/lib/python2.2/site-packages/matplotlib/axes.py", line 436, in autoscale_view tup = locator.autoscale() File "/usr/lib/python2.2/site-packages/matplotlib/ticker.py", line 327, in autoscale return self.nonsingular(self.dataInterval.get_bounds()) TypeError: nonsingular() takes exactly 3 arguments (2 given) ------------- When I run this: -------- #!/usr/bin/env python import time from matplotlib.dates import EpochConverter from matplotlib.matlab import * from matplotlib.ticker import FuncFormatter, NullLocator, MinuteLocator, DayLocator, HourLocator, MultipleLocator, DateFormatter wantLegend=1 time1=[1087192789.89] data1=[-65.54] time2=[ 1087161589.89 , 1087192289.0, 1087192389.0, 1087192489.0, 1087192589.0, 1087192689.0, 1087192789.89 , 1087192889.0, 1087192989.0, 1087193089.0, 1087193189.0, 1087193289.0, 1087238100.0 , ] data2=[ -55.44 -64.54 , -66.54 , -61.54 , -69.54 , -45.66, -55.54 , -77.54, -65.54 , -49.54 , -57.54 , -68.54 , -55.54 , -23.44 ] ax = subplot(111) p1Size=len(time1) p2Size=len(time2) p1=plot_date(time1, data1, None, '-', color='r') p2=plot_date(time2, data2, None, '-', color='b') p1=plot(time1, data1,'-', color='r') p2=plot(time2, data2,'-', color='b') now=time2[-1] then=time2[0] deltaSec=now-then deltaTickSec=deltaSec/7.0 tickList=[item for item in list(arange(then, now, deltaTickSec))] def tickString(x, pos): return time.strftime("%H:%M:%S", time.localtime(x)) formatter = FuncFormatter(tickString) ax.set_xticks(tickList) ax.xaxis.set_major_formatter(formatter) ax.xaxis.set_minor_locator(NullLocator()) #This line does not work. ax.autoscale_view() if wantLegend: legend((p1, p2), ('small data set (%d)' % p1Size, 'large data set (%d)' % p2Size)) xlabel('time') grid(True) show() #savefig('./blah.png') ------ Things work when I comment out the line: ax.autoscale_view() 2) The auto-scaling in plot_date() does not scale properly in some special cases. Consider this: ----------------- from matplotlib.matlab import * time2= [ 1087321489.89, 1087321500.0, 1087321789.89, 1087321800.0, 1087322089.89, 1087322100.0, 1087322389.89, 1087322700.0, 1087322989.89, 1087323000.0, 1087323289.89, 1087323300.0, 1087323589.89, 1087323600.0, 1087323889.89, 1087323900.0, 1087324189.89, 1087324200.0, 1087324489.89, 1087324500.0, ] data2=[ 3.02, 3.02, 3.14, 3.14, 3.21, 3.21, 3.26, 3.26, 3.39, 3.39, 3.51, 3.51, 3.58, 3.58, 3.75, 3.75, 4.0, 4.0, 4.22, 4.22,] plot_date(time2, data2, None, '-', color='b') xlabel('time') grid(True) show() --------------- The same thing happens over differnt ranges when the amount of ticks is large. Perhaps you may use something similar to the code below (from axes.py) to deal with these things. Note the ceilings get rid of the AssertErrors in ticks.Base when int() gives zero. Also, to finalize this, one would have to write a DayMultiLocator type class for the Weeks, otherwise when the number of weeks is close, but less then the number of weeks in numticks*months it will get crowded. This will probably be a little more involved than dealing with days, but perhaps one could use your existent WeekdayLocator class to simplify the problem. Also note that there was an error in defining months in your code; I changed the line to: months = span/(SEC_PER_DAY*31) # approx ----------- def plot_date(self, d, y, converter, fmt='bo', **kwargs): """ plot_date(d, y, converter, fmt='bo', **kwargs) d is a sequence of dates; converter is a dates.DateConverter instance that converts your dates to seconds since the epoch for plotting. y are the y values at those dates. fmt is a plot format string. kwargs are passed on to plot. See plot for more information. pass converter = None if your dates are already in epoch format """ if not self._hold: self.cla() if converter is not None: e = array([converter.epoch(thisd) for thisd in d]) else: e = d assert(len(e)) ret = self.plot(e, y, fmt, **kwargs) span = self.dataLim.intervalx().span() if span==0: span = SEC_PER_MIN minutes = span/SEC_PER_MIN hours = span/SEC_PER_HOUR days = span/SEC_PER_DAY weeks = span/SEC_PER_WEEK months = span/(SEC_PER_DAY*31) # approx years = span/(SEC_PER_WEEK*52) # approx #These should go to the top module. from math import ceil from ticker import DayMultiLocator numticks = 5 if years>numticks: locator = YearLocator(ceil(years/numticks)) # define fmt = '%Y' elif months>numticks: locator = MonthLocator(ceil(months/numticks)) # define fmt = '%b %Y' elif weeks>numticks: locator = WeekdayLocator(0) fmt = '%b %d' #Hack - need a DayLocator which operates like HourLocator #ie. Have a tick every so many days other than every day at a #particular hour. #This is a class I added to ticker.py: ## class DayMultiLocator(MultipleLocator): ## """ ## Make ticks on day which are multiples of base ## """ ## def __init__(self, base): ## MultipleLocator.__init__(self, base*SEC_PER_DAY) elif days>numticks: locator = DayMultiLocator(ceil(days/numticks)) fmt = '%b %d' elif hours>numticks: locator = HourLocator(ceil(hours/numticks)) fmt = '%H:%M\n%b %d' elif minutes>numticks: locator = MinuteLocator(ceil(minutes/numticks)) fmt = '%H:%M:%S' else: locator = MinuteLocator(1) fmt = '%H:%M:%S' formatter = DateFormatter(fmt) self.xaxis.set_major_locator(locator) self.xaxis.set_major_formatter(formatter) self.autoscale_view() return ret Let me know what you think. Peter John Hunter wrote: >>>>>> "Peter" == Peter Groszkowski <pgr...@ge...> writes: >>>>>> > > > Peter> It does?! Do you mean that this is done automatically? Can > Peter> you show me an example of this using only the time module > Peter> (since I use python2.2, dont have datetime)? I thought that > Peter> I had to manually set things up and tell matplotlib whether > > No, it's designed to work automagically. If you take a look at > axes.Axes.plot_date you'll see how it works. It looks at the max/min > span of your data and chooses a locator and formatter accordingly. > All the examples use custom locators and formatters ( I was busy > showing off how you could customize), but you don't need to > > from matplotlib.matlab import * > > time2= [ 1087161589.89 , 1087192289.0, 1087192389.0, 1087192489.0, > 1087192589.0, 1087192689.0, 1087192789.89 , 1087192889.0, > 1087192989.0, 1087193089.0, 1087193189.0, 1087193289.0, 1087238100.0, > ] > data2=[ -55.44 -64.54 , -66.54 , -61.54 , -69.54 , -45.66, -55.54, > -77.54, -65.54 , -49.54 , -57.54 , -68.54 , -55.54 , -23.44 ] > > plot_date(time2, data2, None, '-', color='b') > xlabel('time') > grid(True) > > show() > > In general you should try not to set the limits or ticks explicitly > since this is the job of the autolocator. I realize you were > encountering troubles and this is what led you to do it. Hopefully > the discussion below will save you from having to do this. > > Peter> On the other note, regarding the weird scaling that I > Peter> talked about (and showed pretty pics for) in my last mail, > Peter> I finally put together a small script that exposes the > Peter> problem. It is a bit rough because I ripped bits and pieces > Peter> from here and there, but shows the issue. Use the > Peter> 'wantBadPlot' and 'wantStandardDateTics' to see how things > Peter> go wrong. > > I looked at this and figured out the root cause of the problem. Your > "bad data" is a singleton plot > > time1=[1087192789.89] > data1=[-65.54] > p1=plot_date(time1, data1, None, '-', color='r') > > These are difficult to deal with - I'll explain why. The default > linear transformation maps data limits to display limits, and needs to > divide by the data span to do so. In this case min=max so the span is > zero and the transformation is undefined. To handle this case, I > detect the span=0 case and reset the data limits, lowering the min a > bit and raising the max a bit. > In earlier versions of maptlotlib, I did > > minx, maxx = min(xdata), max(xdata) > if minx==maxx: > minx -= 1 > maxx += 1 > > A problem arose if a person issued multiple calls to plot, first with > a singleton and then with a range of data, all really small > > >>> plot([1e-9], [10]) # x limits are now approx -1, 1 due to above > >>> plot([1e-9,2e-9,3e-9], [10,20,30]) > after the second call the data limits are still -1,1 because the limit > code only changes the data limits if the new lines are outside the > range of the existing limits. In this case they are not. The effect > is the plot is badly scaled since the data limits are wide compared to > all the xdata. > > Thinking myself clever (first mistake!), I rewrote the above code as > > minx, maxx = min(xdata), max(xdata) > if minx==maxx: > minx -= 0.001*abs(minx) > maxx += 0.001*abs(maxx) > > So the scaling would be proportionate to the data size. This helps in > some cases, but is failing for you. Again, in your example code, you > do the dreaded singleton plot followed by more plot commands > > p1=plot_date([1087192789.89], [-65.54], None, '-', color='r') > p2=plot_date(times, values, None, '-', color='r') > > Since minx==maxx in the first plot, the xlimits become 1086105597.1, > 1088279982.67. This is very wide compared to the subsequent range of > data you put in in the second plot_date call. This explains the bad > scaling you are seeing. > > Upon writing this, I realized that the fundamental problem is that I > am manipulating data limits to deal with a singular range, when the > data limits should *always* accurately reflect the true data limits > (doh!). It's the view limits that need to be mucked with to handle > this case. I had to make a number of changes throughout the code to > get everybody playing nicely with this new insight, but now everything > is working. Even your wantBadPlot example! > > Give it a try: > > http://nitace.bsd.uchicago.edu:8080/files/share/matplotlib-0.54.3a.tar.gz > > and let me know if it passes your tests. > > JDH > > ---- Msg sent via @Mail - http://WebBasedEmail.com/ |