# get_minmax_data on implicit_plot

This is a sequel of my question about plotting level set.

In the following, G is a circle :

sage: f(x,y)=x**2+y**2
sage: G=implicit_plot(f==1,(x,-2,2),(y,-3,3))
sage: G.get_minmax_data()
{'xmin': -2.0, 'ymin': -3.0, 'ymax': 3.0, 'xmax': 2.0}


The "correct" get_minmax_data sould be

{'xmin': -1.0, 'ymin': -1.0, 'ymax': 1.0, 'xmax': 1.0}


As far as I understood the code (and the thread "Retrieving xy data from implicit plots" on Sage-support), the following is the relevant part :

xy_data_arrays = numpy.asarray([[[func(x, y) for x in xsrange(*ranges[0],include_endpoint=True)]
for y in xsrange(*ranges[1], include_endpoint=True)]
for func in g],dtype=float)


in ../plot/contour_plot.py

My questions are :

1. can I retrieve that xy_data_array ?

2. If I analyse xy_data_array, I suppose that extracting the point with lowest x-component such that the value is positive will provide me the "correct" xmin of the plot. I'm wrong ?

edit retag close merge delete

Sort by » oldest newest most voted

Following up on the comment discussion about which points are actually used in a plot, here's a revised version of the original answer I gave before kcrisman pointed out g.xy_data_array and I deleted mine as stupid by comparison. This is pretty ugly:


import matplotlib

def get_paths_from_plot(p):
# untested!
m = p.matplotlib()
sp = m.get_children()[1]
for c in sp.get_children():
# not sure if I need to test for both or not? don't understand
# matplotlib internals well enough to know if every Line2D
# will be in some LineCollection and so this is pure duplication (probably)
if isinstance(c, matplotlib.lines.Line2D):
yield c.get_path()
elif isinstance(c, matplotlib.collections.LineCollection):
for path in c.get_paths():
yield path

def get_bounds_from_implicit_plot(P):
# untested!
xx = []
yy = []
for path in get_paths_from_plot(P):
xx += list(path.vertices[:,0])
yy += list(path.vertices[:,1])
print 'xx:', sorted(xx)[:10]
print 'yy:', sorted(yy)[:10]
d = {'xmin': min(xx), 'xmax': max(xx),
'ymin': min(yy), 'ymax': max(yy)}
return d

sage: f(x,y)=x**2+y**2
sage: G=implicit_plot(f==1,(x,-2,2),(y,-3,3))
sage: get_bounds_from_implicit_plot(G)
xx: [-0.99972791583529708, -0.99972791583529708, -0.99972791583529697, -0.99972791583529697, -0.99809541084708742, -0.99809541084708742,
-0.99809541084708719, -0.99809541084708719, -0.9948304008706681, -0.9948304008706681]
yy: [-0.99973154362416372, -0.99973154362416372, -0.99973154362416361, -0.99973154362416361, -0.99901565995525998, -0.99901565995525998,
-0.99901565995525976, -0.99901565995525976, -0.99758389261745239, -0.99758389261745239]
{'xmin': -0.99972791583529708, 'ymin': -0.99973154362416372,
'ymax': 0.99973154362415995, 'xmax': 0.99972791583530107}

more

Thanks a lot ! I'm going to test that on my favourites examples. For informations, I need these xmin,xmax,ymin,ymax in order to automatically determine the bounding box in a pspicture (LaTeX). See [here](http://student.ulb.ac.be/~lclaesse/phystricks-doc.pdf) in the section "Implicit curves"

( 2011-02-06 03:29:17 -0500 )edit

For what it's worth I expect it to fail. :^) I was lazy and hard-coded the [1], so if the AxesSubplot isn't there it'll break, etc., etc. But the information should be in the Paths, so we can probably get at it if we need to. (There might also be some coordinate transformations we need to do, depending.) Hopefully the matplotlib guys can explain the One True Way(tm) to do the above!

( 2011-02-06 03:34:35 -0500 )edit

Actually, the correct data is given. You specified implicit_plot(f==1,(x,-2,2),(y,-3,3)), so it gave you exactly those bounds. If you had done

sage: G=implicit_plot(f==1,(x,-1,1),(y,-1,1))
sage: G.get_minmax_data()
{'xmin': -1.0, 'ymin': -1.0, 'ymax': 1.0, 'xmax': 1.0}


you'd get what you expect.

What is going on here is that implicit_plot just creates a contour plot of the equation with only one contour level.

sage: G=contour_plot(f==1,(x,-1,1),(y,-1,1),contours=[0],fill=False); G


As to your question, this is the attribute (not method)

sage: g = G[0]
sage: g.xy_data_array


Which is large, as it's the values of the function at EVERY data point! Only the 'right' points are connected, using matplotlib's contour functionality. See this as well:

sage: sage.plot.contour_plot.ContourPlot??
class ContourPlot(GraphicPrimitive):
"""
Primitive class for the contour plot graphics type.  See
contour_plot? for help actually doing contour plots.

INPUT:

- xy_data_array - list of lists giving evaluated values of the function on the grid


I hope this helps! By the way, as long as you are in the Sage interpreter or notebook (or a .sage file), you can do f(x,y)=x^2+y^2; you only need ** if you are writing a Python file.

more

+1 for a much better solution! I must have tried xy_data_array on G rather than on G[0] and gave up too soon. On the bright side, I got to search deep in matplotlib's data structures. :-p

( 2011-02-04 13:34:08 -0500 )edit

Thank for the answer, kcrisman. I did the mistake DSM said :) Using G[0].xy_data_array and digging in "by hand", I obtain my result: the xmin,xmax,ymin,ymax of points that are actually plotted. Obviously I would be happier using the matplotlib's functionality ... but I got lost in the code of matplotlib/contour.py ... where can I get the list of "right" points ?

( 2011-02-04 23:06:45 -0500 )edit

Yes, the Graphics objects are lists - so it's annoying when trying to access the stuff, but great when (as usually is the case) you want them to 'just work'. I have no answer for the matplotlib question. Why not ask on their support list? If we can give easy access to it, that would be useful to do.

( 2011-02-05 14:28:48 -0500 )edit

The "right" points are buried pretty deeply. G.matplotlib().get_children()[1] gives the lines etc. that make up the image, and Line2D (and LineCollection?) instances there have a method .get_path() (.get_paths()), and those Path objects returned have an attribute ".vertices" which lists the information.

( 2011-02-05 14:35:33 -0500 )edit

G.matplotlib().get_children() returns [<matplotlib.patches.rectangle object="" at="" 0xc144e4c="">, <matplotlib.axes.axessubplot object="" at="" 0xc14472c="">] Is the data in the Rectiangle ? In the Axes ? I sent the question to the matplotlib's support list.

( 2011-02-06 01:29:32 -0500 )edit