l-d-s
l-d-s

Reputation: 13

set_aspect() and coordinate transforms in matplotlib

I run into what seems to be a bug in matplotlib (version 1.4.3)/pyplot when attempting to draw a line between subplots: after setting set_aspect("equal"), it appears the relevant coordinate transformation functions (transData) don't update. Execute the code below with ax.set_aspect("equal") uncommented to see the difference.

import matplotlib.pyplot as plt
import matplotlib as mpl    

f, (ax1, ax2) = plt.subplots(1, 2, sharey='all', sharex='all')

for ax in (ax1, ax2):
    # ax.set_aspect("equal")
    ax.set_ylim([-.2, 1.2])
    ax.set_xlim([-.2, 1.2])        

# From http://stackoverflow.com/questions/17543359/drawing-lines-between-two-plots-in-matplotlib

transFigure = f.transFigure.inverted()

coord1 = transFigure.transform(ax1.transData.transform([0,0]))
coord2 = transFigure.transform(ax2.transData.transform([1,0]))

line = mpl.lines.Line2D((coord1[0],coord2[0]),(coord1[1],coord2[1]),
                    transform=f.transFigure)
f.lines.append(line)

plt.show()

The relevant images are here (1) and here (2).

Upvotes: 1

Views: 665

Answers (2)

jedwards
jedwards

Reputation: 30210

cge is right with his assessment of why, but for me, his code produced:graph1

The x-limits are getting confused because they're supposed to match the y-limits and be equal.

To fix this, you can adjust your figure size to something more appropriate with the figsize keyword argument to subplots. In the following code, I chose 10in x 5in.

import matplotlib.pyplot as plt
import matplotlib as mpl

f, (ax1, ax2) = plt.subplots(1, 2, sharey='all', sharex='all', figsize=(10,5))

for ax in (ax1, ax2):
    ax.set_aspect("equal")
    ax.set_ylim([-.2, 1.2])
    ax.set_xlim([-.2, 1.2])

plt.draw()


# From http://stackoverflow.com/questions/17543359/drawing-lines-between-two-plots-in-matplotlib
transFigure = f.transFigure.inverted()

coord1 = transFigure.transform(ax1.transData.transform([0,0]))
coord2 = transFigure.transform(ax2.transData.transform([1,0]))

line = mpl.lines.Line2D(
                    (coord1[0],coord2[0]),
                    (coord1[1],coord2[1]),
                    transform=f.transFigure)
f.lines.append(line)

plt.show()

This produces: enter image description here

Upvotes: 0

cge
cge

Reputation: 9890

The problem you're having is that set_aspect isn't applied until after a draw operation. Thus when you're making the line, the limits haven't been changed. Notice the different x limits in your second image, while the line is in the same place: the line is drawn as though the x limits didn't change, because they hadn't been changed yet, and wouldn't be changed until plt.show(). The solution to this is to add a plt.draw() after you do set_aspect, but before you start working on the transforms. The following code does this, with print statements that make clear the issue with the limits and transforms at different times:

import matplotlib.pyplot as plt
import matplotlib as mpl    

f, (ax1, ax2) = plt.subplots(1, 2, sharey='all', sharex='all')

for ax in (ax1, ax2):
    ax.set_ylim([-.2, 1.2])
    ax.set_xlim([-0.2, 1.2])  

transFigure = f.transFigure.inverted()

print ax1.get_xlim(), ax1.transData.transform([0,0])

for ax in (ax1, ax2):
    ax.set_aspect('equal')

print ax1.get_xlim(), ax1.transData.transform([0,0])

plt.draw()

print ax1.get_xlim(), ax1.transData.transform([0,0])

coord1 = transFigure.transform(ax1.transData.transform([0,0]))
coord2 = transFigure.transform(ax2.transData.transform([1,0]))

line = mpl.lines.Line2D((coord1[0],coord2[0]),(coord1[1],coord2[1]),
                    transform=f.transFigure)
f.lines.append(line)

plt.show()

This point should really be added to the docstring for set_aspect, and I'll see if I can do that. It's not a bug: the aspect can't really be determined until after the plot is ready to be drawn.

Upvotes: 1

Related Questions