How to pick a point in a subplot and highlight it in adjacent subplots in matplotlib(extension to region of points)

StackOverflow https://stackoverflow.com/questions/22355435

Question

I want to create a scatter plot matrix which will be composed by some subplots. I have extracted from a .txt file my data and created an array of shape (x,y,z,p1,p2,p3). The first three columns of the array represent the x,y,z coordinates from the original image that these data come from and the last three columns(p1, p2, p3) some other parameters. Consequently, in each row of the array the parameters p1, p2, p3 have the same coordinates(x,y,z).In the scatter plot, I want to visualize the p1 parameter against the p2, p3 parameters in a first stage. For every point I pick, I would like its (x,y,z) parameters from the first three columns of my array to be annotated and the point with the same coordinates in the adjacent subplot to be highlighted or its color to be modified.

In my code, two subplots are created and in the terminal are printed the (p1,p2 or p3) values that are acquired by picking a point, the respective values of the same point in the adjacent subplot and the (x,y,z) parameters of this point.

Moreover, when I pick a point in the first subplot, the color of the corresponding point in the second subplot changes but not vice versa. This color modification is recognizable only if I resize manually the figure. How could I add interactivity for both subplots without having to tweak the figure in order to notice any changes? What kind of modifications should I make in order this interactivity to be feasible in a reduced scatter plot matrix like in this question "Is there a function to make scatterplot matrices in matplotlib?" . I am not an experienced python, matplotlib user, so any kind of help will be appreciated

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



def main():


    #load data from file
    data = np.loadtxt(r"data.txt")

    plt.close("all")
    x = data[:, 3]
    y = data[:, 4]
    y1 = data[:, 5]
    fig1 = plt.figure(1)
    #subplot p1 vs p2
    plt.subplot(121)
    subplot1, = plt.plot(x, y, 'bo', picker=3)
    plt.xlabel('p1')
    plt.ylabel('p2')

    #subplot p1 vs p3
    plt.subplot(122)
    subplot2, = plt.plot(x, y1, 'bo', picker=3)
    plt.xlabel('p1')
    plt.ylabel('p3')

    plt.subplots_adjust(left=0.1, right=0.95, wspace=0.3, hspace=0.45)
    #  art.getp(fig1.patch)
    def onpick(event):

        thisevent = event.artist
        valx = thisevent.get_xdata()
        valy = thisevent.get_ydata()
        ind = event.ind

        print 'index', ind
        print 'selected point:', zip(valx[ind], valy[ind])
        print 'point in the adjacent subplot', x[ind], y1[ind]
        print '(x,y,z):', data[:, 0][ind], data[:, 1][ind], data[:, 2][ind]

        for xcord,ycord in zip(valx[ind], valy[ind]):
            plt.annotate("(x,y,z):", xy = (x[ind], y1[ind]), xycoords = ('data' ),
                xytext=(x[ind] - .5, y1[ind]- .5), textcoords='data',
                arrowprops=dict(arrowstyle="->",
                                connectionstyle="arc3"),
                )
            subplot2, = plt.plot(x[ind], y[ind], 'ro', picker=3)
            subplot1  = plt.plot(x[ind], y[ind], 'ro', picker=3)


    fig1.canvas.mpl_connect('pick_event', onpick)

    plt.show()



main()

Results

In conclusion, information are printed in the terminal, independently of the subplot, when I pick a point. But, the color is modified only in the points of the right subplot, when I pick a point in the left subplot and not vice versa. Moreover, the change of the color is not noticeable until I tweak the figure(e.g. move it or resize it) and when I choose a second point, the previous one remains colored.

Any kind of contribution will be appreciated. Thank you in advance.

Was it helpful?

Solution

You're already on the right track with your current code. You're basically just missing a call to plt.draw() in your onpick function.

However, in our discussion in the comments, mpldatacursor came up, and you asked about an example of doing things that way.

The current HighlightingDataCursor in mpldatacursor is set up around the idea of highlighting an entire Line2D artist, not just a particular index of it. (It's deliberately a bit limited, as there's no good way to draw an arbitrary highlight for any artist in matplotlib, so I kept the highlighting parts small.)

However, you could subclass things similar to this (assumes you're using plot and want the first thing you plot in each axes to be used). I'm also illustrating using point_labels, in case you want to have different labels for each point shown.:

import numpy as np
import matplotlib.pyplot as plt
from mpldatacursor import HighlightingDataCursor, DataCursor

def main():
    fig, axes = plt.subplots(nrows=2, ncols=2)
    for ax, marker in zip(axes.flat, ['o', '^', 's', '*']):
        x, y = np.random.random((2,20))
        ax.plot(x, y, ls='', marker=marker)
    IndexedHighlight(axes.flat, point_labels=[str(i) for i in range(20)])
    plt.show()

class IndexedHighlight(HighlightingDataCursor):
    def __init__(self, axes, **kwargs):
        # Use the first plotted Line2D in each axes
        artists = [ax.lines[0] for ax in axes]

        kwargs['display'] = 'single'
        HighlightingDataCursor.__init__(self, artists, **kwargs)
        self.highlights = [self.create_highlight(artist) for artist in artists]
        plt.setp(self.highlights, visible=False)

    def update(self, event, annotation):
        # Hide all other annotations
        plt.setp(self.highlights, visible=False)

        # Highlight everything with the same index.
        artist, ind = event.artist, event.ind
        for original, highlight in zip(self.artists, self.highlights):
            x, y = original.get_data()
            highlight.set(visible=True, xdata=x[ind], ydata=y[ind])
        DataCursor.update(self, event, annotation)

main()

enter image description here

Again, this assumes you're using plot and not, say, scatter. It is possible to do this with scatter, but you need to change an annoyingly large amount of details. (There's no general way to highlight an arbitrary matplotlib artist, so you have to have a lot of very verbose code to deal with each type of artist individually.)

Hope it's useful, at any rate.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top