From: John H. <jdh...@ac...> - 2004-08-19 14:56:06
|
>>>>> "Jon" == Jon Wright <wr...@es...> writes: Jon> Hi all, Is there a trick hidden somewhere for autoscaling one Jon> axis only? I am frequently plotting data which has a series Jon> of narrow peaks and a large dynamic range. After zooming on Jon> the x-axis to the region of interest, I'd like to have an Jon> option to make the y axis rescale for the data in that range Just to make sure you know, with the new toolbar2 (matplotlib-0.61) you can selectively scale the yaxis interactively by pressing the pan/zoom button, and clicking and dragging your right mouse button over the y axis while holding down the 'y' key. Ditto for pan with the left button. Jon> only. Having flailed around in the source I have the feeling Jon> that this is possible, but I haven't quite fathomed out how Jon> to do it (I'm using matplotlib for an interactive plot Jon> embedded in a tk application). Essentially I want the Jon> appropriate magic spell to give me a bounding box in y for Jon> the current x axis limits. The things I tried so far always Jon> seem to give the bounding box for all the data points, Jon> including the ones which are not currently being plotted on Jon> the x range. Sorry if I've missed something obvious! No you're not missing anything. I can give you an idea of how to hack this though. The autoscaling is controlled by a tick locator, found in matplotlib.tickers. There are a number of locators which derive from the Locator base class. You can access the major tick locator of a given axis with, for example locator = ax.yaxis.get_major_locator() The locator has a method to compute the view limits vmin, vmax = locator.autoscale() It uses a _transforms.Interval instance under the hood to get the data limits dmin, dmax which gives the min and max range for your data on that axis. Of course, this is the min and max for all the y data, not just the data in the current xrange, which is your problem. In order to solve this, your need to: 1) compute the data limits in the current viewport, 2) set the limited data lim on the axis interval instance, 3) use it to get the new autoscale limits, and 4) reset the old data lim interval instance back to its original setting. Assuming you know the y data lim in the xrange of interest (more on that later) you would do (untested but should work barring an obvious screw up) interval = ax.yaxis.get_data_interval() savemin, savemax = interval.get_bounds() dmin, dmax = # compute the y data limits in this xrange as below interval.set_bounds(dmin, dmax) # the locator has a ref to the interval and so sees your changes ax.set_ylim(locator.autoscale()) # autoscale returns (vmin, vmax) # now reset the original data lim interval.set_bounds(savemin, savemax) The only remaining thing is to get the ydata in xrange. This depends on the kind of data you plotted, but let's assume it is from 'plot' and thus your data are stored in Line2D instances. With a single line (eg returned by the call) 'line, = plot(x,y)' (note the comma for tuple unpacking) you can do import matplotlib.numerix as nx xmin, xmax = ax.get_xlim() xdata = line.get_xdata() ydata = line.get_xdata() ind = nx.nonzero(mx.logical_and(nx.greater_equal(xdata, xmin) nx.less_equal(xdata, xmax))) y = nx.take(ydata, ind) dmin, dmax = min(y), max(y) # and now set the data lim in the interval instance as indicated # above Now that's the process for a single line. For multiple lines, all you need to do is keep a running total of all the data to get the min/max of all the lines. The function ax.get_lines() will return all the data lines matplotlib uses. If you have scatters, pcolors and other kinds of plots which may use collections and other data structures, then more work would be needed still. However, you may have direct access to your data, in which case you can just use that to extract the ydata in the range. BTW, there has been some discussion on the devel list recently about a plugin feature to support easy customization and extension of the toolbar. This kind of thing is a perfect candidate for that, because you could create a plugin for 'autoscale y in the current xrange' and we could place it in a contrib plugin dir that others could add to their toolbar when they want. Hope this helps, JDH |