Scan Steps¶
A Scan object partitions the event stream of an Array into
sequential scan steps — for example, one step per delay value in a
pump-probe scan. It is always accessible via array.scan.
What Is a Scan?¶
Internally, the Scan stores:
step_lengths— a list[n0, n1, n2, ...]whereniis the number of events in stepi.parameter— a dict mapping parameter names to their per-step values, e.g.{"delay_ps": {"values": [-1.0, 0.0, 0.5, 1.0, ...]}}.
from escape.storage.example_data import make_scan
sig = make_scan(n_steps=10, n_events_per_step=500, scan_par_name="delay_ps",
scan_par_values=list(range(-5, 5)))
print(sig.scan)
# Scan over 10 steps
# Parameters delay_ps
print(sig.scan.step_lengths) # [500, 500, 500, ...]
Parameter Table¶
par_steps returns a pandas.DataFrame with one row per
step:
print(sig.scan.par_steps)
# delay_ps step_length
# 0 -5.0 500
# 1 -4.0 500
# ...
Accessing Individual Steps¶
Subscript the scan to retrieve a step as an ordinary Array:
step0 = sig.scan[0] # first step
step5 = sig.scan[5] # sixth step
last = sig.scan[-1] # last step
# Slices return an Array covering those steps:
first3 = sig.scan[0:3] # steps 0, 1, 2 concatenated
Each returned Array has step_lengths=[n] and the parameter values for that
step, so it behaves exactly like a single-step scan.
Per-Step Statistics¶
All statistics methods on Scan iterate over steps and return a list — one
value per step:
means = sig.scan.nanmean() # list of per-step means
stds = sig.scan.nanstd() # list of per-step std devs
meds = sig.scan.nanmedian() # list of per-step medians
counts = sig.scan.count() # list of event counts per step
For multi-dimensional data the axis argument is forwarded:
# Mean image per step for a 3-D (events, rows, cols) array
step_mean_imgs = img_arr.scan.mean(axis=0) # list of 2-D arrays
Combined statistics:
med, mad = sig.scan.median_and_mad()
Inline Plotting of Statistics¶
Every per-step statistics method accepts a plot keyword that immediately
visualises the result without a separate .plot() call. Scalar-per-step
results are drawn as a line; 1-D-array-per-step results (e.g. waveforms) are
rendered as a 2-D heat-map.
|
Effect |
|---|---|
|
use the current Matplotlib axes ( |
a |
open a new subplot inside that figure |
an |
draw directly on that axes object |
import matplotlib.pyplot as plt
from escape.storage.example_data import make_scan
sig = make_scan(n_steps=12, scan_par_name="delay_ps",
scan_par_values=list(range(-5, 7)), seed=0)
# Quick plot on the current axes:
sig.scan.nanmean(plot=True)
# Plot on a specific axes and customise style:
fig, axes = plt.subplots(1, 2, figsize=(9, 3))
sig.scan.nanmean(plot=axes[0], plot_kws={"marker": "o", "color": "steelblue"})
sig.scan.nanstd(plot=axes[1], plot_kws={"color": "coral"})
plt.tight_layout()
(png)
The plot_kws dict is forwarded directly to the underlying Matplotlib call, so
any valid Axes.plot keyword works. For 2-D results, plot_kws is forwarded
to plot2D(), and the special key "colorbar" (bool,
default True) controls whether a colourbar is added.
Plotting a Scan¶
scan.plot() produces an errorbar plot with the scan parameter on the x-axis
and the per-step median (with 1σ confidence of the mean) on the y-axis:
import matplotlib.pyplot as plt
sig.scan.plot()
plt.xlabel("delay / ps")
plt.ylabel("signal (a.u.)")
plt.tight_layout()
plt.show()
Step Histogram¶
scan.hist() plots a 2-D colour map of per-step value histograms — useful for
visualising shot-to-shot fluctuations along a scan:
x, bins, hdata = sig.compute().scan.hist(bins=50, normalize_to="max")
Scan Arithmetic¶
Binary operators applied between a Scan and a scalar (or a list with the same length as the scan) perform per-step operations:
# Subtract the per-step mean from each step's data
step_means = sig.scan.nanmean()
corrected = sig.scan - step_means # escape.Array
Number of Events Per Step¶
sig.scan.count() # list of ints
Merging Scans from Multiple Runs¶
merge_scans() combines data from several scans at the same
parameter points, pooling events together:
sig_run1 = make_scan(n_steps=5, scan_par_name="angle")
sig_run2 = make_scan(n_steps=5, scan_par_name="angle")
merged = sig_run1.scan.merge_scans(sig_run2.scan,
roundto_interval=0.01,
par_name="angle")