3. Improved interactive plotting with wanglib.pylab_extensions

This module contains some useful extensions to the pylab interface.

3.1. Plotting while acquiring data

To plot while acquiring data, you will need to implement your data gathering using Python generators. A generator is like a Python function that, rather than returning data all at once (with the return statement), returns it point by point (with the yield statement).

Suppose we have a spex spectrometer object, and a lockin object that we are using to detect the signal at the output slit of the spectrometer. Here is an example of a generator we might use to scan a spectrum, while yielding values along the way:

def scan_wls(wls):
    for wl in wls:
        spex.set_wl(wl)
        sleep(0.1)
        val = lockin.get_x()
        yield wl, val

Note

This pattern is so common that a shorthand is provided for it in wanglib.util.scanner().

Then, if we wanted to scan from 800nm to 810nm, we would do

scan = scan_wls(numpy.arange(800, 810, 0.1))
for x,y in scan:
    print x,y

This will print the data to STDOUT, but we could also:

The plotgen function reads from your generator, plotting data as it goes along. In the case of our scan_wls example above, we can view the spectrum as it gets collected:

wls = arange(800, 810, 0.1))
plotgen(scan_wls(wls))
# ... measures data, plotting as it goes along ...

After your generator yields its last value, it will return the complete array of measured X and Y values. Sometimes we want to do extra things with that:

_,ref = plotgen(scan_wls(wls))   # measure reference spectrum and plot it
_,trn = plotgen(scan_wls(wls))   # measure transmission spectrum and plot it
plot(wls, log(ref/trn), 'k--')   # plot absorption spectrum

Additionally, plotgen can generate multiple lines. Say we wanted to plot both the X and the Y quadrature from our lockin. We’d write our generator like:

def scan_wls(wls):
    for wl in wls:
        spex.set_wl(wl)
        sleep(0.1)
        x = lockin.get_x()
        y = lockin.get_y()
        yield wl, x, wl, y

This is the same as before, but we’re yielding two X,Y pairs: one with the X quadrature (wl, x) and one with the Y quadrature (wl, y). plotgen recognizes this and plots two lines:

_,x,_,y = plotgen(scan_wls(wls))

By default, plotgen plots these on the same axes, but sometimes that doesn’t make sense. For example, if we’re measuring the signal magnitude (in Volts) and phase (in degrees), then those two units have nothing to do with each other, and we should plot them on separate axes.

def scan_wls(wls):
    for wl in wls:
        spex.set_wl(wl)
        sleep(0.1)
        r = lockin.get_r()
        p = lockin.get_phase()
        yield wl, r, wl, p

# create two axes and pass them to plotgen explicitly
ax1 = pylab.subplot(211)
ax2 = pylab.subplot(212)
plotgen(scan_wls(wls), ax=(ax1,ax2))

Finally, you can limit the length of the plotted lines using the maxlen parameter. This is useful for generators which yield infinitely - such as monitoring the last five minutes of a signal.

def monitor_signal():
    start = time()
    while True:
        yield time() - start, lockin.get_phase()
        sleep(0.1)

Note

This pattern is so common that a shorthand is provided for it in wanglib.util.monitor().

This will yield about 10 points per second forever. To cut it short after about 5 minutes of data, try this:

plotgen(monitor_signal(), maxlen=10*60*5)

Full documentation for plotgen:

wanglib.pylab_extensions.live_plot.plotgen(gen, ax=None, maxlen=None, **kwargs)

Take X,Y data from a generator, and plot it at the same time.

Parameters:
  • gen – a generator object yielding X,Y pairs.
  • ax – an axes object (optional).
  • maxlen – maximum number of points to retain (optional).
Returns:

an array of the measured X and Y values.

Any extra keyword arguments are passed to the plot function.

gen can yield any even number of values, and these are interpreted as a set of X,Y pairs. That is, if the provided generator yields 4 values each time, plotgen will plot two lines - with the first line updated from the [0:2] slice and the second line updated from the [2:4] slice.

ax is the axes object into which the line(s) are plotted. By default, the current axes. ax can also be a tuple of axes objects, as long as the tuple is the same length as the number of lines being plotted. Each line is then plotted into the corresponding axes.

maxlen, when provided, limits the buffer size. When the plotted lines each grow to this number of points, the oldest data points will start being trimmed off the line’s trailing end.

3.2. Saving/clearing traces

wanglib.pylab_extensions.misc.apply_mask(line, mask)

mask x and y (to remove bad points, etc).

wanglib.pylab_extensions.misc.apply_offset(line, offset)

move the line up or down

wanglib.pylab_extensions.misc.apply_reference(line, ref)

apply reference data (for absorption spectra)

wanglib.pylab_extensions.misc.bll(index=-1, lag=0.3)

blink the last line, identifying it

wanglib.pylab_extensions.misc.cll(index=-1)

clear last line. removes the last line from the figure.

To remove a different line, specify the index.

wanglib.pylab_extensions.misc.dualtick(func)

Decorator for dual-tick functions.

A dual-tick function is a function that, when called on an axis, adds a second set of tick to it in different units. This decorator creates such functions when applied to the unit conversion function.

For example:

>>> @dualtick
>>> def eV(wl):
>>>     return 1240. / wl

Now, when working with plots of spectral data in units of nm, calling

>>> eV()

will add a second axis along the top in units of eV. To explicitly apply to some other axis ax, use eV(ax). Returns a reference to the twiny axis that was made.

Another example, for a delay stage:

>>> @dualtick
>>> def ns(pos):
>>>     c = 300.  # mm / ns
>>>     zd = 521. # mm
>>>     return 2 * (zd - pos) / c

When plotting delay stage data enumerated in mm, this function will add an axis in ps delay from the zero-delay point at 521mm.

wanglib.pylab_extensions.misc.gll(index=-1, blink=True)

Get last line.

Retrieves x,y data of the last line and returns them as a tuple of two numpy arrays.

To get a different line, specify the index.

wanglib.pylab_extensions.misc.relim(line)

redraw the line’s figure to include the line.

wanglib.pylab_extensions.misc.sll(fname, index=-1, blink=True)

Save last line.

Saves x,y data of the last line, in .npy format. Specify the file name.

To save a different line, specify the index.

3.3. Density plots

wanglib.pylab_extensions.density.density_plot(two_dimensional, horiz, vert, ax=None, **kwargs)

Display a 2D density plot - like imshow, but with axes labels corresponding to the two axes provided.

This will only be accurate for regularly-spaced x and y axes (e.g., as generated by arange or linspace).

Parameters:
  • two_dimensional – data to plot. shape: (M, N)
  • horiz – x-axis. should have length N.
  • vert – y-axis. should have length M.
  • ax – axis upon which to plot.

Keyword arguments are passed to imshow.