0

I am trying to plot a graph like the following and want to connect the points by lines. However, as you can see some of the points (above 0.04 on X axis) are partially overlapping and that does not allow us to represent the connection between them.fig 1.

What I want to do is, make 2 separate graphs with 1 graph having all the points above 0.04 (so that it will be zoomed in and the points will be separated) and other one with just the one point in top left corner. fig 2.

Note that, the size of the points also contains some meaning. So, I can not make the points smaller or larger in size. (unless the change is uniform across all the points)

What is the good way to do this? Is there any function in matplotlib providing such feature? Or is there any other python library apart from matplotlib where I can do this in a better way?

Community
  • 1
  • 1
Niranjan
  • 15
  • 7
  • 1
    While trying to find a better approach than my answer below, I ran into [another solution](http://stackoverflow.com/questions/17543359/drawing-lines-between-two-plots-in-matplotlib).. – Bart Dec 03 '15 at 19:57

1 Answers1

1

Edit Based on this post, a better solution than my previous one might be:

import matplotlib.pylab as pl
import matplotlib
import numpy as np

pl.close('all')

x = np.linspace(0.019, 0.021, 4)
y = np.linspace(0.09,  0.10,  4)
s = np.random.randint(10, 200, 4)

fig = pl.figure()

ax1=pl.subplot(121)
pl.scatter(x, y, s=s)
pl.xlim(0.01, 0.04)
pl.ylim(0.04, 0.12)
pl.xticks([0.01,0.02,0.03,0.04])
pl.yticks([0.04,0.06,0.08,0.10,0.12])

ax2=pl.subplot(122)
pl.scatter(x, y, s=s)
pl.xlim(0.018, 0.022)
pl.ylim(0.08, 0.11)
pl.xticks([0.018,0.020,0.022])
pl.yticks([0.08,0.095,0.11])

transFigure = fig.transFigure.inverted()
for i in range(x.size):
    xy1 = transFigure.transform(ax1.transData.transform([x[i],y[i]]))
    xy2 = transFigure.transform(ax2.transData.transform([x[i],y[i]]))

    line = matplotlib.lines.Line2D((xy1[0],xy2[0]),(xy1[1],xy2[1]),
                                   transform=fig.transFigure)
    fig.lines.append(line)

enter image description here


The other (old) solution:

Interesting question. I came up with the "solution" below (although it ain't pretty...); it does an ax.transData.transform from the data coordinates to figure coordinates, and uses ax.annote to draw the arrows, but this solution unfortunately only works if you keep the figure dpi (dots per inch) equal to the figure ppi (points per inch).

If I can think of a better solution, I'll post it here.

import matplotlib.pylab as pl
import numpy as np

x = np.linspace(0.019, 0.021, 4)
y = np.linspace(0.09,  0.10,  4)
s = np.random.randint(10, 200, 4)

# Transform the data coordinates to figure (pixel) coordinates    
def get_display_coordinates(x,y):
    ax = pl.gca()
    xd = np.zeros_like(x)
    yd = np.zeros_like(y)

    for i in range(x.size):
        xd[i], yd[i] = ax.transData.transform([x[i], y[i]])

    return xd, yd

pl.figure(dpi=72)

ax=pl.subplot(121)
sc=pl.scatter(x, y, s=s)
pl.xlim(0.01, 0.04)
pl.ylim(0.04, 0.12)
pl.xticks([0.01,0.02,0.03,0.04])
pl.yticks([0.04,0.06,0.08,0.10,0.12])
xd_1, yd_1 = get_display_coordinates(x,y)

ax=pl.subplot(122)
pl.scatter(x, y, s=s)
pl.xlim(0.018, 0.022)
pl.ylim(0.08, 0.11)
pl.xticks([0.018,0.020,0.022])
pl.yticks([0.08,0.095,0.11])
xd_2, yd_2 = get_display_coordinates(x,y)

for i in range(x.size):
    ax.annotate("",
                xy=(xd_2[i], yd_2[i]), xycoords='figure pixels',
                xytext=(xd_1[i], yd_1[i]), textcoords='figure pixels',
                arrowprops=dict(arrowstyle="->", connectionstyle="arc3"))

pl.savefig('test.png', dpi=72)

enter image description here

Community
  • 1
  • 1
Bart
  • 9,825
  • 5
  • 47
  • 73