Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

matplotlib values under cursor [duplicate]

Tags:

matplotlib

I'm using matplotlib.imshow to get an interactive display of a 2D array. The x/y coordinate under the cursor is displayed at the bottom left of the window. Is it possible to also get the value of the array under the cursor as well?

like image 200
ajwood Avatar asked Feb 07 '13 15:02

ajwood


2 Answers

You simply need to re-assign ax.format_coord. See this example from the documentation.

(code lifted directly from example)

"""
Show how to modify the coordinate formatter to report the image "z"
value of the nearest pixel given x and y
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

X = 10*np.random.rand(5,3)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(X, cmap=cm.jet, interpolation='nearest')

numrows, numcols = X.shape
def format_coord(x, y):
    col = int(x+0.5)
    row = int(y+0.5)
    if col>=0 and col<numcols and row>=0 and row<numrows:
        z = X[row,col]
        return 'x=%1.4f, y=%1.4f, z=%1.4f'%(x, y, z)
    else:
        return 'x=%1.4f, y=%1.4f'%(x, y)

ax.format_coord = format_coord
plt.show()
like image 91
tacaswell Avatar answered Oct 18 '22 14:10

tacaswell


I needed something I could re-use, so I encapsulated the solution through a class:

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import numpy as np
class imshow_show_z:

    def __init__(self, ax, z, x, y):
        self.ax = ax
        self.x  = x
        self.y  = y
        self.z  = z
        self.dx = self.x[1] - self.x[0]
        self.dy = self.y[1] - self.y[0]
        self.numrows, self.numcols = self.z.shape
        self.ax.format_coord = self.format_coord
    def format_coord(self, x, y):
        col = int(x/self.dx+0.5)
        row = int(y/self.dy+0.5)
        #print "Nx, Nf = ", len(self.x), len(self.y), "    x, y =", x, y, "    dx, dy =", self.dx, self.dy, "    col, row =", col, row
        xyz_str = ''
        if ((col>=0) and (col<self.numcols) and (row>=0) and (row<self.numrows)):
            zij = self.z[row,col]
            #print "zij =", zij, '  |zij| =', abs(zij)
            if (np.iscomplex(zij)):
                amp = abs(zij)
                phs = np.angle(zij) / np.pi
                if (zij.imag >= 0.0):
                    signz = '+'
                else:
                    signz = '-'
                xyz_str = 'x=' + str('%.4g' % x) + ', y=' + str('%.4g' % y) + ',' \
                            + ' z=(' + str('%.4g' % zij.real) + signz + str('%.4g' % abs(zij.imag)) + 'j)' \
                            + '=' + str('%.4g' % amp) + r'*exp{' + str('%.4g' % phs) + u' π j})'
            else:
                xyz_str = 'x=' + str('%.4g' % x) + ', y=' + str('%.4g' % y) + ', z=' + str('%.4g' % zij)
        else:
            xyz_str = 'x=%1.4f, y=%1.4f'%(x, y)
        return xyz_str

def new_imshow(ax, x, y, z, *args, **kwargs):
    assert(len(x) == z.shape[1])
    assert(len(y) == z.shape[0])
    dx = x[1] - x[0]
    dy = y[1] - y[0]
    if (np.iscomplex(z).any()):
        zabs = abs(z)
    else:
        zabs = z
    # Use this to center pixel around (x,y) values
    extent = (x[0]-dx/2.0, x[-1]+dx/2.0, y[0]-dy/2.0, y[-1]+dy/2.0)
    # Use this to let (x,y) be the lower-left pixel location (upper-left when origin = 'lower' is not used)
    #extent = (x[0]-dx/2.0, x[-1]+dx/2.0, y[0]-dy/2.0, y[-1]+dy/2.0)
    im = ax.imshow(zabs, extent = extent, *args, **kwargs)
    imshow_show_z(ax, z, x, y)
    ax.set_xlim((x[0], x[-1]))
    ax.set_ylim((y[0], y[-1]))
    return im

Example usage:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-5, 10, 100)
y = np.linspace(-2.0, 5, 51)
xx, yy = np.meshgrid(x, y)
Z = np.sin(xx**2 + yy**2) / (xx**2 + yy**2)

fig = plt.figure()
ax = fig.add_subplot(1,1,1)
im = new_imshow(ax, x, y, Z, aspect = 'auto', origin = 'lower', interpolation = 'nearest')
ax.set_xlabel('x')
ax.set_ylabel('y')

plt.show()

Features:

  • Can show both floats and complex values. For complex, the real+imaginary parts and polar form are shown.
  • Will set the extent for you, based on the x and y arrays. Note that the matplotlib example works only if you don't use the extent keyword.
  • Pixels are centered around their (x,y) position instead of (x,y) being the lower (or upper) left location.
like image 42
big_gie Avatar answered Oct 18 '22 16:10

big_gie