Artists
The artists module contains a variety of data visualization tools.
Quick artists
To facilitate rapid and easy visualization of data, WrightTools
offers
“quick” artist functions which quickly generate 1D or 2D
representations.
These functions are made to make good representations by default, but
they do have certain keyword arguments to make popular customization
easy.
These are particular useful functions within the context of
auto-generated plots in acquisition software.
WrightTools.artists.quick1D()
is a function that generates 1D representations.
import WrightTools as wt
from WrightTools import datasets
import matplotlib.pyplot as plt
wt.artists.apply_rcparams('default')
# import data
p = datasets.wt5.v1p0p0_perovskite_TA # axes w1=wm, w2, d2
data = wt.open(p)
data.transform("w1", "w2", "d2")
# probe freqency trace
wt.artists.quick1D(data, axis=0, at={"w2": [1.7, "eV"], "d2": [0, "fs"]})
# delay trace
wt.artists.quick1D(data, axis="d2", at={"w2": [1.7, "eV"], "w1": [1.65, "eV"]})
plt.show()
WrightTools.artists.quick2D()
is a function that generates 2D representations.
import WrightTools as wt
from WrightTools import datasets
import matplotlib.pyplot as plt
wt.artists.apply_rcparams('default')
# import data
p = datasets.wt5.v1p0p0_perovskite_TA # axes w1=wm, w2, d2
data = wt.open(p)
data.transform("w1", "w2", "d2")
# probe wigner
wt.artists.quick2D(data, xaxis=0, yaxis=2, at={"w2": [1.7, "eV"]})
# 2D-frequency
wt.artists.quick2D(data, xaxis="w1", yaxis="w2", at={"d2": [0, "fs"]})
plt.show()
Note that the actual quick functions are each one-liners. Keyword
arguments such as autosave
and save_directory
may be supplied if
the user desires to save images (not typical for users in interactive
mode). The channel
kwarg allows users to specify what channel they
would like to plot.
Perhaps the most powerful feature of WrightTools.artists.quick1D()
and WrightTools.artists.quick2D()
are
their ability to treat higher-dimensional datasets by automatically
generating multiple figures. When handing a dataset of higher
dimensionality to these artists, the user may choose which axes will
be plotted against using keyword arguments.
Any axis not plotted against will be iterated over such that an image
will be generated at each coordinate in that axis. Users may also
provide a dictionary with entries of the form
{axis_name: [position, units]}
to choose a specific coordinates
along non-plotted axes. Positions along non-plotted axes are reported
in the title of each plot and overlines are shown when applicable.
These functionalities are derived from WrightTools.data.Data.chop()
.
Interactive artists
WrightTools.artists.interact2D()
allows users to easily vizualize 2D slices of arbitrarily
high dimension data.
import WrightTools as wt
from WrightTools import datasets
import matplotlib.pyplot as plt
# import data
p = datasets.wt5.v1p0p0_perovskite_TA # axes w1=wm, w2, d2
data = wt.open(p)
interact = wt.artists.interact2D(data, xaxis=0, yaxis=2, local=True, verbose=False)
# show-off functionality. The following lines are not needed when in an interactive mode.
interact[1]['w2'].set_val(40) # hack w2 slider
fig = plt.gcf()
# simulate mouse event to get crosshairs
fig.canvas.button_release_event(160, 375, 1)
plt.show()
Side plots show x and y projections of the slice (shaded gray). For signed channels, side plots will also show projections of the negatively signed components and positively signed components. Left clicks on the main axes draw 1D slices on side plots at the coordinates selected. Right clicks remove the 1D slices. For 3+ dimensional data, sliders below the main axes are used to change which slice is viewed. interact2D also supports keyboard navigation
key |
action |
tab / ctrl+tab |
cycle focus between the sliders and the plot |
left/right arrow |
decrement/increment slice (slider focus) or change y slice (plot focus) |
up/down arrow |
change x slice (plot focus) |
Note that the left/right arrow navigation overrides the built-in undo/redo action of the qt viewer. Users can still undo/redo with the ‘c/v’ key presses, or through the GUI toolbar above the figure.
Colors
Two-dimensional data is often represented using “heatmaps”.
Your choice of colormap is a crucial part of how your data is perceived.
WrightTools
has a few choice colormaps built-in.
(Source code
, png
, pdf
)
All of these are held in the colormaps dictionary.
>>> wt.artists.colormaps['default']
<matplotlib.colors.LinearSegmentedColormap at 0x7f6d8b658d30>
Throughout WrightTools you can refer to colormaps by their name. By default, WrightTools will use the “default” colormap when plotting unsigned channels and the “signed” colormap when plotting signed channels.
There are many great resources on how to choose the best colormap. Choosing Colormaps is a great place to start reading. WrightTools tries to use perceptual colormaps wherever possible. When a large dynamic range is needed, the data can always be scaled to accommodate.
The default colormap is based on the wonderful cubehelix color scheme. [1] The cubehelix parameters have been fine-tuned to roughly mimic the colors of the historically popular “jet” colormap.
The isoluminant series are instances of the color scheme proposed by Kindlmann et al. [2]
The skyebar series were designed by Schuyler (Skye) Kain for use in his instrumental software package COLORS.
wright and signed_old are kept for legacy purposes.
Custom figures
WrightTools
offers specialized tools for custom publication quality figures.
As an example, we will break down the figure in Custom Figure,
exploring the relationships between WrightTools
and the underlying matplotlib
.
The preprocessing of data is handled in tools covered in Data.
First, the full code and the image it creates:
import matplotlib.pyplot as plt
import numpy as np
import WrightTools as wt
from WrightTools import datasets
# obtain and process data
p = datasets.wt5.v1p0p1_MoS2_TrEE_movie
data = wt.open(p)
data.level(0, 2, -3)
data.convert("eV", convert_variables=True, verbose=False)
data.smooth([2, 2, 2])
data.ai0.symmetric_root(2)
data.ai0.normalize()
data.ai0.clip(min=0, replace="value")
# chop out data of interest
d2_vals = [-50, -500]
w2_vals = [1.7, 1.8, 1.9, 2.0]
wigners = [data.chop("w1=wm", "d2", at={"w2": [w2, "eV"]})[0] for w2 in w2_vals]
traces1 = [
data.chop("w1=wm", at={"w2": [w2, "eV"], "d2": [d2_vals[0], "fs"]})[0] for w2 in w2_vals
]
traces2 = [
data.chop("w1=wm", at={"w2": [w2, "eV"], "d2": [d2_vals[1], "fs"]})[0] for w2 in w2_vals
]
tracess = [traces1, traces2]
# prepare spine colors
wigner_colors = ["C0", "C1", "C2", "C3"]
trace_colors = ["#FE4EDA", "#00B7EB"]
# prepare figure gridspec
cols = [1, 1, "cbar"]
aspects = [[[0, 0], 0.3]]
fig, gs = wt.artists.create_figure(
width="double", cols=cols, nrows=3, aspects=aspects, wspace=0.35, hspace=0.35
)
# plot wigners
indxs = [(row, col) for row in range(1, 3) for col in range(2)]
for indx, wigner, color in zip(indxs, wigners, wigner_colors):
ax = plt.subplot(gs[indx])
ax.pcolor(wigner, vmin=0, vmax=1) # global colormpa
ax.contour(wigner) # local contours
ax.grid()
wt.artists.set_ax_spines(ax=ax, c=color)
# set title as value of w2
wigner.constants[0].format_spec = ".2f"
wigner.round_spec = -1
wt.artists.corner_text(wigner.constants[0].label, ax=ax)
# plot overlines
for d2, t_color in zip(d2_vals, trace_colors):
ax.axhline(d2, color=t_color, alpha=0.5, linewidth=6)
# plot w2 placement
ax.axvline(wigner.w2.points, color="grey", alpha=0.75, linewidth=6)
# plot traces
indxs = [(0, col) for col in range(2)]
for indx, color, traces in zip(indxs, trace_colors, tracess):
ax = plt.subplot(gs[indx])
for trace, w_color in zip(traces, wigner_colors):
ax.plot(trace, color=w_color, linewidth=1.5)
ax.grid()
ax.set_xlim(trace.axes[0].min(), trace.axes[0].max())
wt.artists.set_ax_spines(ax=ax, c=color)
# plot colormap
cax = plt.subplot(gs[1:3, -1])
ticks = np.linspace(data.ai0.min(), data.ai0.max(), 11)
wt.artists.plot_colorbar(cax=cax, label="amplitude", cmap="default", ticks=ticks)
# set axis labels
wt.artists.set_fig_labels(xlabel=data.w1__e__wm.label, ylabel=data.d2.label, col=slice(0, 1))
# ylabel of zeroth row
ax = plt.subplot(gs[0, 0])
ax.set_ylabel("amplitude")
# saving the figure as a png
wt.artists.savefig("custom_fig.png", fig=fig, close=False)
Layout
WrightTools
defines a handy function, create_figure()
, for easily and flexibly making complicated figures.
When made with this function, Axes
created have additional functionality built in to work with Data
objects directly.
create_figure()
makes it easy to create figures the perfect size for "single"
or double"
column figures for journal articles (though they are convenient in other contexts as well).
create_figure()
also creates a GridSpec
to help layout subplots.
Columns are created with a weighted list with the number of columns, passed as cols
.
A special weight, "cbar"
, provides a fixed width column intended for color bars.
All other columns are proportionally distributed according to their weights.
The number of rows in the grid are specified with the nrows
kwarg.
You can modify the aspect ratio of particular rows independently using the aspects
and default_aspect
kwargs.
Spacing between figures can be adjusted with the wspace
and hspace
kwargs for the width and height, respectively.
Axes can be accessed with matplotlib.pyplot.subplot()
.
Importantly, axes may span multiple rows/columns by using slice syntax into the gridspec.
This is demonstrated with the color bar axes here, which takes up two rows in the last column.
# prepare figure gridspec
cols = [1, 1, "cbar"]
aspects = [[[0, 0], .3]]
fig, gs = wt.artists.create_figure(
width="double", cols=cols, nrows=3, aspects=aspects, wspace=1.35, hspace=.35
)
# plot wigners
indxs = [(row, col) for row in range(1, 3) for col in range(2)]
for indx, wigner, color in zip(indxs, wigners, wigner_colors):
ax = plt.subplot(gs[indx])
...
indxs = [(0, col) for col in range(2)]
for indx, color, traces in zip(indxs, trace_colors, tracess):
ax = plt.subplot(gs[indx])
...
cax = plt.subplot(gs[1:3, -1])
Plot
Once you have axes with the subplot()
call, it can be used as you are used to using matplotlib.axes.Axes
objects (though some defaults, such as colormap, differ from bare matplotlib).
For certain plot methods, you can also pass WrightTools.data.Data
objects in directly (and there are some extra kwargs available when you do).
These WrightTools.artists.Axes
will extract out the proper arrays and plot the data.
for indx, wigner, color in zip(indxs, wigners, wigner_colors):
ax = plt.subplot(gs[indx])
ax.pcolor(wigner, vmin=0, vmax=1) # global colormap
ax.contour(wigner) # local contours
...
for indx, color, traces in zip(indxs, trace_colors, tracess):
ax = plt.subplot(gs[indx])
for trace, w_color in zip(traces, wigner_colors):
ax.plot(trace, color=w_color, linewidth=1.5)
Currently supported plot methods are:
- contour()
- contourf()
- imshow()
- pcolor()
- pcolormesh()
- plot()
- scatter()
Beautify
Once the main data is plotted, additional information can be overlaid on the axes.
Of course, standard matplotlib methods like axhline()
or set_xlim()
are all available.
In addition, WrightTools
defines some small helper functions for common tasks.
set_ax_spines()
Easily set color/width of the outline (spines) of an axisGreat for using color to connect different parts of a figure (or figures throughout a larger work)
corner_text()
Quick and easy plot labeling within a dense gridPairs well with
WrightTools.data.Constant.label
plot_colorbar()
Add a colorbar in a single function callset_fig_labels()
Label axes in a whole row/column of a figureAllows the use of slice objects to limit range affected
Removes axis labels from other axes in the rectangle
Pairs well with
WrightTools.data.Axes.label
wigner_colors = ["C0", "C1", "C2", "C3"]
trace_colors = ["#FE4EDA", "#00B7EB"]
...
for indx, wigner, color in zip(indxs, wigners, wigner_colors):
...
ax.grid()
wt.artists.set_ax_spines(ax=ax, c=color)
# set title as value of w2
wigner.constants[0].format_spec = ".2f"
wigner.round_spec = -1
wt.artists.corner_text(wigner.constants[0].label, ax=ax)
# plot overlines
for d2, t_color in zip(d2_vals, trace_colors):
ax.axhline(d2, color=t_color, alpha=.5, linewidth=6)
# plot w2 placement
ax.axvline(wigner.w2.points, color="grey", alpha=.75, linewidth=6)
...
for indx, color, traces in zip(indxs, trace_colors, tracess):
...
ax.set_xlim(trace.axes[0].min(), trace.axes[0].max())
wt.artists.set_ax_spines(ax=ax, c=color)
# plot colormap
cax = plt.subplot(gs[1:3, -1])
ticks = np.linspace(data.ai0.min(), data.ai0.max(), 11)
wt.artists.plot_colorbar(cax=cax, label="amplitude", cmap="default", ticks=ticks)
# set axis labels
wt.artists.set_fig_labels(xlabel=data.w1__e__wm.label, ylabel=data.d2.label, col=slice(0, 1))
Save
Saving figures is as easy as calling savefig()
.
This is a simple wrapper for matplotlib.pyplot.savefig()
which allows us to override defaults so that figures created with create_figure()
have proper margins and resolution.
If you wish to change margin padding or transparancy settings, the matplotlib function will work just as well.
# saving the figure as a png
wt.artists.savefig("custom_fig.png", fig=fig, close=False)