From: Alan G I. <ai...@am...> - 2004-09-13 12:39:14
|
Setting the compression ratio to unity is important for some graphs. (Definition: compression ratio = aspect ratio of viewing window/aspect ratio of viewport). In gnuplot you do this by simply setting set size ratio vwar where vwar is the aspect ratio of the viewing window, as determined by previous xrange and yrange commands. I believe the following is the procedure in matplotlib.matlab: i. set the figsize ii. use the axes command to set the propert viewport aspect ratio, which requires careful attention to figsize (and of course knowledge of the limits to be set with the axis command) iii. plot your graph iv. use the axis command to set the aspect ratio of the viewing window My description is a bit verbose, but it may nevertheless highlight some nice things about gnuplot's size command. (Or I may have missed the obvious way to do this.) This doesn't matter much in scripts, but for interactive use (e.g., classroom demonstration) it is nice to keep things as simple and straightforward as possible. So this is almost a feature request. The only problem is that gnuplot's size command does not seem obviously suited to the matplotlib.matlab model, and I'm not sure what a good version would be. I think it would be something like "get the current axis, keep the "long" axis (as implied by the ratio) full length, and "shrink" the shorter axis as necessary to achieve the right ratio. This would respect the existing margins to everywhere except by the shrinking axis. However it would probably be better to adjust the figure size as well, to keep the margins unchanged. fwiw, Alan |
From: John H. <jdh...@ac...> - 2004-09-13 14:25:03
|
>>>>> "Alan" == Alan G Isaac <ai...@am...> writes: Alan> So this is almost a feature request. The only problem is Alan> that gnuplot's size command does not seem obviously suited Alan> to the matplotlib.matlab model, and I'm not sure what a good Alan> version would be. I think it would be something like "get Alan> the current axis, keep the "long" axis (as implied by the Alan> ratio) full length, and "shrink" the shorter axis as Alan> necessary to achieve the right ratio. This would respect Alan> the existing margins to everywhere except by the shrinking Alan> axis. However it would probably be better to adjust the Alan> figure size as well, to keep the margins unchanged. I'm not fully understanding you, in part because I find the terminology ambiguous. Could you give an example of what you want to achieve (eg a scatterplot with a square viewing window). For concreteness, we have ylim / xlim : the data view limits axh / axw : the axes height / width, relative coords 0-1 figh / figw : the figure width and height in inches dpiy / dpix : the display devices resolution in pixels in the vertical and horizontal directions In order to get true physical sizes and ratios, we need to know dpiy and dpix. matplotlib currently only supports a single dpi, though it is still possible to hack true sizes by adjusting for example, figh and figw to compensate for variation in dpiy and dpix. It is certainly possible to provide some helper functions to create a properly sized and scaled figure and axes with a desired aspect ratio or physical size. JDH |
From: Chris B. <Chr...@no...> - 2004-09-13 16:55:26
|
John Hunter wrote: > I'm not fully understanding you, in part because I find the > terminology ambiguous. I'm not sure what he means either, but it sounds like what he wants is to be able to specify the ratio of the x-scale and y-scale of the figure. Most commonly, you'd want to specify they are the same, like MATLAB's axis('equal') Even if the OP wasn't looking for that, I am. Can matplotlib do that? -Chris -- Christopher Barker, Ph.D. Oceanographer NOAA/OR&R/HAZMAT (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chr...@no... |
From: Alan G I. <ai...@am...> - 2004-09-14 05:28:12
|
On Mon, 13 Sep 2004, Chris Barker apparently wrote: > I'm not sure what he means either, but it sounds like what he wants is > to be able to specify the ratio of the x-scale and y-scale of the > figure. Most commonly, you'd want to specify they are the same, like > MATLAB's > axis('equal') This seems the same in the 2D case. I haven't thought about what it means in the 3D case. Cheers, Alan Isaac |
From: Alan G I. <ai...@am...> - 2004-09-14 05:28:11
|
Alan G Isaac <ai...@am...> writes: >> So this is almost a feature request. The only problem is >> that gnuplot's size command does not seem obviously suited >> to the matplotlib.matlab model, and I'm not sure what a good >> version would be. I think it would be something like "get >> the current axis, keep the "long" axis (as implied by the >> ratio) full length, and "shrink" the shorter axis as >> necessary to achieve the right ratio. This would respect >> the existing margins to everywhere except by the shrinking >> axis. However it would probably be better to adjust the >> figure size as well, to keep the margins unchanged. On Mon, 13 Sep 2004, John Hunter apparently wrote: > I'm not fully understanding you, in part because I find the > terminology ambiguous. Could you give an example of what you want to > achieve (eg a scatterplot with a square viewing window). For > concreteness, we have > ylim / xlim : the data view limits > axh / axw : the axes height / width, relative coords 0-1 > figh / figw : the figure width and height in inches > dpiy / dpix : the display devices resolution in pixels in the > vertical and horizontal directions > In order to get true physical sizes and ratios, we need to know dpiy > and dpix. matplotlib currently only supports a single dpi, though it > is still possible to hack true sizes by adjusting for example, figh > and figw to compensate for variation in dpiy and dpix. In gnuplot if I want to set a compression ratio of unity for a figure that has an xrange of [xmin,xmax] and a yrange of [ymin,ymax], I just say set size ratio (ymax-ymin)/(xmax-xmin) So e.g. if I do this when I am plotting a function f:R->R then the unit size will be physically identical along each axis. So for example if I plot a circle it looks like a circle and not an oval. In a very clever move, gnuplot also allows set size ratio -1 to achieve the same effect. Crudely: I'm looking for something just as simple in matplotlib.matlab. My last post outlined what I think the current procedure is to achieve the same effect, and it is much more cumbersome. While this matters little when one is leisurely writing a script, it matters a lot when one is doing interactive plotting in a classroom setting. Now you point out that this request has an ambiguity: what "physical" medium am I talking about? Specifically, a true compression ratio on screen requires tricky stuff if dpiy != dpix. OK, I assume ignoring this will yield a "close enough" result in a classroom setting, so the only time I need it to be exact is when I produce figures for paper display. Hope that is clearer. Thank you, Alan Isaac |
From: John H. <jdh...@ac...> - 2004-09-14 14:25:29
|
>>>>> "Alan" == Alan G Isaac <ai...@am...> writes: Alan> Crudely: I'm looking for something just as simple in Alan> matplotlib.matlab. My last post outlined what I think the Alan> current procedure is to achieve the same effect, and it is Alan> much more cumbersome. While this matters little when one is Alan> leisurely writing a script, it matters a lot when one is Alan> doing interactive plotting in a classroom setting. Alan> Now you point out that this request has an ambiguity: what Alan> "physical" medium am I talking about? Specifically, a true Alan> compression ratio on screen requires tricky stuff if dpiy != Alan> dpix. OK, I assume ignoring this will yield a "close Alan> enough" result in a classroom setting, so the only time I Alan> need it to be exact is when I produce figures for paper Alan> display. w/o having to worry about dpi differences in the x and y direction, it is much easier, and your hardcopy will be correct in any case. Do you really need arbitrary compression ratios, or just ratios equal to 1. I added the option 'equal' to the axis command, which simply sets the xlim and ylim to be equal and includes the range of both. I'll include the modified axis method from matlab.py at the end of this email. Thus you can do from matplotlib.matlab import * figure(figsize=(6,6)) ax = axes([0.1, 0.1, 0.8, 0.8]) plot(rand(100), rand(100), 'o') axis('equal') show() Admittedly this is more typing than you want to do in an interactive session. I experimented with a function 'square' which isn't the right name but you get the idea. It handles setting the figure size, axes ratio and lim ratio for you. Some suitably named function could be included in the standard interface from matplotlib.matlab import * def square(): fig = gcf() ax = gca() figw, figh = fig.get_size_inches() l,b,axw,axh = ax.get_position() side = min([figw, figh]) frac = min([axw, axh]) fig.set_figsize_inches(side, side) ax.set_position([l,b,frac,frac]) axis('equal') draw() plot(rand(100), rand(100), 'o') square() show() It's the right idea, but it doesn't work currently if you are using a GUI backend. The reason it fails is has to do with the pipeline order in which figure managers, figures etc are created by default, and the fact that configure events and resize events change the figure width and height (so you can for example, resize your figure window). It *does* work if you specify the figure size figure(figsize=(8,8)) plot(rand(100), rand(100), 'o') square() show() because then matplotlib has the information it needs at figure creation time. Of course, you can set the figure size to have a unity aspect ratio in your rc file and then you could do without the figsize command altogether. I'll think about how to best fix the GUI resizing problem. I'll probably need to add an observer to the figure limits so that the GUI backend can update its size from script calls to figure size. As an aside, traits are starting to look very useful, with their build in observer framework. For now, any feedback on whether this approach looks like the right one, whether any ratios other than 1 are actually needed, what the function square should be called and anything else is welcome. JDH def axis(*v): """ axis() returns the current axis as a length a length 4 vector axis(v) where v= [xmin xmax ymin ymax] sets the min and max of the x and y axis limits axis('off') turns off the axis lines and labels axis('equal') sets the xlim and ylim to be identical """ if len(v)==1 and is_string_like(v[0]): s = v[0] if s.lower()=='on': gca().set_axis_on() elif s.lower()=='off': gca().set_axis_off() elif s.lower()=='equal': ax = gca() xmin, xmax = ax.get_xlim() ymin, ymax = ax.get_ylim() vmin = min([xmin, ymin]) vmax = max([xmax, ymax]) ax.set_xlim((vmin, vmax)) ax.set_ylim((vmin, vmax)) draw_if_interactive() else: error_msg('Unrecognized string %s to axis; try on or off' % s) return try: v[0] except IndexError: xlim = gca().get_xlim() ylim = gca().get_ylim() return [xlim[0], xlim[1], ylim[0], ylim[1]] v = v[0] if len(v) != 4: error_msg('v must contain [xmin xmax ymin ymax]') return gca().set_xlim([v[0], v[1]]) gca().set_ylim([v[2], v[3]]) draw_if_interactive() |
From: Alan G I. <ai...@am...> - 2004-09-20 13:38:20
|
On Tue, 14 Sep 2004, John Hunter apparently wrote: > def square(): > fig = gcf() > ax = gca() > figw, figh = fig.get_size_inches() > l,b,axw,axh = ax.get_position() > side = min([figw, figh]) > frac = min([axw, axh]) > fig.set_figsize_inches(side, side) > ax.set_position([l,b,frac,frac]) > axis('equal') > draw() OK, this does not work for me. Test case: d=arange(201)*pi/100 plot(cos(d),sin(d)) square() show() This should look like a circle. It looks like an oval. Problems: 1. fig.set_figsize_inches is ignored. It has no effect on figure size. (It changes the size the figure reports, as checked by printing it, but not the size that is drawn---a missing argument to draw?) 2. axis('equal') should not be desirable, if it is matlab compatible http://www.mathworks.com/access/helpdesk/help/techdoc/ref/axis.html That is, square() should set an axis aspect ratio of unity, but axis('equal') should equate the *unit length* along each axis. (For my example circle this is not a problem, but in generaly this is completely wrong.) Hope that's useful, Alan Isaac |
From: John H. <jdh...@ac...> - 2004-09-20 14:17:18
|
>>>>> "Alan" == Alan G Isaac <ai...@am...> writes: Alan> OK, this does not work for me. Test case: Alan> d=arange(201)*pi/100 plot(cos(d),sin(d)) square() show() Alan> This should look like a circle. It looks like an oval. Alan> Problems: 1. fig.set_figsize_inches is ignored. It has no Right, I noted this in my original post - if you continue that post where I posted this code: It's the right idea, but it doesn't work currently if you are using a GUI backend. The reason it fails is has to do with the pipeline order in which figure managers, figures etc are created by default, and the fact that configure events and resize events change the figure width and height (so you can for example, resize your figure window). It *does* work if you specify the figure size figure(figsize=(8,8)) This is a bug, as I mentioned before, but will require some thought about how to get this right across backends. Alan> 2. axis('equal') should not be desirable, if it is matlab Alan> compatible Alan> http://www.mathworks.com/access/helpdesk/help/techdoc/ref/axis.html Alan> That is, square() should set an axis aspect ratio of unity, Alan> but axis('equal') should equate the *unit length* along each Alan> axis. (For my example circle this is not a problem, but in Alan> generaly this is completely wrong.) With the amended axes code I'm attaching below, does this example work as you hope from matplotlib.matlab import * figure(figsize=(8,8)) ax = axes([0.1, 0.1, 0.8, 0.8]) d=arange(201)*pi/100 plot(10+cos(d),sin(d)) axis('equal') show() Perhaps more typing than you want for an interactive demo, but does make everything clear and should work, at least up to the dpix/dpiy error we discussed earlier. If it works fine, I'll make it a FAQ. I still have to handle the case where the axis lim are decreasing... JDH def axis(*v): """ axis() returns the current axis as a length a length 4 vector axis(v) where v= [xmin xmax ymin ymax] sets the min and max of the x and y axis limits axis('off') turns off the axis lines and labels axis('equal') sets the xlim width and ylim height to be to be identical. The longer of the two intervals is chosen """ if len(v)==1 and is_string_like(v[0]): s = v[0] if s.lower()=='on': gca().set_axis_on() elif s.lower()=='off': gca().set_axis_off() elif s.lower()=='equal': ax = gca() xmin, xmax = ax.get_xlim() ymin, ymax = ax.get_ylim() width = xmax-xmin height = ymax-ymin # TODO: handle decreasing lim interval = max([width, height]) ax.set_xlim((xmin, xmin+interval)) ax.set_ylim((ymin, ymin+interval)) draw_if_interactive() else: error_msg('Unrecognized string %s to axis; try on or off' % s) return try: v[0] except IndexError: xlim = gca().get_xlim() ylim = gca().get_ylim() return [xlim[0], xlim[1], ylim[0], ylim[1]] v = v[0] if len(v) != 4: error_msg('v must contain [xmin xmax ymin ymax]') return gca().set_xlim([v[0], v[1]]) gca().set_ylim([v[2], v[3]]) draw_if_interactive() |
From: Alan G I. <ai...@am...> - 2004-09-20 19:10:19
|
Hi John, Since I read your previous message so poorly, I really appreciate your willingness to communicate again. At the moment I'm overcaffeinated and underslept, so I may do as badly again. I just do not quite "get it" yet. I think I do not fully understand xlim and ylim. What I have been doing up to now to get what I need is to use figsize and 'axes' to determine the aspect ratio of the viewport (AR(V), the screen area where the data will be displayed) and then (after the plot) 'axis' to determine the aspect ratio of the viewing window (AR(W), the relative lengths of the axes in data units). OK, I see all these same steps, with the only thing new that instead of having to plug the right numbers into 'axis', I get a keyword instead. (That "only" is comparative, not disparaging, of course.) So here is what I think you are offering: a keyword that will set the same "width" of data to be displayed on each axis, so that the AR(W)=1. This means that I can determine the compression ration CR=AR(W)/AR(V) from AR(V) alone. And in particular, if I use figsize and 'axes' to give me AR(V)=1, then axis('equal') will produce a compression ratio of unity as well. Do I have that right? If so: i. this seems useful ii. this seems somewhat related to the behavior of axis('square') in Matlab, if I read the documentation right http://www.mathworks.com/access/helpdesk/help/techdoc/ref/axis.html iii. This does *not* seem to be like the behavior of Matlab's axis('equal') I'm not a Matlab user, but (relying on the documentation) here is an example of the kind of thing I think you get from axis('equal') in Matlab: #define function to graph def f(x): return 2*x**3-5*x #choose points in domain for plotting d=(arange(401)-200.0)/100 #set figure size for plot figure(1,figsize=(2,5)) #determine AR(V) = AR(W) = 5/2 axes([0.1, 0.1, 0.8, 0.8]) plot(d,f(d)) #determine AR(W) = 5/2 axis([-2,2,-5,5]) show() Conceptually this works a little backwards, of course. What you really know first is the AR(W) that you want, and you choose the AR(V) to yield a compression ratio of unity (so that the units match along each axis). So if I have understood correctly, you have indeed offered something that can be useful, but it should have a different name. Or this may be overcaffeinated babble. Gotta run... fwiw, Alan |