pyyeti.datacursor.DataCursor

class pyyeti.datacursor.DataCursor(ax=None, figs=None, hover=True, mk_label=<function mk_label>, offsets=(-20, 20), bbox={'alpha': 0.5, 'boxstyle': 'round, pad=0.5', 'fc': 'gray'}, arrowprops={'arrowstyle': '->', 'connectionstyle': 'arc3, rad=0'}, followdot={'alpha': 0.7, 'color': 'green', 's': 130}, permdot={'alpha': 0.4, 'color': 'red', 's': 130})[source]

Class to show x, y data points and to allow selection of points for annotations.

hover

If True, an annotated large green, semi-transparent dot is displayed that follows the mouse as long as the mouse is inside the axes. Note: setting this directly is possible; you just have to turn the DataCursor off and back on for the setting to take effect. For example:

from pyyeti.datacursor import DC
DC.hover = False
DC.off()
DC.on()
Type:

bool

mk_label

Function that returns an annotation label for a data point. Must accept a single SimpleNamespace argument that contains information about the selected data point. mk_label defaults to:

def mk_label(point):

    def tostr(xyz):
        if isinstance(xyz, numbers.Number):
            return f"{xyz:.5g}"
        return f"{xyz}"

    if point.ax.name == "polar":
        deg = np.rad2deg(point.x % (2 * np.pi))
        label = (
            f"θ: {tostr(point.x)} ({tostr(deg)}°)\n"
            f"rad: {tostr(point.y)}"
        )
    else:
        label = f"x: {tostr(point.x)}\ny: {tostr(point.y)}"
        if point.z is not None:
            label += f"\nz: {tostr(point.z)}"

    if point.nlines > 1:
        label += f"\n{point.handle.get_label()}"

    return label

The SimpleNamespace point contains the following attributes:

attr

Description

ax

Axes handle

handle

Line2D or PathCollection handle for line

index

Index into the data vectors for data point

lines

Total number of lines on axes

n

Line number starting at 0

x,y,z

x, y, z coordinates of data point; z is None for 2D

dot

The matplotlib.pyplot.scatter() (PathCollection) object handle. Note that DataCursor ignores these added annotation points when moused over; they are identified by the added attribute “_pyyeti_dc_point”.

note

The annotation object handle from matplotlib.pyplot.annotate().

xy_note

The (x, y) coordinates for the note.

Type:

function

offsets

Two element tuple containing x and y offsets in points for the annotation.

Type:

tuple

bbox

Defines the bbox parameter for matplotlib.axes.Axes.annotate()

Type:

dict; optional

arrowprops

Defines the arrowprops parameter for matplotlib.axes.Axes.annotate()

Type:

dict; optional

followdot

Typically defines the s, color, and alpha settings (and possibly others as desired) for matplotlib.axes.Axes.scatter(). That function is used for drawing the “dot” on the plot that follows the mouse and highlights the currently selected data point.

Type:

dict; optional

permdot

Similar to followdot except this is a “permanent” dot; this gets placed after left clicking.

Type:

dict; optional

points

Contains list of SimpleNamespace objects. Each “point” is as described above under the mk_label attribute.

Type:

list

xyz

If points have been selected and the datacursor has been turned off, the xyz attribute will be filled with the x, y, z data for the selected points for all axes. If there are no 3d plots, only the x and y columns will be present. The order of the rows follows the selection order (same order as points; the source of this data).

Type:

2d ndarray or None

Notes

Having multiple data-cursors active at the same time is undesirable. Therefore, this module instantiates one DataCursor object called DC during the initial import. It is recommended to always use DC rather than instantiating new DataCursor objects. From within a script:

from datacursor import DC
...
DC.getdata()   # blocks until DataCursor is turned off via 't'
DC.getdata(n)  # blocks until user selects `n` points (or
               # DataCursor is turned off via 't')

From an interactive prompt:

from datacursor import DC
DC.on()       # doesn't block, but DC is active

Once the DataCursor is turned on, you’ll just mouse over the data to see selected data points. These operations are available (when the mouse is inside the axes):

Action

Description

left-click

Data point will be stored in the member list points.

right-click

Last point is deleted from the plot and from points.

typing ‘t’

Turns off DataCursor. To turn on, use DC.on. Note that turning on will reset points.

typing ‘D’

Deletes last point AND removes the line from the plot via line_handle.remove(). Any older annotations are not deleted.

To get data points from plots from within a script, use DataCursor.getdata(). Enter the number of points or press ‘t’ to end blocking so the script will continue (see DataCursor.getdata()).

Once the DataCursor is turned off, the annotations become draggable. Note that, at least for some versions of Matplotlib, annotations sometimes become linked (moving one will move another). When that happens, try dragging a different annotation; this sometimes breaks the link.

Interactively, the member functions DataCursor.on() and DataCursor.off() are used to turn the DataCursor on and off. These functions will update the internal state of the DataCursor to account for deleted or added items. DataCursor.getdata() calls DataCursor.on() internally.

The following example plots some random data, calls DataCursor.getdata() to wait for the user to optionally select data points and then turn the DataCursor off (with keystroke ‘t’). It then prints the selected points:

import matplotlib.pyplot as plt
import numpy as np
from pyyeti.datacursor import DC
rng = np.random.default_rng()
x = np.arange(500)/250
y = rng.uniform(size=x.shape)
fig = plt.figure('demo')
fig.clf()
ax = fig.add_subplot(1, 1, 1)
ax.plot(x, y)
DC.getdata()
# use DC.pause if you want to drag boxes around before
# continuing:
DC.pause()
print('x, y values of selected points are:')
print(np.array([[p.x, p.y] for p in DC.points]))

Settings can be changed after instantiation. Here is an example of defining a new format for the annotation. Only the line label is included in the annotation. The example also changes the permanent dot to a gray pentagon:

import matplotlib.pyplot as plt
import numpy as np
from pyyeti.datacursor import DC

def new_label(point):
    return (f'{point.handle.get_label()}\n'
            f'({point.x},{point.y:.2f})')

DC.mk_label = new_label
DC.permdot = dict(s=130, color='black', alpha=0.4,
                  marker='p')

rng = np.random.default_rng()
plt.plot(rng.normal(size=50), label='Gaussian')
plt.plot(rng.uniform(size=50), label='Uniform')
DC.on()

For increased versatility, there are two optional functions the user can define that will be called when a point is added (left-click) and when a point is deleted (right-click). See DataCursor.addpt_func() and DataCursor.delpt_func() for more information on the call signatures. Here is a simple example that just prints statements to the screen:

import matplotlib.pyplot as plt
import numpy as np
from pyyeti.datacursor import DC

def addpt(point):
    print(f'You selected ({point.x}, {point.y}, {point.z})')

def delpt(point):
    print(f'You deleted ({point.x}, {point.y}, {point.z})')

DC.addpt_func(addpt)
DC.delpt_func(delpt)

fig = plt.figure()
ax = fig.add_subplot(projection="3d")
rng = np.random.default_rng()
coords = rng.normal(size=(6, 3))
dots = ax.scatter(*coords.T)
ax.plot(*(coords.T + 0.1), "v")
DC.on()
__init__(ax=None, figs=None, hover=True, mk_label=<function mk_label>, offsets=(-20, 20), bbox={'alpha': 0.5, 'boxstyle': 'round, pad=0.5', 'fc': 'gray'}, arrowprops={'arrowstyle': '->', 'connectionstyle': 'arc3, rad=0'}, followdot={'alpha': 0.7, 'color': 'green', 's': 130}, permdot={'alpha': 0.4, 'color': 'red', 's': 130})[source]

Initialize the DataCursor.

Parameters:
  • ax (axes object(s) or None; optional) – Axes object or list of axes objects as created by matplotlib.pyplot.subplot() (for example). If None, all axes on all selected figures will be automatically included. Takes precedence over the figs input.

  • figs (figure object(s) or None; optional) – Alternative to the ax input. If ax is not input, figs specifies a figure object or list of figure objects as created by matplotlib.pyplot.figure(). If None, all applicable figures will be automatically included.

  • hover (bool; optional) – Sets the hover attribute.

  • form1 (function; optional) – Sets the form1 attribute.

  • form2 (function; optional) – Sets the form2 attribute.

  • offsets (tuple; optional) – Sets the offsets attribute.

  • bbox (dict; optional) – Sets the bbox attribute.

  • arrowprops (dict; optional) – Sets the arrowprops attribute.

  • followdot (dict; optional) – Sets the followdot attribute.

  • permdot (dict; optional) – Sets the permdot attribute.

Methods

__init__([ax, figs, hover, mk_label, ...])

Initialize the DataCursor.

addpt_func(func)

Function to call on a left-click.

delpt_func(func)

Function to call on a right-click.

getdata([maxpoints, msg])

Suspend python while user selects points up to maxpoints.

off([stop_blocking])

Turns off the DataCursor and optionally stops it from blocking

on([ax, figs, callbacks, reset])

Turns on and (re-)initializes the DataCursor for current figures.

pause([msg])

Suspend python so user can interact with plots (such as moving previously added annotations) before continuing.