2

I have a Matplotlib scatter plot, plotting 3 different circles and I am trying to find the axes x, y coordinates of the center point of each of these circles, i.e. a number within the bbox between 0 and 1. I am trying to do this because I save the image to a .png file and want to find the corresponding x,y point in the (512, 1024) space of the .png file.

I have tried to use the following code, but I have no idea how to interpret the vertices and codes this prints:

ax = fig.gca()
for collection in ax.collections:
    for path in collection._paths:
        for p in path.iter_segments():
            print p

It prints 10 arrays for the three circles, but this makes no sense to me because there are only three circles and I can't seem to find any documentation that helps explain this:

(array([ 0. , -0.5]), 1)

(array([ 0.13260155, -0.5 , 0.25978994, -0.44731685, 0.35355339, -0.35355339]), 4)

(array([ 0.44731685, -0.25978994, 0.5 , -0.13260155, 0.5 , 0. ]), 4)

(array([ 0.5 , 0.13260155, 0.44731685, 0.25978994, 0.35355339, 0.35355339]), 4)

(array([ 0.25978994, 0.44731685, 0.13260155, 0.5 , 0. , 0.5 ]), 4)

(array([-0.13260155, 0.5 , -0.25978994, 0.44731685, -0.35355339, 0.35355339]), 4)

(array([-0.44731685, 0.25978994, -0.5 , 0.13260155, -0.5 , 0. ]), 4)

(array([-0.5 , -0.13260155, -0.44731685, -0.25978994, -0.35355339, -0.35355339]), 4)

(array([-0.25978994, -0.44731685, -0.13260155, -0.5 , 0. , -0.5 ]), 4)

(array([ 0. , -0.5]), 79)

Thanks

0

1 Answer 1

3

I think you might be interested in the ax.transData transformation. Using the transform method, you can convert points in 'Data' units to display units. See the transformation tutorial for more information about this.

Below I have an example that includes an axes that fills the whole plot, so it is a bit easier to understand the transformation. I set three points in a scatter plot, then, I take the data I used for the plot and get, using the transform, the corresponding point in pixels for each of the three points.

import matplotlib.pyplot as plt

x = [1,2,4]
y = [1,3,2]

fig1 = plt.figure(figsize=(5,5),dpi=100)
ax = fig1.add_axes([0,0,1,1])

ax.scatter(x,y)

ax.set_xlim(0,5)
ax.set_ylim(0,5)
transDataToFig1 = ax.transData+fig1.transFigure.inverted()
for ix,iy in zip(x,y):
    inDots = ax.transData.transform((ix,iy))
    inFigIndirect = fig1.transFigure.inverted().transform(inDots)
    inFigDirect = transDataToFig1.transform((ix,iy))
    print inDots,"->",inFigIndirect," or ",inFigDirect

fig1.savefig('scatterPos1.png')

Since (1) the axes spans the whole figure, (2) the axes limits are set from 0 to 5, and (3) the figure size is set to 5x5 and 100 dots-per-inch, then one axes unit corresponds to 100 pixels. This is confirmed by the output:

[ 100.  100.] -> [ 0.2  0.2]  or  [ 0.2  0.2]
[ 200.  300.] -> [ 0.4  0.6]  or  [ 0.4  0.6]
[ 400.  200.] -> [ 0.8  0.4]  or  [ 0.8  0.4]

The above code also shows you how to get from dpi to figure units (the values within the bbox, between 0 and 1), and it provides an example of a transform pipeline (see tutorial linked above). For reference, this is the resulting figure:

enter image description here

Now if you make a second figure that has subplots, the process still works, but the pixel values are not as special. Take for example this additional code:

fig2=plt.figure(figsize=(5,5),dpi=100)
ax1 = fig2.add_subplot(121)
ax2 = fig2.add_subplot(122)
ax1.scatter(x,y)
ax1.set_xlim(0,5)
ax2.set_ylim(0,5)
ax2.plot(range(10))

transDataToFig2 = ax1.transData+fig2.transFigure.inverted()
for ix,iy in zip(x,y):
    inDots = ax1.transData.transform((ix,iy))
    inFigIndirect = fig2.transFigure.inverted().transform(inDots)
    inFigDirect = transDataToFig2.transform((ix,iy))
    print inDots,"->",inFigIndirect," or ",inFigDirect

fig2.savefig('scatterPos2.png')

It results in this figure:

enter image description here

And this output:

[  97.72727273  116.66666667] -> [ 0.19545455  0.23333333]  or  [ 0.19545455  0.23333333]
[ 132.95454545  383.33333333] -> [ 0.26590909  0.76666667]  or  [ 0.26590909  0.76666667]
[ 203.40909091  250.        ] -> [ 0.40681818  0.5       ]  or  [ 0.40681818  0.5       ]

Note that in this case, the transformation is sensitive to which axes you used. I used the axes ax1 because that is the one with the scatter plot that corresponds to the points stored in x and y.

These examples are different from what it sounds like you want to do. You have x,y positions in pixels and you want to see what point is selected. That is simply the inverted transform of transData, which normally (un-inverted) takes points in data units and returns them to points in display units (pixels). Each axes has it's own unique transData, which is sensitive to the axes limits.

For example, if I simulate a mouse click at position (120,480), in pixels, then each subplot would convert that into different data coordinates, like so:

click = (120,480)
print "Click ",click," transforms into:"
print ax1.transData.inverted().transform(click)," for axes 1"
print ax2.transData.inverted().transform(click)," for axes 2"

This results in:

Click  (120, 480)  transforms into:
[ 1.63225806  3.725     ]  for axes 1
[-7.86193548  9.675     ]  for axes 2

Note that the transformation for ax2 (the second subplot) leads to a negative x position. This is because the pixel position (120,480) is outside the x limits of the second subplot.

Sign up to request clarification or add additional context in comments.

2 Comments

This worked great when I only had one subplot, is there something I should do when I am dealing with multiple subplots? The numbers I get when I compare inFigIndirect and inFigDirect are very different, so I am not sure how to proceed. Any help would be greatly appreciated. Thanks!
@hotshotiguana That's because there was an issue with my definition of the transDataToFig transform. It was defined in the wrong order, so inFigDirect was not correctly calculated. It happened to behave well for the special example above, but you are right it failed when I went to a less forced example. I added a further example and discussion. Please let me know if this is helpful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.