Daniel
Daniel

Reputation: 351

Draw lines through figure of subplot matrix in matplotlib

I want to draw lines on a figure consisting of a varying number of subplots, looking like the red lines for the 3x3 example. How can I do this in matplotlib?

The code is basically an array of 2D projections of multidimensional data (upper-right half of a scatter matrix plot), on a 4D example.

tabular matrix

from matplotlib import pyplot as plt
import numpy as np
data = np.random.random_sample((10,4))
labels = ['p1','p2','p3','p4']
fig, axarr = plt.subplots(3,3, sharex='col', sharey='row')
# Iterate over rows of subplots array
for row in range(axarr.shape[0]):
    i = row # data index corresponds to row index
    # Iterate over columns of subplots array
    for col in range(axarr.shape[1]):
        j = col+1 # data index corresponds to column index +1
        # Do what's needed in lower-left half of array and leave
        if row>col:
            if col==0:
                axarr[row,col].set_ylabel(labels[i],labelpad=5)
            axarr[row,col].spines['left'].set_visible(False)
            axarr[row,col].spines['right'].set_visible(False)
            axarr[row,col].spines['bottom'].set_visible(False)
            axarr[row,col].spines['top'].set_visible(False)
            axarr[row,col].xaxis.set_ticks_position('none')
            axarr[row,col].yaxis.set_ticks_position('none')
            axarr[row,col].tick_params(labelleft=False)
            axarr[row,col].tick_params(labelbottom=False)
            continue
        # Proceed with upper-right half of array
        axarr[row,col].scatter(data[:,i],data[:,j], s=4)
        axarr[row,col].tick_params(labelleft=False)
        axarr[row,col].tick_params(labelbottom=False)
        if row==0:
            axarr[row,col].set_xlabel(labels[j],labelpad=5)
            axarr[row,col].xaxis.set_label_position('top')
        if col==0:
            axarr[row,col].set_ylabel(labels[i],labelpad=5)
            axarr[row,col].yaxis.set_label_position('left')

Upvotes: 1

Views: 845

Answers (1)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339310

Here is a solution which is independent of the actual figure size and scales together with the figure.

We use a blended transform to be able to specify the length of the line in figure coordinates, while specifying the vertical or horizontal position in axes coordinates of the top left subplot. So the vertical line goes from 0 to 1 in figure coordinates in y direction, while it is bound to x=0 in axes coordinates of the first subplot.
We then also add an offset transform to move it by half the linewidth in points, such that it sits tight against the axes spines.

enter image description here

from matplotlib import pyplot as plt
import matplotlib.lines
import matplotlib.transforms as transforms
import numpy as np

data = np.random.random_sample((10,10))
labels = "Some labels around all the subplots"
fig, axarr = plt.subplots(3,3, sharex='col', sharey='row')
for i, ax in enumerate(axarr.flatten()):
    ax.scatter(data[:,i], data[:,i+1])
    ax.xaxis.set_label_position('top')
for i in range(3):
    axarr[2-i,0].set_ylabel(labels.split()[i])
    axarr[0,i].set_xlabel(labels.split()[i+3])
    axarr[2-i,0].set_yticklabels([])

#### Create lines ####
lw=4  # linewidth in points
#vertical line
offset1 = transforms.ScaledTranslation(-lw/72./2., 0, fig.dpi_scale_trans)
trans1 = transforms.blended_transform_factory(
    axarr[0,0].transAxes +offset1, fig.transFigure)
l1 = matplotlib.lines.Line2D([0,0], [0, 1], transform=trans1,
            figure=fig, color="#dd0000", linewidth=4, zorder=0)
#horizontal line
offset2 = transforms.ScaledTranslation(0,lw/72./2., fig.dpi_scale_trans)
trans2 = transforms.blended_transform_factory(
     fig.transFigure, axarr[0,0].transAxes+offset2)
l2 = matplotlib.lines.Line2D([0, 1], [1,1], transform=trans2,
            figure=fig, color="#dd0000", linewidth=4, zorder=0)
#add lines to canvas
fig.lines.extend([l1, l2])

plt.show()

Here a smaller version, from which can be seen that the lines' position adapt to the figure size.

enter image description here

Upvotes: 1

Related Questions