Input & output¶
Reading ring-width and chronology files, inspecting metadata, and writing data back out. See the Reading & writing data guide for worked examples.
readers¶
Read a common ring-width data file (.csv, .rwl) into a pandas DataFrame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
str or path - like
|
a data file (.CSV, .RWL or .RAW). May be a local path (str or
|
required |
header
|
bool or None
|
whether a site-metadata header sits at the top of the file. |
None
|
skip_lines
|
int
|
how many of the first PHYSICAL lines of the file to skip before anything
else (blank and comment lines count). The skipped lines are recorded on
|
0
|
format
|
(None, tucson, csv, tridas, pos)
|
force the file format regardless of suffix (case-insensitive). |
None
|
strict
|
bool
|
|
True
|
join
|
bool
|
|
True
|
encoding
|
str or None
|
character encoding. |
None
|
verbose
|
bool
|
print the one-line success summary (file, format, series count, period) and the report of merged/split duplicate IDs. Advisories about the file (a truncated value, a short interior gap, case-variant IDs, an assumed encoding) are always issued as warnings, never printed. |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
data |
pandas dataframe
|
Year-indexed ring widths, |
Examples:
>>> import dplpy as dpl
>>> data = dpl.readers("../tests/data/csv/file.csv")
>>> data = dpl.readers("../tests/data/rwl/file.rwl") # header auto-detected
>>> data = dpl.readers("https://www.ncei.noaa.gov/pub/data/paleo/treering/"
... "measurements/northamerica/usa/ak132x.rwl") # read from a URL
References
.. [1] https://opendendro.github.io/dplPy/reference/io/#readers
readers_url¶
Read a Tucson (.rwl) file directly from a URL.
Deprecated: readers() accepts an http(s) URL directly, so
dpl.readers(url) does the same thing. This thin wrapper is kept for
backward compatibility: it emits a DeprecationWarning and forwards every
keyword (header, skip_lines, strict, join, encoding,
format, verbose) to readers.
metadata¶
Extract site/sample metadata from a Tucson (.rwl) file's header.
Returns a dict with best-effort fields (site_id, site_name, species_code,
species_name, country_region, elevation_m, latitude, longitude, first_year,
last_year, investigators) plus n_header_lines and the raw header lines.
Unreadable fields are None. Reads only the header, so it is cheap and
independent of loading the data; accepts a local path (str or pathlib.Path)
or an http(s) URL. header and skip_lines mean what they mean in
readers() (skip_lines counts physical lines). The same result is
captured at read time on df.attrs['dplpy_metadata'].
read_crn¶
Read a Tucson (.crn) chronology file into a DataFrame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
str or path - like
|
Path (or http/https URL) to a .crn chronology file. Standard single chronologies and combined multi-block files (stacked ARSTAN types, or many sites concatenated) are both handled, as are BC chronologies in dplR's layout (a 5-character site ID with the year's sign in column 6). |
required |
strict
|
bool
|
|
True
|
split_by_site
|
bool
|
If True, return a dict mapping each site ID to its own DataFrame (each
framed as a single-site file: value columns named by chronology type
plus a shared |
False
|
verbose
|
bool
|
print the one-line success summary. Advisories are warnings regardless. |
True
|
Returns:
| Type | Description |
|---|---|
pandas.DataFrame or dict of pandas.DataFrame
|
By default one DataFrame indexed by year: one value column per
chronology (named by chronology type for a single-site file, e.g.
'std'/'res'/'ars'; by site, or 'site.type', for a multi-site file),
index values divided by 1000 and 9990 mapped to NaN, with sample depths
as 'samp_depth' (shared when several chronologies carry identical depth)
or ' |
Examples:
>>> dpl.read_crn("ga004.crn")
>>> dpl.read_crn("celaque_prelim_v01.rwl_crns.txt")
>>> by_site = dpl.read_crn("ALL.CRNS", split_by_site=True) # {site: DataFrame}
References
.. [1] https://opendendro.github.io/dplPy/reference/io/#read_crn
read_ids¶
Derive tree and core IDs from series names (site-tree-core convention).
Split each series name into a tree identifier (site + tree number, so trees and
sites never collide) and a core designator, and return the mapping
dpl.rwi_stats() and dpl.sss() use to know which cores belong to the same tree.
Two parsing modes: the pattern default (stc=None) splits on letter/digit
boundaries (leading letters = site, first digit run = tree, trailing characters
= core); an STC mask (stc=(site_len, tree_len[, core_len])) splits by fixed
character positions.
IMPORTANT: the pattern default cannot detect a DIGIT core. A name like "CAM031"
is read as tree "031" with an empty core (no warning), leaving "CAM031" and
"CAM032" as separate trees instead of grouping them under "CAM03". For digit
cores, or site codes that contain digits, supply an explicit stc mask (e.g.
stc=(3, 2, 1) for "CAM031").
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
pandas dataframe or iterable of str
|
a dataframe whose column names are the series names (e.g. from dpl.readers()), or any iterable of series-name strings. |
required |
stc
|
tuple of int, or None
|
None to use the pattern default; otherwise a (site_len, tree_len) or (site_len, tree_len, core_len) character mask. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
ids |
pandas dataframe indexed by series name, with columns 'tree' and
|
'core'. Pass this directly as the |
Examples:
>>> import dplpy as dpl
>>> data = dpl.readers("../tests/data/csv/file.csv")
>>> ids = dpl.read_ids(data) # letter-core names
>>> ids = dpl.read_ids(data, stc=(3, 2, 1)) # digit-core names (e.g. CAM031)
>>> dpl.rwi_stats(dpl.detrend(data, plot=False), ids=ids)
References
.. [1] https://rdrr.io/cran/dplR/man/read.ids.html
combine_rwl¶
Combine ring-width datasets on the union of their years (dplR combine.rwl).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
pandas.DataFrame or list/tuple of DataFrames
|
Either the first dataset (with |
required |
y
|
DataFrame
|
The second dataset, when |
None
|
warn_duplicates
|
bool
|
Emit a warning if the combined frame ends up with duplicate series names (the columns are still kept, matching dplR -- this only flags them). |
True
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
All series side by side, indexed by a contiguous year range spanning the
earliest to the latest year of the inputs (years with no data are NaN).
|
Notes
Series columns are concatenated as-is: like dplR, duplicate series IDs are neither merged nor renamed. Deduplicate or rename beforehand if that matters for your workflow.
writers¶
Output dplpy datasets to .csv, .rwl, .crn, .txt and .tridas (.xml) files.
Write a dataframe to a file as set by format, created in the current
directory unless a path is included in label. For 'csv'/'rwl', data
is a ring-width dataframe (years x series); for 'crn', data is a
chronology (from dpl.chron()) and a header of site metadata is
REQUIRED to populate the Tucson .crn header.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
pandas dataframe
|
ring widths (csv/rwl) or a chronology from dpl.chron() (crn). |
required |
label
|
str
|
name (can include file path) to give the file; no extension. |
required |
format
|
str
|
'csv', 'rwl', 'crn', 'txt' or 'tridas' (case-insensitive). |
required |
header
|
dict or SiteMetadata, required for format='crn'
|
site metadata with the keys: 'site_id', 'site_name', 'species_code', 'state_country', 'species', 'elevation', 'latitude', 'longitude', 'investigators'; optionally 'completion_date'. First/last year are taken from the chronology. Coordinates may be decimal degrees (packed to the ITRDB DDMM/DDDMM field) or already-packed strings; elevation in metres. |
None
|
chronology_type
|
str
|
for format='crn', the ITRDB chronology type written in header record 2: 'standard' (blank code), 'arstan' ('A'), or 'residual' ('R'). |
'standard'
|
column
|
str
|
for format='crn', which chronology column to write (e.g. 'std', 'res'). |
'std'
|
prec
|
float or None
|
for format='rwl'/'tridas', the measurement precision in mm. |
None
|
gaps
|
(int, split)
|
for format='rwl', how to encode a true interior gap (a NaN inside a series; a real 0 is a locally absent ring and is always written as 0): a negative integer (default -99) writes that sentinel in the gap (it must be negative so it is not read as a ring width, and not a stop marker; the reader turns it back into NaN), or "split" closes the block with the end marker and reopens a new block at the next present year. |
int
|
sep
|
for format='txt', the column delimiter (default tab), printf float format
(default '%.4f', None for full precision), and missing-value text (default
'NA'). For 'txt', |
'\t'
|
|
float_format
|
for format='txt', the column delimiter (default tab), printf float format
(default '%.4f', None for full precision), and missing-value text (default
'NA'). For 'txt', |
'\t'
|
|
na_rep
|
for format='txt', the column delimiter (default tab), printf float format
(default '%.4f', None for full precision), and missing-value text (default
'NA'). For 'txt', |
'\t'
|
Returns:
| Type | Description |
|---|---|
str
|
the path of the file written. |
References
.. [1] https://opendendro.github.io/dplPy/reference/io/#writers
summary¶
Summarize a ring-width dataset.
Reports per-series summary information for a dataframe of tree-ring widths.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inp
|
str or pandas dataframe
|
a data file (.CSV or .RWL) or a pandas dataframe imported from dpl.readers(). |
required |
Returns:
| Name | Type | Description |
|---|---|---|
data |
pandas dataframe
|
|
Examples:
References
.. [1] https://opendendro.github.io/dplPy/reference/io/#summary
report¶
Generates a report
Extended Summary
Generates a text report about the input dataset that includes: Number of dated series Number of measurements Avg series length (years) Range (total years) Span (start-end year) Mean (Standard Deviation) series intercorrelation Mean (Standard Deviation) AR1 Years with locally absent rings (zero values), with per-series and total counts Years with internal NA (missing measurements), with per-series and total counts
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inp
|
str or pandas dataframe
|
a data file (.CSV or .RWL) or a pandas dataframe imported from dpl.readers(). |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
the per-series statistics table the report is built from (the output of
|
Examples:
References
.. [1] https://opendendro.github.io/dplPy/reference/io/#report
check¶
Check a ring-width collection for defects, reported as a table of findings.
The data counterpart to report(): report prints what a file looks
like, check returns what is wrong with it, one row per finding. Every
finding carries a stable check id and a severity ("error", "warning",
"note"). Analog of dplR 1.8.0's rwl.check.
The crossdating group slides the leave-one-out master past each series over
-lag..+lag (xdate._lag_table()), so a RWL_DATING_LAG with a
NEGATIVE lag means a MISSING ring in the series, as in COFECHA, xdate and
series_corr.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
pandas.DataFrame, str, or list of str
|
a ring-width collection ( |
required |
file
|
str or None
|
label for the |
None
|
checks
|
list of str or None
|
which groups to run. Default all: structure, series, values, zeros, crossdating, file, provenance. |
None
|
summary
|
bool
|
if True, return one row per file (counts per check and per severity) instead
of the findings. |
False
|
min_depth
|
int
|
|
2
|
depth_run
|
int
|
|
2
|
min_length
|
int
|
|
2
|
run_min
|
int
|
|
2
|
lag
|
int
|
|
2
|
min_overlap
|
int
|
|
2
|
spline_period
|
int
|
|
2
|
r_dating
|
float
|
|
0.35
|
r_margin
|
float
|
|
0.35
|
outlier_mad
|
float
|
|
0.35
|
outlier_gap
|
float
|
|
0.35
|
r_cohesion
|
float
|
|
0.35
|
plausible_mean
|
float
|
|
0.35
|
small_thresh
|
Optional[float]
|
tunables; defaults follow dplR's |
None
|
big_thresh
|
Optional[float]
|
tunables; defaults follow dplR's |
None
|
max_year
|
Optional[float]
|
tunables; defaults follow dplR's |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
the findings (columns |
Notes
Every check group runs inside a guard: an exception inside a group (or in
reading the file) is converted to a RWL_CHECK_ERROR finding carrying the
error text, so one failing check never hides the others' findings and a sweep
over an archive never stops on one bad file (audit G4). Check the engine
group of the result to see whether anything failed to run. A misdated series
may draw both RWL_DATING_LAG and RWL_SERIES_OUTLIER (its lag-0
correlation is low because of the lag); both are kept.
References
.. [1] Bunn, A. G. (2026) dplR 1.8.0, rwl.check.
SiteMetadata¶
Site/sample metadata shared by dplPy's file exporters.
Field names mirror dpl.metadata() / df.attrs['dplpy_metadata']. Use
from_rwl() to auto-fill from a frame read by dpl.readers(),
from_metadata() from a metadata dict, or construct one directly and set
fields by hand. to_crn_header() renders the dict the Tucson .crn
writer expects (which uses a few different key names). Coordinates are
decimal degrees and elevation is metres; the .crn writer packs them into
the ITRDB fields (DDMM / DDDMM, "%dM") itself.
from_metadata
classmethod
¶
Build from a metadata dict (dpl.metadata() output or the same
shape). Unknown keys (e.g. n_header_lines) are ignored.
from_rwl
classmethod
¶
Build from a frame read by dpl.readers() (uses
df.attrs['dplpy_metadata']), or from a metadata dict directly.
coerce
classmethod
¶
Return obj if it is already a SiteMetadata, or build one from a
metadata dict. Used by writers so they accept either form.
to_crn_header ¶
Render the header dict the Tucson .crn writer expects. The writer
uses a few different key names (state_country/species/
elevation); this maps them. Values are passed through unchanged
(decimal degrees, metres); write_crn packs them into the ITRDB
fields via pack_crn_coordinates() / pack_crn_elevation(). Fields that
are None are omitted, so the writer's own "missing required key" check
reports what still needs filling rather than writing the literal 'None'.
read_tridas¶
Read a TRiDaS (Tree Ring Data Standard) XML file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
str
|
path to a |
required |
warn_units
|
bool
|
warn when a measurement series uses a variable that is not a width in a length unit (e.g. a density), which dplPy does not convert to mm. Such series are kept out of the ring-width tables. |
True
|
Returns:
| Type | Description |
|---|---|
TridasProject
|
A typed container: |
Notes
Missing values become NaN (not 0); units are converted to millimetres;
years use the dplPy continuous integer axis. See
dev/tridas_design_2026-10-14.md for the full design.
References
.. [1] Jansma, Brewer & Zandhuis (2010), Dendrochronologia 28, 99-130.
write_tridas¶
Write a TRiDaS 1.2.2 file from ring widths and/or a chronology.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fname
|
str
|
output path. |
required |
rwl
|
pandas DataFrame
|
ring widths (Year-indexed, series in columns), as from dpl.readers(). Written as measurementSeries under one object (site). |
None
|
crn
|
pandas DataFrame
|
a chronology from dpl.chron(); columns named |
None
|
metadata
|
TridasMetadata or dict
|
project/site metadata. Mandatory fields missing with |
None
|
prec
|
float
|
measurement precision in mm (0.001, 0.01, 0.02, 0.05, 0.1, 1, 10, 1000).
|
None
|
ids
|
pandas DataFrame
|
tree/core[/radius/measurement] grouping, as from dpl.read_ids(). Default: each series is its own element (tree). |
None
|
taxon
|
str
|
taxon for the elements. Mandatory per the standard when writing rwl. |
None
|
variable
|
str
|
the measured variable (controlled vocabulary). |
"ring width"
|
measuring_method
|
str
|
free-text measuring method (mandatory per the standard). |
"unknown"
|
validate
|
bool
|
enforce the mandatory-field set. |
True
|
Returns:
| Type | Description |
|---|---|
str
|
the path written. |
TridasProject¶
Parsed TRiDaS file.
Attributes:
| Name | Type | Description |
|---|---|---|
measurements |
dict of str -> DataFrame
|
Dated measurement series grouped into ring-width tables, keyed by
|
derived |
dict of str -> DataFrame
|
Chronologies (TRiDaS derivedSeries), keyed by series title. Each frame is
Year-indexed with the chronology column and, when the file records per
value counts, a |
undated |
dict of str -> Series
|
Measurement series with no calendar years (floating series), keyed by series id. Position-indexed; feed these to dpl.xdate_floater(). |
metadata |
TridasMetadata
|
The full metadata hierarchy. |
rwl ¶
Return the single ring-width table, or raise if the file holds more
than one measurement table (then use measurements).
TridasMetadata¶
The project/object/element/sample/radius/series metadata of a TRiDaS file.
Project-level fields are named (they are the mandatory ones in Brewer's list
and what a .crn-style export needs). The object hierarchy is kept as nested
dicts under objects so the whole tree stays navigable without a deep
class hierarchy. laboratories and extra capture the rest.
read_pos¶
Read CooRecorder .pos file(s) into a PosCollection.
path may be a single .pos file or a directory tree (every .pos under it is read and stacked into one ring-width frame). Series are named by file stem.
PosCollection¶
Result of read_pos(): whole-ring widths plus the pieces a .pos file carries that a Tucson .rwl drops. Mirrors TridasProject's role for TRiDaS.