2

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.

Community
  • 1
  • 1
user_jt
  • 259
  • 4
  • 15
  • Nice question! First off, you're missing `plt.draw()` in your `onpick` function. Second, you might find this useful if you're open to adding a dependency: https://github.com/joferkington/mpldatacursor/blob/master/examples/multi_highlight_example.py – Joe Kington Mar 12 '14 at 19:11
  • Thank you Joe. This module seems to be very handy! I will add it. – user_jt Mar 12 '14 at 19:28
  • @JoeKington I added the module and it works perfectly. Could you please upgrade your comment as an answer in order to accept it as the appropriate answer to my question? – user_jt Mar 14 '14 at 16:54
  • @JoeKington - I tried to extend the functionality for more subplots and I received errors, regarding the dict command. I want to create a reduced scatter plot matrix with the highlighting functionality. Is this possible with your module? Which class do I have to modify? Thanks in advance – user_jt Mar 15 '14 at 00:08
  • @JoeKington - With the new implementation, although the points can be highlighted and annotated, I can not print the correct event.ind and the x, y, z coordinates from the original data. Do you have any idea why does something like that happen? For the ind it prints always 0 and for the x,y,z it prints always the first values of the array. It looks like that the onpick function must be iterated. Here is the code: http://pastebin.com/nMaXiixj – user_jt Mar 15 '14 at 00:17
  • It's because of the way the plot calls have to be split up with that particular snippet of code. For exactly what what you're doing, it would be best to do things a bit differently. (That's actually why i didn't post it as a full answer initially.) I'll add an example later tonight. Cheers! – Joe Kington Mar 15 '14 at 00:21
  • One more thing that I would like to ask is whether I should use the `scatter` command instead of `plot` for the reduced scatter plot matrix that I want to create? @JoeKington – user_jt Mar 15 '14 at 18:08
  • In the code I pasted earlier the problem is probably the fact that the `Multihighlight` class uses the `points1 & lines1` lists for the highlighting of the points which do not contain information like the arrays `one, two` of the `(x,y,z)` coordinates (`data[:,0], data[:,1], data[:,2]`). These lists (`points1, lines1`) contain series of objects of this shape `` . I am looking forward to your example, it will help me significantly. Thank you @JoeKington – user_jt Mar 15 '14 at 18:10
  • I've added an example. Hopefully it helps! You can use `scatter`, but the code I have below assumes you're using `plot`. If you want to use `scatter` instead, it actually requires changing a suprising amount of details. (That's the main reason why `mpldatacursor` doesn't focus on highlighting very much. It's annoying to implement in a general way.) – Joe Kington Mar 15 '14 at 20:25
  • @JoeKington -I just realized how surprising is the amount of details that I need to modify in order to implement the same functionality with the `scatter` command. I am interested in extending this functionality to drawing a region in a subplot and highlighting the points included in every subplot. Is this possible with the `mpldatacursor` dependency? Any ideas about how I could extend the current functionality to region one?? Thanks in advance. – user_jt Mar 23 '14 at 16:29
  • Sorry I haven't been replying to very much lately. It is possible, and I probably should add the functionality to `mpldatacursor`. I'll try to add an example later today. (No guarantees I'll get to it, though. Lots of work piling up lately!) – Joe Kington Mar 23 '14 at 16:36
  • Thanks Joe. I am looking forward to any suggestions. :) – user_jt Mar 23 '14 at 19:12

1 Answers1

3

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.

Joe Kington
  • 275,208
  • 71
  • 604
  • 463
  • Your example is really helpful and is very close to what I want to implement. I want in each subplot to plot different kind of data from the data that I extract from the file. Could you explain a bit the `for` loop in the `main` function, because I am trying to gain control of each subplot. For example in `subplot(1,2,1)` I want to plot parameter `p1 vs p2` and in `subplot(1,2,2)` `p1 vs p2`. – user_jt Mar 15 '14 at 21:43
  • 1
    Problem solved. I used the `axes.flat[].plot` for each subplot. Thanks for your help. It was very crucial. :) – user_jt Mar 15 '14 at 21:51
  • If the above code is implemented with scatter command instead of `plot`, what kind of changes should be done? I tried to implement it with `scatter` and the errors I get, have to do with `IndexedHighlight(axes.flat)` and `artists = [ax.lines[0] for ax in axes] -> IndexError: list index out of range`. I can not fully understand why this modification causes this kind of errors. Probably it has to do with the way `plot` and `scatter` visualize the data. The kwargs properties fro `scatter` are `Collection` properties and the kwargs properties for `plot` are Line2D. Could you recommend something? – user_jt Mar 22 '14 at 17:14
  • Here you can find a version of the code with the scatter command I am trying to implement: http://pastebin.com/VeRgJU0d . Any kind of suggestion will help me significantly – user_jt Mar 22 '14 at 21:17
  • I managed to combine and partially implement the functionalities I wanted at the beginning. I combined the functionality you posted in this answer for picking a point and highlight it in an adjacent subplot and the matplotlib's `lasso` example. But, I have to fix some last details. The single point picking functionality works successfully. The `lasso` can be drawn in each and every subplot, but the included points are highlighted only when a lasso is drawn in the bottom-right axes. – user_jt Apr 10 '14 at 23:24
  • Furthermore, when I apply the lasso functionality and some points are highlighted, and then I pick a point with the single picking point functionality, the last picked (and highlighted) point and the points that are included in the last drawn lasso are all highlighted, while only the points of the last applied functionality should remain highlighted. A lasso can be drawn by holding shift and pressing the left mouse button, whereas picking a single point can be done by just pressing left mouse button. Do you have any kind of suggestions? I pasted the code here: http://pastebin.com/gc5w9c6S – user_jt Apr 10 '14 at 23:25
  • Moreover, it can be seen in the following image, the problem I am currently facing.Each time a functionality is applied only a single point should be highlighted or only the points that are included in a lasso. Img link: http://imgur.com/inyrIg8 – user_jt Apr 10 '14 at 23:30
  • I would also like to mention that in order to modify the way a lasso is drawn, I modified the Lasso class which is included in the `widgets.py`, which is in the matplotlib folder. `widgets.py` code: http://pastebin.com/ZWqn7DZz – user_jt Apr 13 '14 at 15:58
  • I updated the question, in order to be more clear what I want to achieve. What probably could be a problem in the fact that the included points in Lasso are highlighted, when a Lasso is drawn in the bottom-right axes? Also, how could I keep highlighted points only from one of the two functionalities? – user_jt Apr 14 '14 at 00:45