A simple tool to manipulate and visualize UV/optical/NIR spectra for astronomical research.
Online documentation is hosted on Read the Docs: https://specbox.readthedocs.io/en/latest/index.html
@software{fu_2026_18642758,
author = {Fu, Yuming},
title = {specbox: a simple tool to manipulate and visualize UV/optical/NIR spectra for astronomical research},
month = feb,
year = 2026,
publisher = {Zenodo},
version = {v1.0.0},
doi = {10.5281/zenodo.18642758},
url = {https://doi.org/10.5281/zenodo.18642758}
}GPLv3. See LICENSE.
numpyscipyastropypyqtgraphPySide6specutilsmatplotlibpandaspyarrow(installed automatically for parquet support from 1.0.3)requestspillow(PIL)astroquery
Python >=3.9 is declared; Python 3.12 is recommended for WP9 inspection. Pip installs the dependencies. Set up an isolated environment for a new installation (choose either option A or B):
# Option A: Python venv
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip# Option B: conda
conda create -n specbox python=3.12 -y
conda activate specbox
python -m pip install --upgrade pipInstall the stable release from PyPI (recommended):
python -m pip install specboxTo update, activate the same environment, close the viewer, then run:
python -m pip install --upgrade specbox
python -m pip show specbox
specbox-viewer --helpThe WP9 workflow requires 1.0.3 or later once published. Restart the viewer
after upgrading. If the version remains old, compare python -m pip --version
and python -c "import sys; print(sys.executable)" with the launcher path
(command -v specbox-viewer on macOS/Linux, where specbox-viewer on Windows).
Claim a batch and confirm it with Yuming, then download and extract the whole folder. Keep its script and parquet inputs together. In the activated environment:
cd /path/to/your/batch
bash alias_001_review.shFinalize redshift and classification, press Q to commit and advance, and use
Save regularly and Save & Quit at the end. Send the CSV named by the script's
--output-file to Yuming on Slack or yfu@strw.leidenuniv.nl, with the batch ID
and complete/partial status. See the VI tutorial
for line markers, save/resume limitations, recovery, and Windows instructions.
To install a pre-release/development version from source:
git clone https://github.com/rudolffu/specbox.git
cd specbox
python -m pip install .specbox installs five CLIs:
specbox-viewer: launch the enhanced viewerspecbox-coadd: coadd Euclid BGS+RGS chunksspecbox-euclid-parquet: convert raw single-arm Euclid combined FITS to parquetspecbox-pcf: run template PCF redshift and writeZ_TEMPspecbox-merge-redshift-table: merge external reference redshifts into spectra parquet files
# Viewer (history auto-loads when output CSV already exists)
specbox-viewer --spectra your_spectra.fits --spec-class euclid
# Recovery snapshots are written every 50 completed spectra to:
# ./temp/<sample-name>/vi_temp_<count>.csv
# Viewer with image panel / cutout downloads enabled explicitly
specbox-viewer --spectra your_spectra.fits --spec-class euclid --images
# Viewer with an external reference-redshift table
specbox-viewer --spectra your_spectra.parquet --spec-class euclid \
--redshift-table catalog.fits --redshift-key object_id --redshift-column Z
# AIMS-z review parquet (images are disabled by default)
specbox-viewer --spectra review_bundle_specbox.parquet --spec-class aimsz-review
# Coadd paired Euclid arms (default: EXTNAME intersection)
specbox-coadd --rgs-file rgs_chunk.fits --bgs-file bgs_chunk.fits --output-prefix coadd/out_chunk_001
# Convert raw single-arm Euclid FITS to parquet
specbox-euclid-parquet --fits rgs_chunk.fits --output-prefix parquet/rgs_chunk_001
# Merge an external redshift table into a spectra parquet file
specbox-merge-redshift-table --spectra your_spectra.parquet \
--redshift-table catalog.fits --redshift-key object_id --redshift-column Z \
--output your_spectra_with_zref.parquet --fill-z-vi
# PCF default: Type 1 only
specbox-pcf --fits coadd/out_chunk_001.fits
# PCF with Type 1 + Type 2 (ragn_na, internally limited to 0 < z < 3)
specbox-pcf --fits coadd/out_chunk_001.fits --enable-type2
# PCF with ragn_dr1 only (mapped to type1)
specbox-pcf --fits coadd/out_chunk_001.fits --ragn-dr1-onlyTemporary recovery snapshots are cumulative and use a sample name derived from the input filename. Paired RGS/BGS inputs share a sample folder after the arm token is removed. Final and manual saves continue to use the configured viewer output CSV.
Package versions are derived from Git tags via setuptools-scm. Do not edit
specbox.__version__ or hard-code a version in pyproject.toml; at runtime,
specbox.__version__ is read from the installed package metadata.
For a release, create and push a new version tag such as v1.0.3, then publish a
GitHub Release from that tag. The PyPI workflow builds from the release tag and
uploads only when the GitHub Release is published, not when a tag is pushed.
See release notes and checklist. Never move an existing release tag; validate tests, docs, and distributions before tagging.
The main classes and functions of specbox are:
SpecLAMOSTandSpecSDSS: classes to read and manipulate spectra from the LAMOST and SDSS surveys, respectively.SpecIRAF: class to read and manipulate spectra from the IRAF format.SpecAIMSZReview: parquet reader for AIMS-z review bundles with canonical stringobjidkeys (aimsz:{object_id}).SpecEuclid1d: reader for Euclid combined 1D spectra, withMASK/good_masksupport and optionalgood_pixels_only=True.SpecEuclid1dDual: paired Euclid reader for BGS+RGS with overlap scaling and merged/coadd-ready outputs.SpecEuclidCoaddRow: reader for dataframe/parquet rows containing coadded spectra arrays.SpecPandasRow: generic reader for "table-of-spectra" files readable by pandas (parquet/csv/feather/...), where each row stores arrays (e.g. wavelength/flux/ivar).SpecSparcl: SPARCL parquet/table reader (e.g., for filesparcl_spectra.parquet). Common metadata columns includeredshift,data_release,targetid, and (optional)euclid_object_idfor Euclid overlay.
PGSpecPlot: class to plot spectra in apyqtgraphplot.PGSpecPlotApp: class to create apyqtgraphplot with aQApplicationinstance.PGSpecPlotThread: class to create apyqtgraphplot in a thread.
from specbox import SpecLAMOST
spec = SpecLAMOST('input_file.fits')
spec.plot()
# Smooth the spectrum
spec.smooth(5, 3, inplace=False)from specbox.basemodule import SpecSparcl
# ext is a 1-based row index (ext=1 -> first row)
sp1 = SpecSparcl("sparcl_spectra.parquet", ext=1)
sp1.plot()Default SPARCL parquet files with a scalar redshift column initialize
sp1.redshift and the viewer startup redshift sp1.z_vi from that value.
If redshift is missing or non-finite, SpecSparcl falls back to positive
finite z_desi, z_sdss, z_ref, then z.
from specbox.basemodule import SpecEuclid1d
sp = SpecEuclid1d("sz_ragn_dr1_rgs_chunk_001_part001.parquet", ext=1)
sp.plot()Euclid parquet rows may use raw archive columns (wavelength, flux or signal,
var, mask, quality, ndith) or processed spectra columns. Processed files
can include redshift candidates; viewer startup uses the first positive finite
value in z_vi > z_sdss > z_desi > z_hybrid > z_fusion > z_temp > z_pcf_best > z_gaia > z_phot. z_temp and z_pcf_best are treated as aliases, with
z_temp preferred when both are present.
Because raw Euclid archive parquet files do not record the numeric flux scale,
SpecEuclid1d scales flux/signal and err by
1e-16 erg/s/cm^2/Angstrom by default. Variance has squared flux units and
inverse variance has inverse-squared units; uncertainty is derived before
applying the flux scale. Add a positive scalar
flux_scale column (signal_scale, fscale, or FSCALE also work) to override
that default for files that already store physical flux values or use a different
scale.
specbox-viewer \
--spectra coadd/sz_ragn_dr1_coadd_chunk_001_part001.parquet \
--spec-class euclid-coaddspecbox-viewer \
--rgs-file dual_001_rgs.parquet \
--bgs-file dual_001_bgs.parquet \
--spec-class euclid \
--z-max 6.5 \
--no-images \
--output-file dual_001_vi_results.csvDual-arm mode opens the paired RGS and BGS spectra directly; it does not
require or create a coadd. Parquet matching uses the first column present in
both tables from source_id, object_id, extname, objid. The union of
objects is inspected, including objects with only one available arm. Existing
output history loads automatically; the starting index follows its row count,
so inspect sequentially and check the index when resuming out-of-order work.
Images and cutout downloads are off by default. Use --images to opt in, or --no-images for an explicit image-off command line.
For sparcl and aimsz-review, the viewer now plots raw spectra by default. Use the Downsample toolbar toggle to enable pyqtgraph native downsampling and draw a black downsampled trace on top.
For dual-arm Euclid parquet inputs, matching uses source identity as described above.
When --redshift-table is provided, the viewer loads the external table once at startup and stores the matched value as z_ref; this remains an external overlay and is not part of the processed Euclid parquet priority list.
from specbox import SpecLAMOST
from specbox.qtmodule import PGSpecPlotThread
from glob import glob
basepath = 'lamost_spec/fits_files/'
flist = glob(basepath+'*fits.gz')
flist.sort()
flist = flist[0:60]
a = PGSpecPlotThread(speclist=flist, SpecClass=SpecLAMOST, output_file='vi_output_test60.csv')
a.run()from specbox.basemodule import SpecSparcl
from specbox.qtmodule import PGSpecPlotThreadEnhanced
viewer = PGSpecPlotThreadEnhanced(
spectra="sparcl_spectra.parquet",
SpecClass=SpecSparcl,
# Optional: overlay Euclid spectrum when the parquet has `euclid_object_id`
# and the Euclid combined FITS uses that ID as `EXTNAME`.
euclid_fits="COMBINED_EUCLID_SPECS.fits",
output_file="sparcl_vi_results.csv",
z_max=6.0,
load_history=True,
)
viewer.run()Notes:
- Results CSV includes
targetidanddata_release(when available from the input table). - The enhanced viewer has a
Save PNGbutton that writes screenshots to./saved_pngs/.
