# The Run's Data: RunData Every plot function receives one argument, `data`: a `RunData` holding every row the run has saved so far, where a row is one step's record (see [How plesty-lib runs a measurement](../quickstart/experiment.md#how-plesty-lib-runs-a-measurement)). `Viz` creates it and refreshes it; a plot only asks it questions. [The layers of a viewer](index.md#the-layers-of-a-viewer) shows where it sits. ## One store for all plots `RunData` reads the run's rows **once** and keeps them, and every plot of the viewer gets the same `RunData`. A spectrum, a map of all spectra and a fit of the same scan therefore read the run's file once between them, and each sees every row. At each refresh, only the rows added since the last one are read. ## What it answers | Call | Gives | |---|---| | `data.latest` | the newest row, whichever step wrote it; `None` before the first | | `data.last("counts")` | the newest row that has a `counts` field; `None` if there is none yet | | `data.rows` | every row so far, as a list of dictionaries | | `data.column("hwp_deg")` | one field across all rows, as an array | | `data.stack("counts")` | a field that holds an array in each row, stacked into a 2-D array, one row per record | | `data.where(qd="QD-02")` | only the rows whose field has that value, as a `RunData` again | | `data.config` | the settings the run's plan was built from | | `data.provenance` | which versions of which packages produced the run | | `data.status()` | the run's state and step counts (see [Reading a Run](../experiment/reading_runs.md)) | **`latest` or `last`.** Not every step of a plan saves the same kind of row. A scan might save a spectrum per angle and then one row with a fit, so the newest row is the fit, and a plot of the spectra wants `data.last("counts")`. **`column` or `stack`.** `column` is for a field with one value per row. For a field that holds a whole array per row, such as a spectrum, it raises an error naming `stack`, which is what that field needs. ## `reads`: rows that name a file A device that produces large files saves them itself, and its row holds only the file's path (see [Data on a Shared Disk](../experiment/shared_disk.md)). To plot the numbers inside, give the viewer a `reads` function. It receives a row and the run, and returns the extra fields to add to that row: ```python from plesty.lib.data import SpectrumData from plesty.lib.monitor import Viz def read_spectrum(row, run): """Turn the file a row names into the fields the plots want.""" if "data_file" not in row: return {} spectrum = SpectrumData.load(run.local(row["data_file"])) counts = spectrum.intensity.reshape(-1, spectrum.intensity.shape[-1]).sum(axis=0) return {"wavelength": spectrum.wavelength, "counts": counts} viz = Viz("Polarization PL", experiment="pol_pl", reads=read_spectrum) ``` `run.local()` turns the recorded path into a path on this computer. `reads` is called once per row and its answer kept, so each file is loaded once however many plots use it. If it fails, for example on a file that is still being written, that row simply lacks the extra fields; the viewer goes on. An experiment whose rows hold their own numbers needs no `reads`.