nb.plot
One line per run
Every selected dataset is drawn on the same axes, each with its own color and marker from the palette. Nothing to loop over.
nb.plot(x='time_s', y='cht_c')
unichart example gallery
19 figures, each one produced by the few lines of code printed under it. The data is the same four engine dyno runs throughout — load once, then choose what to show. Every chart is live, exactly as it comes out of the notebook: hover a trace for values, drag to zoom, double-click to reset.
14 kinds of chart. Pick one to jump to its example.
Four runs of a piston engine on a test stand, 240 samples each at 2.5 s,
sharing one schema — the shape unichart is built for.
Load it once and every example below starts from this state. No example re-reads the file or touches Plotly directly.
from unichart import UnichartNotebook
import pandas as pd
# 4 runs x 240 samples, one tidy frame
df = pd.read_csv('dyno_runs.csv')
nb = UnichartNotebook()
nb.set_default_format(marker=None, linestyle='-',
linewidth=1.8) # traces, not markers
nb.load_df(df, set_idx_column='run_id',
set_name_column='run_name')
nb.set_plot_size(4.6, 3.0) # one panel size, every plot
The four layouts that cover most of a test campaign.
nb.plot
Every selected dataset is drawn on the same axes, each with its own color and marker from the palette. Nothing to loop over.
nb.plot(x='time_s', y='cht_c')
nb.plot(by='vars')
Pass a list of Y columns and you get a grid — the default layout. The runs stay color-matched across the panels.
nb.plot(x='time_s', y=['rpm', 'torque_nm', 'cht_c', 'eta_pct'], ncols=2)
nb.plot(by='sets')
Same data, transposed: one panel per dataset with every variable in it. ncols sets the grid; hspace and vspace set the gaps between panels, in pixels.
nb.select([0,3])
nb.plot(x='time_s', y=['cht_c', 'egt_c'], by='sets', vspace=110)
nb.plot_ymult
Stacked Y axes for quantities that share an X but nothing else — rpm in thousands next to a fuel flow in single digits.
nb.select([0, 2])
nb.linestyle(2, ':')
nb.color('rpm', 'blue')
nb.color('cht_c', 'red')
nb.color('fuel_kgh', 'orange')
nb.plot_ymult(x='time_s', y=['rpm', 'cht_c', 'fuel_kgh'])
Choose what is drawn before you worry about how it looks.
nb.select · nb.query
Selection decides which datasets are drawn; a query filters rows inside them. Both stick until you change them.
nb.select([0, 3]) # draw these two runs
nb.query('all', 'phase == "climb"') # keep these rows
nb.plot(x='time_s', y=['rpm', 'eta_pct'])
nb.line · nb.highlight
Reference lines carry a label drawn inside the plot area; highlights shade a band of the X axis. Both persist across plots.
nb.line('cht_c', 150, color='firebrick', linestyle='--',
label='CHT limit', label_position='left above')
nb.highlight('time_s', (300, 450), color='orange', alpha=0.12)
nb.plot(x='time_s', y='cht_c')
nb.var_format
Where the variables are the series rather than the runs, a per-variable override gives each column its own identity — and it outranks the per-dataset style, so CHT is dashed red everywhere.
nb.select(0)
nb.var_format('cht_c', color='firebrick', linestyle='--')
nb.var_format('egt_c', color='darkorange')
nb.var_format('fuel_kgh', color='steelblue')
nb.plot_ymult(x='time_s', y=['cht_c', 'egt_c', 'fuel_kgh'])
When the spread matters more than the trace.
nb.plot_marginal
The operating points, with the distribution of each axis in a strip beside it. Swap in 'box', 'violin', 'rug' or 'kde'.
nb.marker('all', 'o'); nb.linestyle('all', ''); nb.markersize('all', 5)
nb.plot_marginal(x='rpm', y='torque_nm', marginal='box')
nb.histogram
Overlaid histograms per dataset, with the binning, normalization and opacity you would expect to be able to set.
nb.histogram(x='cht_c', nbins=40, alpha=0.6)
nb.box
One box per run per category, grouped along a categorical X column.
nb.box(x='phase', y='eta_pct', points='outliers')
nb.bar
Bar height is an aggregate of Y over the rows in each category — 'mean' by default, or any pandas reducer.
nb.bar(x='phase', y=['fuel_kgh', 'eta_pct'], agg='mean', barmode='group')
Scattered data as a surface, and numbers as numbers.
nb.contour
Efficiency over the rpm/torque plane, interpolated from the samples the runs happened to visit, with one run's track drawn over it.
nb.combine_sets('all', title='all runs')
nb.select(4)
nb.color(0, 'black')
nb.linestyle(0, '--')
nb.contour(x='rpm', y='torque_nm', z='eta_pct', overlay_sets=0)
nb.table
Interpolation mode reads each run's Y at your X inputs, so runs sampled at different times line up in one table.
nb.table(cols=['rpm', 'eta_pct'], x_col='time_s',
x_in=[150, 300, 450], sig_figs=4, output='fig')
nb.summary
Count, min, mean, max and std for the columns you name — the same sortable table widget as nb.table.
nb.summary(cols=['rpm', 'torque_nm', 'eta_pct'], sig_figs=4, output='fig')
Differences against a baseline, and fits through the cloud.
nb.delta
Each study run is aligned to the base run and differenced, producing DL_<parm> and DLPCT_<parm> columns in new datasets that keep the study run's color.
nb.delta(base_idx=0, study_indices=[1, 2, 3],
align_on='time_s', delta_parms=['cht_c', 'eta_pct'])
nb.select([4, 5, 6])
nb.plot(x='time_s', y=['DL_cht_c', 'DLPCT_eta_pct'], hspace=100)
nb.reg_order
Give a dataset a regression order and its line becomes the fit, labelled in the legend. LOWESS needs statsmodels.
nb.marker('all', 'o'); nb.markersize('all', 4); nb.alpha_marker('all', 0.7)
nb.reg_order('all', 2)
nb.plot(x='rpm', y='eta_pct')
nb.hue
A dataset can take its point colors from any column, turning a scatter into a three-variable read.
nb.select(3)
nb.marker(3, 'o'); nb.linestyle(3, ''); nb.markersize(3, 6)
nb.hue(3, 'eta_pct')
nb.plot(x='rpm', y='torque_nm')
The same figures, restyled.
nb.set_plot_style
Every figure above is drawn in the default Matplotlib-like style — four spines, outward ticks, DejaVu Sans, the tab10 cycle. One call switches the whole environment to Plotly's own look instead.
nb.set_plot_style('plotly')
nb.plot(x='time_s', y=['torque_nm', 'eta_pct'], hspace=200)
nb.toggle_darkmode
One switch, applied to the whole environment — plots, tables and dashboards alike.
nb.toggle_darkmode(True)
nb.plot(x='time_s', y=['rpm', 'cht_c'], hspace=200)