New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tooltips for images (eg matshow) #81

Open
eldad-a opened this Issue Feb 24, 2014 · 14 comments

Comments

Projects
None yet
4 participants
@eldad-a

eldad-a commented Feb 24, 2014

This package it fantastic!
One of the only things I was missing upon the transition from (cli) ipython to the notebook interface was interacting with plots.
This not only regain it but has huge potential to extend it!
Thanx!

I was trying to find a way to get the information of an image using mouse hovering, a la D3 Scatter Plot (with tooltips!) in D3 Plugins: Truly Interactive Matplotlib In Your Browser (for example [x, y, pxl value] ).
I did not figure out how this should be done.
Previously I could use variations over image_zcoord.py.
Using the notebook interface and mpld3 the best I came up with so far is the following ugly trick: add an invisible scatter plot, with one marker per entry in the array, and create a plugin based on this scatter.

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import mpld3
mpld3.enable_notebook()

import numpy as np

np.random.seed(0)
A = np.random.rand(25, 25)
fig = plt.figure(figsize=(9,6))
ax = fig.gca()
ax.matshow(A)
## get the indices of the array to "tile" using scatter
y, x = np.mgrid[:A.shape[0], :A.shape[1]]
x,y = x.ravel(),y.ravel()
## pseudo-transparent scatter
scatter = ax.scatter(x, y, c=A, s=40, marker='s', edgecolor='none')
## create the plugin
labels = ['({},{},{})'.format(i,j,a) for i,j,a in zip(y, x, A.ravel())]
fig.plugins = [plugins.PointLabelTooltip(scatter, labels)]

There's probably a cleaner way to get this done. What am I missing?
Besides being an ugly hack, this solution is missing several desired features -

  • The markers do not extend over the entire "effective pixel" but only over the marker (which size does not vary as we zoom-in/out)
  • It would be nice to place the label at a fixed position outside the array (say at the bottom-left corner)
  • It would be nice to change the colour of the label, or put in a text box, such that it is readable while hovering.

Looking forward for hints as well as for progress of the mpld3 package!

@jakevdp

This comment has been minimized.

Show comment
Hide comment
@jakevdp

jakevdp Feb 24, 2014

Collaborator

Hi - thanks for the feedback, and I'm glad you're finding this helpful!
Right now, I'm not sure there's a better way to do this. The problem is that currently, image data is not saved as data, but as a base64 png encoding of the matplotlib output. In order to recover the image data within the javascript, we'd need to handle the image rendering within the javascript (not a trivial process for arbitrary colormaps!). So, for right now, your hack is a pretty good solution!

Regarding changing the appearance of a tooltip, you might take a look at this example: http://mpld3.github.io/examples/html_tooltips.html

Collaborator

jakevdp commented Feb 24, 2014

Hi - thanks for the feedback, and I'm glad you're finding this helpful!
Right now, I'm not sure there's a better way to do this. The problem is that currently, image data is not saved as data, but as a base64 png encoding of the matplotlib output. In order to recover the image data within the javascript, we'd need to handle the image rendering within the javascript (not a trivial process for arbitrary colormaps!). So, for right now, your hack is a pretty good solution!

Regarding changing the appearance of a tooltip, you might take a look at this example: http://mpld3.github.io/examples/html_tooltips.html

@eldad-a

This comment has been minimized.

Show comment
Hide comment
@eldad-a

eldad-a Feb 25, 2014

Here's an improved version, based on pcolormesh, which returns a collection. This in turn can be passed to the plugin. The ndenumerate function gives a nice iterable for the labels.

np.random.seed(0)
A = np.random.rand(25, 25)
fig = plt.figure(figsize=(9,6))
ax = fig.gca()
ax.set_aspect('equal', 'box')
mesh = ax.pcolormesh(A)
#ax.autoscale_view()
ax.invert_yaxis()
labels = ['({},{},{})'.format(i,j,a) for (i,j),a in np.ndenumerate(A)]
fig.plugins = [plugins.PointLabelTooltip(mesh, labels)]

eldad-a commented Feb 25, 2014

Here's an improved version, based on pcolormesh, which returns a collection. This in turn can be passed to the plugin. The ndenumerate function gives a nice iterable for the labels.

np.random.seed(0)
A = np.random.rand(25, 25)
fig = plt.figure(figsize=(9,6))
ax = fig.gca()
ax.set_aspect('equal', 'box')
mesh = ax.pcolormesh(A)
#ax.autoscale_view()
ax.invert_yaxis()
labels = ['({},{},{})'.format(i,j,a) for (i,j),a in np.ndenumerate(A)]
fig.plugins = [plugins.PointLabelTooltip(mesh, labels)]

@jakevdp jakevdp added the question label Mar 7, 2014

@aflaxman

This comment has been minimized.

Show comment
Hide comment
@aflaxman

aflaxman Mar 17, 2014

Collaborator

This is great, I think we should make an example out of it! Two thoughts: the first approach would work better if we implement a "Vornoi mouseover" plugin, a la http://bl.ocks.org/mbostock/8033015 ; for a data exploration situation I often find myself in, it would be excellent to have the mouseover information appear in a panel in a fixed location, e.g. to the right of the heatmap.

Here is a start towards that example, which uses the PointHTMLTooltip plugin.
http://nbviewer.ipython.org/gist/aflaxman/6f7b9a1feba884ff65cb

Collaborator

aflaxman commented Mar 17, 2014

This is great, I think we should make an example out of it! Two thoughts: the first approach would work better if we implement a "Vornoi mouseover" plugin, a la http://bl.ocks.org/mbostock/8033015 ; for a data exploration situation I often find myself in, it would be excellent to have the mouseover information appear in a panel in a fixed location, e.g. to the right of the heatmap.

Here is a start towards that example, which uses the PointHTMLTooltip plugin.
http://nbviewer.ipython.org/gist/aflaxman/6f7b9a1feba884ff65cb

@jakevdp jakevdp modified the milestone: 0.2 Apr 17, 2014

@quaquel

This comment has been minimized.

Show comment
Hide comment
@quaquel

quaquel May 8, 2014

Contributor

Inspired by the above remark, I made a voronoi mesh version of the random walk example. This voronoi version has better highlighting behavior then the current example. Comments or suggestions for improvement are more than welcome.

The example can be found at http://nbviewer.ipython.org/gist/quaquel/dbcfc706fa3752b32d24

Contributor

quaquel commented May 8, 2014

Inspired by the above remark, I made a voronoi mesh version of the random walk example. This voronoi version has better highlighting behavior then the current example. Comments or suggestions for improvement are more than welcome.

The example can be found at http://nbviewer.ipython.org/gist/quaquel/dbcfc706fa3752b32d24

@jakevdp

This comment has been minimized.

Show comment
Hide comment
@jakevdp

jakevdp May 8, 2014

Collaborator

Very cool!

Collaborator

jakevdp commented May 8, 2014

Very cool!

@aflaxman

This comment has been minimized.

Show comment
Hide comment
@aflaxman

aflaxman May 9, 2014

Collaborator

Agree! I think a plot with less points will make a better demo, at least on my pitiful laptop: http://nbviewer.ipython.org/gist/aflaxman/31cd0a65ef5627585fb8

In my stripped down example, I can see that the Vornoi cells are functioning a little bit differently than I expected, so maybe this isn't the perfect demo, either.

Collaborator

aflaxman commented May 9, 2014

Agree! I think a plot with less points will make a better demo, at least on my pitiful laptop: http://nbviewer.ipython.org/gist/aflaxman/31cd0a65ef5627585fb8

In my stripped down example, I can see that the Vornoi cells are functioning a little bit differently than I expected, so maybe this isn't the perfect demo, either.

@quaquel

This comment has been minimized.

Show comment
Hide comment
@quaquel

quaquel May 9, 2014

Contributor

I agree, the mesh looks different from what I am expecting as well. Perhaps there is something wrong in how I extract the points on which to base the voronoi mesh from the lines. I will try and take at it in detail look over the weekend.

Contributor

quaquel commented May 9, 2014

I agree, the mesh looks different from what I am expecting as well. Perhaps there is something wrong in how I extract the points on which to base the voronoi mesh from the lines. I will try and take at it in detail look over the weekend.

@quaquel

This comment has been minimized.

Show comment
Hide comment
@quaquel

quaquel May 9, 2014

Contributor

I fixed the voronoi mesh: http://nbviewer.ipython.org/gist/quaquel/46985e912f103a2ceffd

I made a small error in the looping over the data and therefore left out the first point of each line. Is this the behavior you were expecting?

Contributor

quaquel commented May 9, 2014

I fixed the voronoi mesh: http://nbviewer.ipython.org/gist/quaquel/46985e912f103a2ceffd

I made a small error in the looping over the data and therefore left out the first point of each line. Is this the behavior you were expecting?

@aflaxman

This comment has been minimized.

Show comment
Hide comment
@aflaxman

aflaxman May 9, 2014

Collaborator

Ah, nice catch. This addresses the issue I was seeing before, but there is still some funny stuff. It is probably because I have come up with a pathological example, but sometimes hovering over the middle of a line segment highlights a different line.

Collaborator

aflaxman commented May 9, 2014

Ah, nice catch. This addresses the issue I was seeing before, but there is still some funny stuff. It is probably because I have come up with a pathological example, but sometimes hovering over the middle of a line segment highlights a different line.

@quaquel

This comment has been minimized.

Show comment
Hide comment
@quaquel

quaquel May 9, 2014

Contributor

True. The approach used here is that for each line, I retrieve the x,y coordinates. Next, I make a voronoi mesh based on these coordinates. The resulting patches are used to attach a mouseover to. In your example you specify each line by only two points, resulting in the sometimes weird behavior. I hope this clarifies what's going on.

Contributor

quaquel commented May 9, 2014

True. The approach used here is that for each line, I retrieve the x,y coordinates. Next, I make a voronoi mesh based on these coordinates. The resulting patches are used to attach a mouseover to. In your example you specify each line by only two points, resulting in the sometimes weird behavior. I hope this clarifies what's going on.

@aflaxman

This comment has been minimized.

Show comment
Hide comment
@aflaxman

aflaxman May 9, 2014

Collaborator

Definitely clarifies, and probably the solution is to include more points in the line on the matplotlib side. That makes me wonder: does this play nice with the zoom plugin? (I'd check myself, but my computer is not behaving...)

Collaborator

aflaxman commented May 9, 2014

Definitely clarifies, and probably the solution is to include more points in the line on the matplotlib side. That makes me wonder: does this play nice with the zoom plugin? (I'd check myself, but my computer is not behaving...)

@quaquel

This comment has been minimized.

Show comment
Hide comment
@quaquel

quaquel May 10, 2014

Contributor

Yes, the more points are being used to specify a line, the better the behavior will be.

With respect to zooming, at the moment it does not play nice with zooming. I deliberately disabled the default plugins. Any pointers on how to make it zoomable would be very welcome. It was already on my list of planned improvements.

Another issue is that the current implementation requires the user to specify the lines that should be used for the voronoi mesh. I implicitly assume that these exist in a single axes. It might be better that the user specifies the axes instead. Axes.elements contains the mpld3 elements plotted on that axes, so I could use that to infer the points for the voronoi mesh.

Contributor

quaquel commented May 10, 2014

Yes, the more points are being used to specify a line, the better the behavior will be.

With respect to zooming, at the moment it does not play nice with zooming. I deliberately disabled the default plugins. Any pointers on how to make it zoomable would be very welcome. It was already on my list of planned improvements.

Another issue is that the current implementation requires the user to specify the lines that should be used for the voronoi mesh. I implicitly assume that these exist in a single axes. It might be better that the user specifies the axes instead. Axes.elements contains the mpld3 elements plotted on that axes, so I could use that to infer the points for the voronoi mesh.

@jakevdp

This comment has been minimized.

Show comment
Hide comment
@jakevdp

jakevdp May 12, 2014

Collaborator

Regarding zooming... the voronoi object would need to have a callback in the zoomed function of the axes (see https://github.com/jakevdp/mpld3/blob/master/src/core/axes.js#L266). The best way to do that would be to make the Voronoi object and actual mpld3.Element, with its own draw and zoomed functions, and add it to the list of elements stored in the axes.

Collaborator

jakevdp commented May 12, 2014

Regarding zooming... the voronoi object would need to have a callback in the zoomed function of the axes (see https://github.com/jakevdp/mpld3/blob/master/src/core/axes.js#L266). The best way to do that would be to make the Voronoi object and actual mpld3.Element, with its own draw and zoomed functions, and add it to the list of elements stored in the axes.

@quaquel

This comment has been minimized.

Show comment
Hide comment
@quaquel

quaquel May 12, 2014

Contributor

Thanks for the clarification. I should be able to get that to work.

Contributor

quaquel commented May 12, 2014

Thanks for the clarification. I should be able to get that to work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment