csiio
Python reader and converter for Campbell Scientific Inc data files either OOP or functional.
This package focuses on practical ingestion and conversion workflows for CSI formats (including TOA5, TOACI1, TOB1, TOB3, and CSIXML), with DataFrame-native handling for downstream analysis pipelines.
Citation: If you use csiio in published work and want to cite it, see the citation guidance in How To Cite.
Install
pip install -e .
Core Functionality
- Read CSI files into pandas DataFrames.
- Auto-detect file type during reads.
- Convert between supported CSI formats.
- Export single CSV outputs or time-window-split CSVs via
CSIDataFile.write(..., "CSV")orcsiio convert --output-format CSV. - Use either CLI workflows or Python API workflows.
Note: When using split_window functionality the start and endtimestamp is added as part of the ilename. Due to potentially exist_action checks it is good practice to instead of passing the full outfile to write/convert to pass a folder with fileprefix. Within the processing chain, the filename is split into folder, filename and extension and the output_format is added in front of the filename and the STARTDATE_STARTTIME_ENDDATE_ENDTIME are added after in the format YYYYmmdd_HHMMSS_YYYYmmdd_HHMMSS floored/ceiled to the split window frequency.
Examples: DATAFILE.dat becomes TOB1_DATAFILE_20230101_000000_20230102_000000.dat (and more if more data are available if split_window='1D') DATAFILE.dat becomes TOB1_DATAFILE_20230101_000000_20230101_003000.dat (and more if more data are available if split_window='30Min')Changelog Policy
- Release notes live in
CHANGELOG.md. - Add user-visible changes under
[Unreleased]in the section that fits best:Added,Changed,Fixed,CI/Build, orDocs. - On release, run
python scripts/release_changelog.py 0.2.0to move[Unreleased]into a dated version section, then tag the release. - Use
python scripts/release_changelog.py 0.2.0 --dry-runto preview the edit without writing.
Python API Usage
from csiio import CSIDataFile, read_csi_files, convert_csi_file
import pandas as pd
# High-level object workflow
reader = CSIDataFile(["a.dat", "b.dat"])
df = reader.read()
normalized_meta = reader.meta
per_file_meta = reader.file_meta
csv_files = reader.write("/tmp/out.csv", "CSV", split_window="1h")
# Functional workflow
df2, meta = read_csi_files("/path/to/file.dat")
df_many, meta_many = read_csi_files(["/path/to/a.dat", "/path/to/b.dat"], max_workers=2)
converted = convert_csi_file("/path/to/in.dat", "/tmp/TOA5_out.dat", "TOA5")
converted_merge = convert_csi_file(
"/path/to/in.dat",
"/tmp/TOA5_out.dat",
"TOA5",
exists_action="merge",
line_terminator="\n",
)
split_outputs = convert_csi_file(
"/path/to/in.dat",
"/tmp/TOA5_out.dat",
"TOA5",
split_window="1h",
closed="right",
label="right",
)
# Each output file includes the chunk start/end timestamps, e.g.:
# TOA5_out_20240101T000000_20240101T010000.dat
split_outputs_limited = convert_csi_file(
"/path/to/in.dat",
"/tmp/TOA5_out.dat",
"TOA5",
split_window="1h",
closed="right",
label="right",
max_workers=2
)
# Use `closed` and `label` to control how time windows are grouped and labeled.
# `closed` determines which side of the interval is inclusive, and `label` selects the timestamp used for the group.
# Initialize from an existing pandas DataFrame
frame = pd.DataFrame(
{"air_temp (degC)": [12.1, 12.4, 12.8]},
index=pd.date_range("2024-01-01 00:00:00", periods=3, freq="30min"),
)
from_df = CSIDataFile(data=frame)
csv_files = from_df.write("/tmp/out.csv", "CSV")
split_csv_files = from_df.write(
"/tmp/out.csv",
"CSV",
split_window="1h",
closed="right",
label="right",
line_terminator="\n",
max_workers=2,
)
converted_file = from_df.write("/tmp/out.dat", "TOB3", max_workers=2)
converted_file_merge = from_df.write(
"/tmp/out.dat",
"TOB3",
exists_action="merge",
)
CLI (command line) Usage
csiio --help
# Read and print DataFrame summary
csiio read /path/to/file.dat
# Read metadata only, useful to figure out contained columns
csiio read /path/to/file.dat --meta-only
# Stream CSV to stdout (good for shell pipelines)
csiio read /path/to/file.dat --as-csv
# Read many files with explicit worker limit
csiio read /path/to/a.dat /path/to/b.dat --max-workers 2
# Convert to another CSI format
csiio convert /path/to/in.dat --output-format TOB3 --output /tmp/TOB3_out.dat
# Convert to another CSI format and split by timewindow, useful for e.g. EC processing
# Output files are created per chunk with timestamps in the filename, e.g.
# TOA5_out_20240101T000000_20240101T010000.dat
csiio convert /path/to/in.dat --output-format TOB1 --split-window 1h --output /tmp/TOB1_out.dat
# Split conversion with explicit worker limit
csiio convert /path/to/in.dat --output-format TOB1 --split-window 1h --output /tmp/TOB1_out.dat --max-workers 2
# The CLI also supports interval grouping with closed/label semantics:
# csiio convert /path/to/in.dat --output-format TOB1 --split-window 1h --closed right --label right --output /tmp/TOB1_out.dat
# When the output file already exists, choose merge, overwrite, or skip behavior
csiio convert /path/to/in.dat --output-format TOB1 --output /tmp/TOB1_out.dat --exists-action merge
# Output conflict behavior for convert
# `merge` adds new rows and deduplicates on timestamp, `overwrite` replaces the existing output,
# and `skip` leaves existing files untouched.
csiio convert /path/to/in.dat --output-format TOB1 --output /tmp/TOB1_out.dat --exists-action skip
# Export CSV and split by time window where time window is one of pandas known frequency strings found at https://pandas.pydata.org/docs/user_guide/timeseries.html#dateoffset-objects
csiio convert /path/to/in.dat --output-format CSV --output /tmp/out.csv --split-window 1h
# Split CSV export with explicit worker limit
csiio convert /path/to/in.dat --output-format CSV --output /tmp/out.csv --split-window 1h --max-workers 2
Typical Use Cases and Functionality
Supported File Formats
csiio is designed to work with the common Campbell Scientific formats where for most 4 header lines are available describing the logger (l1), the columnname (l2), the unit (l3) and the aggregation (l4):
| Format | Description | Pro | Con |
|---|---|---|---|
| TOA5 | ASCII file | Human-readable | file size |
| TOACI1 | TOA5 with shorter header | Human-readable | file size |
| TOB1 | Binary file with one timestamp per line | File size small | binary |
| TOB3 | Binary file with 4 header lines with record frames to ensure integrity; native format of CSI loggers | Integrity | binary and complex |
| CSIXML | XML-based format | More descriptive | tree-structure not that useful for many data-related tasks |
Main Use Cases
1) Read CSI Data to DataFrame
Use this when you need immediate pandas-based analysis from Python.
The CLI shows a summary by default; use --as-csv if you want the data on stdout.
CLI summary:
csiio read /path/to/file.dat
CLI data stream:
csiio read /path/to/file.dat --as-csv
Python:
from csiio import read_csi_files
# read one file
data, meta = read_csi_files("/path/to/file.dat")
# read many files
data, meta = read_csi_files(["/path/to/file.dat", "/path/to/another/file.dat"])
# read only selected columns
data, meta = read_csi_files(
"/path/to/file.dat",
columns=["air_temp (degC)", "co2_flux (umol m-2 s-1)"],
)
If none of the requested columns exist in the file, the reader raises a `ValueError`.
When reading multiple files, if at least one file contains some requested columns, any files with no matching columns are logged as warnings and the remaining data is still returned.
Outcome:
- Data is returned as a pandas DataFrame.
read_csi_files(...)returns per-file metadata alongside the DataFrame.CSIDataFile.metastores normalized metadata for the combined in-memory DataFrame.CSIDataFile.file_metastores per-file metadata as a dictionary keyed by filename.
2) Read Metadata Only
Use this when validating variable names or file headers without loading full data payloads.
CLI:
csiio read /path/to/file.dat --meta-only
Python:
from csiio import read_csi_files
meta = read_csi_files("/path/to/file.dat", meta_only=True)
When using CSIDataFile, read(meta_only=True) updates:
reader.metawith normalized metadata for the current in-memory viewreader.file_metawith per-file metadata keyed by filename
3) Convert to another CSI format
Use this when standardizing logger output or generating downstream-compatible files.
CLI:
csiio convert /path/to/in.dat --output-format TOB3 --output /tmp/out.dat
Python:
from csiio import convert_csi_file
out = convert_csi_file("/path/to/in.dat", "/tmp/out.dat", "TOB3")
4) Convert to another CSI format and split by frequency
Use this when standardizing logger output or generating downstream-compatible files.
CLI:
csiio convert /path/to/in.dat --output-format TOB3 --output /tmp/out.dat --split-window 1h
Python:
from csiio import convert_csi_file
outputs = convert_csi_file("/path/to/in.dat", "/tmp/out.dat", "TOB3", split_window="1h")
# Limit split-window writer threads
outputs_limited = convert_csi_file(
"/path/to/in.dat", "/tmp/out.dat", "TOB3", split_window="1h", max_workers=2
)
5) Export CSV Outputs
Use this when delivering data to tools that expect CSV.
Single-file export (Python):
from csiio import CSIDataFile
reader = CSIDataFile("/path/to/in.dat")
reader.read()
outputs = reader.write("/tmp/out.csv", "CSV")
Time-window split export (Python):
outputs = reader.write("/tmp/out.csv", "CSV", split_window="1h")
# Limit split-window writer threads
outputs_limited = reader.write("/tmp/out.csv", "CSV", split_window="1h", max_workers=2)
CLI equivalent:
csiio convert /path/to/in.dat --output-format CSV --output /tmp/out.csv --split-window 1h
6) Initialize from a pandas DataFrame
Use this when your data is already in pandas and you want to export or convert it without first writing an intermediate CSI file.
Python:
import pandas as pd
from csiio import CSIDataFile
frame = pd.DataFrame(
{
"air_temp (degC)": [12.1, 12.4, 12.8],
"co2_flux (umol m-2 s-1)": [0.01, -0.02, 0.03],
},
index=pd.date_range("2024-01-01 00:00:00", periods=3, freq="30min"),
)
reader = CSIDataFile(data=frame)
# Export CSV directly
csv_files = reader.write("/tmp/out.csv", "CSV")
# Export split CSV files
split_csv_files = reader.write("/tmp/out.csv", "CSV", split_window="1h")
# Convert directly to a CSI format
tob3_file = reader.write("/tmp/out.dat", "TOB3")
Outcome:
CSIDataFilenormalizes the DataFrame to aTIMESTAMPindex and addsRECORD (RN)when missing.reader.metais auto-generated from the DataFrame columns.- You can export CSV or CSI files directly from the in-memory DataFrame.
Parallelism Controls
- Parallel workers are enabled by default for:
- reading multiple files via list input
- split-window conversion writes
- split-window CSV writes
- Default worker count is
max(1, cpu_count // 4), capped by number of tasks. - You can override with
max_workersin Python API calls and--max-workersin CLI commands. - Safety check:
max_workersmust be an integer between1and available CPU count.
7) Stream Data Through Shell Pipelines
Use this when integrating with command-line tooling.
csiio read /path/to/in.dat --as-csv | gzip > out.csv.gz
API Surface Summary
The package currently exposes:
Instance
- CSIDataFile
CSIDataFile.write(output_file, output_format, ...)
Instance / Reading
- read_csi_files
- read_csi_meta
- read_csi_toa5
- read_csi_tob1
- read_csi_tob3
- read_csi_csixml
Conversion
- convert_csi_file
Writing
- write_csi_ascii
- write_csi_tob1
- write_csi_tob3
- write_csi_csixml
Notes and Constraints
- Pandas dataframe are cnetral, csiio keeps its data internally as dataframe giving users the ease of use of pandas
- The main purpose of csiio is the reader functionality for the binary, frame-oriented TOB3 format (which may be slower than proprietary tooling for very large files due to frame-level handling in Python.) and the utility to directly convert in a pipeline instead of via cardconvert
How To Cite
If csiio supports published work, and you have the opportunity to cite it you may cite the software repository and version you used.
Suggested BibTeX entry:
@software{csiio,
title = {csiio: Reader and Converter for Campbell Scientific Data Files},
author = {Spirig, Robert},
year = {2026},
url = {https://github.com/spirrobe/csiio},
version = {0.1.0}
}
If relevant/available for your workflow, include a commit hash or release tag for exact reproducibility.
Testing
Generate dual-status conversion audits (strict plus tolerance) with reason codes:
python tests/generate_conversion_audit.py
Outputs are written to:
tests/reports/conversion_audit_dual.csvtests/reports/conversion_audit_dual_summary.csv
Metadata
Release files for csiio-py 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| csiio_py-0.4.0.tar.gz | 30.4 kB | With provenance | Details for csiio_py-0.4.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| csiio_py-0.4.0-py3-none-any.whl | Python 3 | none | any | Details for csiio_py-0.4.0-py3-none-any.whl |
Total release size: 58.1 kB
Release files / csiio_py-0.4.0.tar.gz
| Download URL | csiio_py-0.4.0.tar.gz |
|---|---|
| Size | 30.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c2d249533eeb58dec720cdb6e5de7b2e65329b2b93b597752a6f8efd64f57d00
|
|
BLAKE2b-256 checksum How to use checksums |
88d1a76f9677cbb28f9efe3f892bcad7176de3d5b86a99debb8a2545197fa439
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.
Transparency logRelease files / csiio_py-0.4.0-py3-none-any.whl
| Download URL | csiio_py-0.4.0-py3-none-any.whl |
|---|---|
| Size | 27.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
75fbe7811c0f71f7f7ef9372f7156ba487b2f585859fc05c3daa3e47fd803de6
|
|
BLAKE2b-256 checksum How to use checksums |
9567a513dbf0a54fd21ebe21830d8aa983085144cca9a20381608614586920ef
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.
Transparency logSecurity for csiio-py 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for csiio_py-0.4.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.