From: Glen W. M. <Gle...@sw...> - 2006-04-26 22:36:47
|
Glen W. Mabey wrote: > I'm in the process of migrating some software I've written from a > machine with MPL 0.86.2 on it to another with 0.87.2 . > > The app is Qt-based with MPL embedded as a widget. The plot itself uses > dates on the x-axis. > > It appears that the behavior of autoscale_view() changed quite a bit > somewhere between these two versions. > > In my application I create a patches.Rectangle that is 100 seconds wide: > > start_time_num = matplotlib.dates.date2num( start_time ) > lower_left = [ matplotlib.dates.date2num( start_time ), start_y ] > width = matplotlib.dates.date2num( start_time + datetime.timedelta( seconds=10 ) ) - start_time_num > r = matplotlib.patches.Rectangle( lower_left, width, height ) > > With the 0.86.2 version, the total span of the x-axis after an > autoscale_view() call is about 12 seconds. For the 0.87.2 version > however, the span is more than 200 years. (see attached .png files) > > I don't see this type of behavior in the pylab environment, yet I'm > having a hard time creating a concise example that demonstrates the > behavior. > > I can keep working on extracting an snippet, but I thought I'd ask > if this is some intended behavioral change, and if so, if there is a > workaround. Okay, I figured out what the critical parameter is: if the width of the Rectangle is less than one-millionth of the x value, then I get the centuries of timespan on the x-axis phenomenon. (That's probably blindingly obvious in the source code ... somewhere.) So, first of all, this behavior has nothing to do with the OO interface, nor the Qt backend. This seems like reasonable behavior for autoscaling in general. Certainly you can't zoom in nicely around a patch with zero width; you have to draw the line somewhere. (ha ha, no pun intended) It just so happens that for my application, I need to plot Rectangles that represent events as a function of time and frequency. As such, my x-values (as produced by date2num()) are on the order of 732421, yet I need to display patches that are like 10 seconds wide, which turns out to be 0.000115740695037, after a date2num() conversion. This behavior is exhibited in the following lines of code. A delta of 65000 seconds is sufficient to get the scaling right. ######### import matplotlib import matplotlib.dates from matplotlib.pylab import * import datetime ax = subplot(111) start_time = datetime.datetime(2006, 4, 21) start_time_num = matplotlib.dates.date2num( start_time ) lower_left = [ start_time_num, 1.345680002 ] width = matplotlib.dates.date2num( start_time + datetime.timedelta( seconds=100 ) ) - start_time_num print 'width = ', width ax.add_patch( matplotlib.patches.Rectangle( lower_left, width, 2 ) ) ax.autoscale_view() ax.xaxis.set_major_formatter( matplotlib.dates.DateFormatter('%Y') ) print ax.get_xlim() show() ########## So, this seems like a fairly narrow case where the threshold in use causes a problem. I can't imagine another instance where 1e-6 would not be prefectly fine. One solution would be to just patch the source for my deployment of this software ... I hesitate to suggest that this level ought to be globally tunable by the user, although an additional parameter that could be passed to autoscale_view() wouldn't be as bad. Still that seems like clutter. What say ye? Thank you, Glen Mabey |
From: Eric F. <ef...@ha...> - 2006-04-26 23:23:05
|
Glen, The change you ran into is mine. The relevant parameter is in this method of the base Locator class: def nonsingular(self, vmin, vmax, expander=0.001, tiny=1e-6): if vmax < vmin: vmin, vmax = vmax, vmin if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: if vmin==0.0: vmin -= 1 vmax += 1 else: vmin -= expander*abs(vmin) vmax += expander*abs(vmax) return vmin, vmax This method gets called in several places. Some of them supply the expander kwarg, but I don't think any supply the tiny kwarg, so the default, which I arbitrarily set to 1e-6, is used. I dimly recall finding that using a *much* smaller value could cause trouble elsewhere. Rather than patching the code you send out, I suggest you consider using a custom Locator, which would allow you to set this parameter to whatever works for you, and maybe put in additional tweaks for your unusual use case. You could subclass whatever Locator you are using now, e.g. class myLocator(MaxNLocator): def nonsingular(self, vmin, vmax, expander=0.001, tiny=1e-12): return Locator.nonsingular(self, vmin, vmax, expander=expander, tiny=tiny) and then ax.xaxis.set_major_locator(myLocator()) This is untested, but I think something along these lines is all you would need. Everything except nonsingular would be inherited, and nonsingular would be called with your new default value for tiny. As an alternative approach, can you simply use as your x-coordinate time deltas from a suitable base time, so that vmin and vmax would not be so huge compared to their difference? My assumption while working with the ticker.py code was that this would be the logical solution to this sort of problem. Longer term, it would be easy to modify the Locator classes so that the default values of tiny and expander would appear as a kwargs in __init__. Whether this would be a valuable change or mere clutter, I don't know; I will listen for more advice. Eric Glen W. Mabey wrote: > Glen W. Mabey wrote: > >>I'm in the process of migrating some software I've written from a >>machine with MPL 0.86.2 on it to another with 0.87.2 . >> >>The app is Qt-based with MPL embedded as a widget. The plot itself uses >>dates on the x-axis. >> >>It appears that the behavior of autoscale_view() changed quite a bit >>somewhere between these two versions. >> >>In my application I create a patches.Rectangle that is 100 seconds wide: >> >> start_time_num = matplotlib.dates.date2num( start_time ) >> lower_left = [ matplotlib.dates.date2num( start_time ), start_y ] >> width = matplotlib.dates.date2num( start_time + datetime.timedelta( seconds=10 ) ) - start_time_num >> r = matplotlib.patches.Rectangle( lower_left, width, height ) >> >>With the 0.86.2 version, the total span of the x-axis after an >>autoscale_view() call is about 12 seconds. For the 0.87.2 version >>however, the span is more than 200 years. (see attached .png files) >> >>I don't see this type of behavior in the pylab environment, yet I'm >>having a hard time creating a concise example that demonstrates the >>behavior. >> >>I can keep working on extracting an snippet, but I thought I'd ask >>if this is some intended behavioral change, and if so, if there is a >>workaround. > > > Okay, I figured out what the critical parameter is: if the width of the > Rectangle is less than one-millionth of the x value, then I get the > centuries of timespan on the x-axis phenomenon. > > (That's probably blindingly obvious in the source code ... somewhere.) > > So, first of all, this behavior has nothing to do with the OO interface, > nor the Qt backend. > > This seems like reasonable behavior for autoscaling in general. > Certainly you can't zoom in nicely around a patch with zero width; > you have to draw the line somewhere. (ha ha, no pun intended) > > It just so happens that for my application, I need to plot Rectangles > that represent events as a function of time and frequency. As such, my > x-values (as produced by date2num()) are on the order of 732421, yet > I need to display patches that are like 10 seconds wide, which turns > out to be 0.000115740695037, after a date2num() conversion. > > This behavior is exhibited in the following lines of code. A delta of > 65000 seconds is sufficient to get the scaling right. > > > ######### > > > import matplotlib > import matplotlib.dates > from matplotlib.pylab import * > > import datetime > > ax = subplot(111) > > start_time = datetime.datetime(2006, 4, 21) > start_time_num = matplotlib.dates.date2num( start_time ) > > lower_left = [ start_time_num, 1.345680002 ] > width = matplotlib.dates.date2num( start_time + datetime.timedelta( seconds=100 ) ) - start_time_num > print 'width = ', width > > ax.add_patch( matplotlib.patches.Rectangle( lower_left, width, 2 ) ) > > ax.autoscale_view() > ax.xaxis.set_major_formatter( matplotlib.dates.DateFormatter('%Y') ) > > print ax.get_xlim() > show() > > > ########## > > So, this seems like a fairly narrow case where the threshold in use > causes a problem. I can't imagine another instance where 1e-6 would not > be prefectly fine. > > One solution would be to just patch the source for my deployment of this > software ... > > I hesitate to suggest that this level ought to be globally tunable by > the user, although an additional parameter that could be passed to > autoscale_view() wouldn't be as bad. Still that seems like clutter. > > What say ye? > > Thank you, > Glen Mabey > > > ------------------------------------------------------- > Using Tomcat but need to do more? Need to support web services, security? > Get stuff done quickly with pre-integrated technology to make your job easier > Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo > http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642 > _______________________________________________ > Matplotlib-users mailing list > Mat...@li... > https://lists.sourceforge.net/lists/listinfo/matplotlib-users |
From: Glen W. M. <Gle...@sw...> - 2006-04-27 16:59:30
|
On Wed, Apr 26, 2006 at 01:22:17PM -1000, Eric Firing wrote: > class myLocator(MaxNLocator): > def nonsingular(self, vmin, vmax, expander=0.001, tiny=1e-12): > return Locator.nonsingular(self, vmin, vmax, > expander=expander, tiny=tiny) > and then > > ax.xaxis.set_major_locator(myLocator()) This worked out of the box. Thanks! > As an alternative approach, can you simply use as your x-coordinate time > deltas from a suitable base time, so that vmin and vmax would not be so > huge compared to their difference? My assumption while working with the > ticker.py code was that this would be the logical solution to this sort > of problem. Well, the complication is that I not only need to be able to zoom in for ~10sec segments, but also to zoom out on long segments. So, yes, I could do a modulus-year operation, but that would complicate labeling of the ticks and also in getting data points back from click events. Doable, just a little more complicated. > Longer term, it would be easy to modify the Locator classes so that the > default values of tiny and expander would appear as a kwargs in > __init__. Whether this would be a valuable change or mere clutter, I > don't know; I will listen for more advice. If it's that simple, then I would vote for it. Surely someone will find it useful someday. However, the work-around I've implemented isn't bad. Thank you for your help. Glen Mabey |