A simple tool for visualizing scientific data — a lightweight, scriptable replacement for quick gnuplot-style plotting, built on numpy and matplotlib.
vizit is for the everyday case of "I have some columns of numbers in a text file and I want to see them" — while still handling headers, comments, multiple series, log axes, error bars, fits and a handful of plot types. It works three ways:
- as a command-line tool —
vizit line data.dat -c 2 - as a Python library —
from vizit import load, line - in shell pipelines —
cat data.dat | vizit line -
vizit line trajectory.dat -x 1 -c 2,3 --logy --title "Energy" -o energy.png- Installation
- Quick start
- The data-file model
- Column references
- Command-line interface
- Reading from a pipe / stdin
- Statistics & overlays
- Styling & output
- Headless / no-display behaviour
- Python API
- Recipes
- Exit status & error handling
- Development
- Building the docs
- Copyright
vizit requires Python 3.12+.
pip install .
# or, for development (tests + tooling):
pip install -e '.[test]'This installs the vizit command and the importable vizit package. You can
also invoke it as a module: python -m vizit ....
Check the install:
vizit --versionA few representative commands (all use the packaged sample file
vizit/data/look_and_say.dat, a single column of integers):
# line plot of the single column, log y-axis, with a title
vizit line vizit/data/look_and_say.dat -c 1 --logy --title "Look and say"
# save to a PNG and don't open a window
vizit line vizit/data/look_and_say.dat -c 1 --logy -o look.png --no-show
# histogram with 5 bins
vizit hist vizit/data/look_and_say.dat -c 1 --bins 5For multi-column files, select the x column and one or more y columns:
vizit line data.dat -x 1 -c 2,3,4 # three series vs column 1
vizit scatter data.dat -x 1 -y 2 --fit # scatter + linear fit
vizit bar data.dat -x 1 -c 2,3 # grouped barsvizit reads whitespace- or delimiter-separated numeric tables. The parser handles the messy realities of scientific data files:
- Comments. Any line whose first non-blank character is
#or@is ignored. Change the markers with--comments(e.g.--comments '%'), or pass--comments ''to disable comment handling. Blank lines are always skipped. - Separators. Whitespace, comma, tab and semicolon are auto-detected. Force
a specific one with
-s/--sep— for example-s ,for CSV, or-s $'\t'for tab-separated data. - Headers. If the first non-comment line is not all-numeric, it is taken
as a row of column names. You can then select columns by name. Force the
behaviour either way with
--header/--no-header. - Scientific notation (
1.5e-3) is supported natively. - Leading rows can be discarded with
--skip-rows N(applied after any header line is consumed).
Example file with a header and a comment:
# simulation run 7
time energy error
0.0 0.10 0.01
1.0 0.22 0.02
2.0 0.31 0.02
Note on headers vs comments. Column names are read from a plain (non-comment) header line. A
#-prefixed line like# time energyis treated as a comment and skipped, so it is not used for names. Put the names on their own uncommented line if you want name-based selection.
Malformed input is reported with the exact file line number and offending
token, e.g. vizit: data.dat, line 4: could not parse 'NA' as a number.
All column references — on the command line and in the Python API — are
1-based: column 1 is the first column. This matches the convention used by
gnuplot and awk.
- Numeric:
-c 2,-c 2,3,4,-x 1,-y 2,--yerr 3 - By name (when the file has a header):
-c energy,-c energy,error,-x time - When no
xcolumn is given, the row index (starting at 0) is used as the x-axis.
An out-of-range index or unknown name produces a clear error listing the valid columns.
vizit <plot> <file> [options]
<plot> is one of line, scatter, hist, bar. <file> is a path, or -
to read standard input. Run vizit <plot> --help for the exact, up-to-date
flag list.
line — line plot of one or more y columns against an x column (or the row
index), with optional statistical overlays.
| Flag | Meaning |
|---|---|
-c, --cols |
1-based y columns, comma-separated (e.g. 2,3,4) |
-x |
1-based x column (default: row index) |
--yerr |
column of symmetric y error-bar magnitudes |
--mean |
overlay the mean of each series |
--median |
overlay the median of each series |
--fit |
overlay a linear least-squares fit of each series |
--rolling N |
overlay a rolling average with window N |
scatter — scatter plot of one column against another.
| Flag | Meaning |
|---|---|
-x |
1-based x column (default: row index) |
-y |
1-based y column |
--yerr |
column of symmetric y error-bar magnitudes |
--fit |
overlay a linear least-squares fit |
hist — histogram of one or more columns.
| Flag | Meaning |
|---|---|
-c, --cols |
1-based columns, comma-separated |
--bins N |
number of bins (default: 10) |
--density |
normalise to a probability density |
bar — bar chart of one or more value columns (multiple columns produce
grouped bars).
| Flag | Meaning |
|---|---|
-c, --cols |
1-based value columns, comma-separated |
-x |
1-based column for bar positions (default: row index) |
Every subcommand accepts the following groups.
Input
| Flag | Meaning |
|---|---|
-s, --sep SEP |
column separator (default: auto-detect) |
--comments CHARS |
comment-line markers (default: #@) |
--header / --no-header |
force / disable the column-name header line |
--skip-rows N |
discard N leading data rows |
Axes
| Flag | Meaning |
|---|---|
--xlabel / --ylabel |
axis labels |
--title |
figure title |
--xlim LO,HI |
x-axis limits (either side may be left blank, e.g. 0, ) |
--ylim LO,HI |
y-axis limits |
--logx / --logy |
logarithmic axis |
--symlogx / --symlogy |
symmetric-log axis (handles zero/negative values) |
--grid / --no-grid |
toggle the background grid (default: on) |
--no-legend |
hide the legend |
Style
| Flag | Meaning |
|---|---|
--style NAME |
matplotlib style (e.g. ggplot, seaborn-v0_8) |
--color C |
series color (repeatable, cycles over series) |
--marker M |
series marker (repeatable) |
--figsize W H |
figure size in inches |
--dpi N |
output resolution (default: 150) |
Output
| Flag | Meaning |
|---|---|
-o, --output PATH |
save figure; format inferred from extension (.png, .pdf, .svg, …) |
--no-show |
do not open an interactive window |
Use - as the filename to read from standard input, so vizit drops into shell
pipelines:
cat data.dat | vizit line -
awk '{print $1, $5}' raw.log | vizit line - -o out.png --no-show
seq 1 100 | vizit hist - --bins 20 --no-show -o hist.pngBecause stdin is consumed for data, combine it with -o ... --no-show when
running non-interactively.
The line plot can add analysis overlays computed with numpy:
vizit line noisy.dat -c 1 --mean --median # horizontal mean/median lines
vizit line noisy.dat -c 1 --rolling 20 # 20-point rolling average
vizit line data.dat -x 1 -c 2 --fit # linear least-squares fitThe --fit legend entry includes the fitted slope and R². scatter also
supports --fit. Error bars are drawn by pointing --yerr at a column of
symmetric magnitudes (single series only):
vizit line data.dat -x 1 -c 2 --yerr 3vizit line data.dat -c 2 \
--style ggplot \
--color tab:red --marker o \
--figsize 8 5 --dpi 300 \
--title "Run 7" --xlabel "time (ns)" --ylabel "energy" \
-o figure.pdf --no-showThe output format is taken from the file extension, so -o figure.pdf produces
a vector PDF and -o figure.svg an SVG — handy for publication figures.
vizit never hardcodes a GUI backend. It shows an interactive window by default, but automatically falls back to non-interactive rendering when:
- you pass
--no-show, or - no display is detected (e.g. over SSH without X forwarding).
In the headless case, use -o to capture the figure to a file. If you set the
MPLBACKEND environment variable, vizit respects your choice and does not
override it.
The CLI is a thin wrapper over the library, so anything you can do from the
shell you can do from Python. Each plotting function returns a matplotlib
Figure, which you can
keep customising before saving or showing.
from vizit import load, line, scatter, hist, bar, PlotConfig
# Load once into a Dataset, then plot.
ds = load("data.dat") # auto-detects separator/header/comments
ds = load("data.csv", sep=",") # force a separator
ds = load("-", header=True) # read stdin, force a header row
fig = line(ds, cols=[2, 3], x=1, logy=True)
fig.savefig("plot.png", dpi=300)Columns may be given by 1-based index or by name:
line(ds, x="time", cols=["energy", "error"])Options can be passed as keyword overrides, or bundled in a reusable
PlotConfig:
# inline overrides
line(ds, cols=[2], logy=True, title="Run 7", output="run7.png", show=False)
# or a shared config object
cfg = PlotConfig(style="ggplot", figsize=(8, 5), dpi=300, show=False)
line(ds, cols=[2], config=cfg, output="a.png")
scatter(ds, x=1, y=2, fit=True, config=cfg, output="b.png")The Dataset container exposes the underlying numpy array and helpers:
ds.data # 2-D float64 numpy array, shape (n_rows, n_cols)
ds.n_rows, ds.n_cols
ds.column(2) # 1-D array of column 2 (1-based) — or ds.column("energy")
ds.columns([2, 3]) # 2-D array of the selected columns
ds.name_of(2) # display label ("energy", or "col 2" if no header)Post-process the returned figure with the full matplotlib API:
fig = line(ds, cols=[2], show=False)
ax = fig.axes[0]
ax.axvspan(10, 20, alpha=0.2, color="red")
fig.savefig("annotated.png", dpi=300)# Multiple series vs a shared x, log scale, legend off
vizit line data.dat -x 1 -c 2,3,4 --logy --no-legend
# Select columns by name (file has a header line)
vizit line data.dat -x time -c energy,entropy
# CSV input, custom axis limits
vizit scatter data.csv -s , -x 1 -y 2 --xlim 0,100 --ylim -5,5
# Density histogram of two columns overlaid
vizit hist samples.dat -c 1,2 --bins 40 --density
# Smooth a noisy signal and mark its mean
vizit line noisy.dat -c 1 --rolling 25 --mean -o smoothed.png --no-show
# Build a figure straight from a pipeline
grep -v '^#' raw.tsv | cut -f1,3 | vizit line - -s $'\t' -o out.pdf --no-showvizit returns:
0on success2for expected errors (missing file, bad column, malformed value, bad option) — it prints a concisevizit: ...message to standard error, with no Python traceback.
This makes it safe to use in scripts (vizit ... || echo "plot failed"). In the
Python API the same conditions raise a vizit.VizitError subclass
(DataError, ColumnError, PlotError) that you can catch.
pip install -e '.[test]'
pytest # run the test suite
ruff check . # lint
ruff format . # format
mypy vizit # type-checkVersioning is handled automatically from git tags by
versioningit; to cut a release, tag the
commit (git tag -a X.Y.Z -m "vizit X.Y.Z" && git push --follow-tags).
Continuous integration runs on GitHub Actions (see .github/workflows/ci.yml).
Full documentation (getting started, CLI reference, examples and API) lives in
docs/ and is built with Sphinx:
pip install sphinx sphinx_rtd_theme
cd docs && make html
# open docs/_build/html/index.htmlCopyright (c) 2020-2026, Alex Holehouse — released under the LGPL-3.0-or-later license.
Project originally based on the Computational Molecular Science Python Cookiecutter version 1.1.