Skip to content

Crossdating

Statistical crossdating against a master chronology, COFECHA-style batch reporting, floating-series dating, and relative dating of undated collections. See the Crossdating & COFECHA and Dating a floating series guides.

xdate

Crossdate a set of ring-width series against a leave-one-out master.

Each series is normalized, optionally AR-prewhitened, and correlated against a biweight master built from all the other series, over segments of window_length years overlapping by half. Mirrors dplR's corr.rwl.seg.

Flagged segments are labelled A or B (mutually exclusive):

  • B -- the segment correlates better at a non-dated lag (best lag != 0): a possible dating shift. (dplR flags on significance only; dplPy adds this COFECHA-style lag flag, also used by preset="COFECHA".)
  • A -- lag 0 is the best match but the correlation is not significant (one-tailed p >= p_val): weak but correctly placed.

Lag sign: the dated segment stays fixed and the master slides past it, so a negative lag means a missing ring in the series (as in COFECHA). The same convention holds in series_corr and check.

Parameters:

Name Type Description Default
data DataFrame

detrended RWI (e.g. from dpl.detrend). With preset="COFECHA" pass the RAW ring-width frame instead; the preset detrends internally, so do not pre-detrend.

required
prewhiten bool

AR-prewhiten each series (autoreg._prewhiten: Yule-Walker, order by the first local AIC minimum up to ar_max; the first order years of a series become NaN as in dplR).

True
corr (spearman, pearson, kendall)

correlation method (case-insensitive).

"spearman"
window_length int

segment length in years (slide_period= is a deprecated alias).

50
bin_floor int

the first segment is floored to a multiple of this.

100
p_val float

significance level for the A flag.

0.05
biweight bool

build the master with a Tukey biweight robust mean (else arithmetic).

True
lag int

maximum ± lag examined for the B flag.

10
show_flags bool

print the summary box, the flag summary and lag tables.

True
plot bool

draw the segment-correlation plot.

False
preset str or None

"COFECHA" (case-insensitive) emulates the COFECHA program instead of dplR. Pass the RAW frame (the preset detrends and variance-stabilizes it itself): it uses Pearson correlation, a z-scored arithmetic leave-one-out master, 50-year segments on a 25-year grid anchored to each series' span, a 99% one-tailed-t critical value, Burg AR prewhitening, and a lag search that slides the master past the dated segment. Adds the segments key; corr, biweight, bin_floor and p_val are ignored.

None
seg_lag int or None

segment step in years (overlap). None uses window_length // 2. Preset only.

None
ar_max int or None

AR-order ceiling for prewhitening (ARSTAN/COFECHA's 10). None uses dplR's floor(10*log10(n)).

10
first_aic_min bool

pick the AR order at the first local AIC minimum (the ARSTAN/COFECHA rule, dplPy's package default) rather than the global minimum. ar_max=None, first_aic_min=False reproduces dplR's corr.rwl.seg prewhitening exactly. Ignored by the COFECHA preset (always first-minimum, Burg).

True
absent DataFrame or None

COFECHA preset only -- marks locally absent rings to omit from a series' correlations. A boolean (years x series) frame, or a raw ring-width frame whose zeros mark them (e.g. absent=raw_rwl == 0). None disables the omission.

None
spline_period int

COFECHA preset only -- spline wavelength (years) for the internal detrend and variance stabilization (stabilize_period= is a deprecated alias).

32

Returns:

Type Description
dict

with keys:

  • seg_corr -- DataFrame (series x bins) of segment correlations
  • p_val -- DataFrame (series x bins) of one-tailed p-values
  • overall -- DataFrame (series x ['rho', 'p_val']), the correlation with the leave-one-out master
  • avg_seg_corr -- Series (bins), mean correlation across series
  • flags -- dict {series: {'A': [...], 'B': [...]}}, each entry a dict {segment, best_lag, best_corr, lags} where lags is the list of correlations at lags -lag..+lag (floats, NaN where untestable)
  • bins -- list of "start-end" bin labels
  • rwi -- DataFrame of the normalized/prewhitened series used (attrs["kind"] == "rwi")
  • n_problems -- int, the number of flagged segments (A + B)
  • summary -- dict, the header-box statistics (n_series, first_year, last_year, span, total_rings, dated_rings_checked, intercorrelation, mean_sensitivity, n_problems, mean_series_length)

With preset="COFECHA" also:

  • segments -- dict {series: [{lo, hi, r0, best_lag, best_corr, n, crit, flag, lags}]}
  • preset -- "COFECHA"

Examples:

>>> rwi = dpl.detrend(ca533, fit="spline", plot=False)
>>> res = dpl.xdate(rwi)
>>> res = dpl.xdate(rwl, preset="COFECHA")   # pass the RAW frame
>>> res["n_problems"]
References

.. [1] https://opendendro.github.io/dplPy/reference/crossdating/#xdate

xdate_report

Generate a COFECHA-style crossdating QA report for one or more .rwl files.

For each file: read (salvage mode), detrend, cross-date with dpl.xdate, collate per-series statistics, and print the report and/or save it as <name>.txt in out_dir (see output). Built for batch QA of ITRDB submissions. Pass preset="COFECHA" to emulate the COFECHA program instead (Burg prewhitening, spline variance stabilization, an arithmetic z-scored master, Pearson correlation, COFECHA segment anchoring and critical value, and "omit absent rings" fed from the raw frame); pass a RAW frame in that case.

Parameters:

Name Type Description Default
files str, pandas.DataFrame, or list

A .rwl path, an already-loaded ring-width DataFrame (e.g. from dpl.readers()), or a list mixing either. A DataFrame is reported directly (the read step is skipped). For the COFECHA preset, pass a RAW frame (zeros marking absent rings).

required
out_dir str

Directory for the .txt reports (created if needed).

"."
fit str

detrending curve passed to dpl.detrend (ignored when preset="COFECHA", which always uses a spline).

"Spline"
corr

passed through to dpl.xdate (corr, bin_floor and p_val are ignored when preset="COFECHA"; window_length still applies). slide_period= is a deprecated alias of window_length.

'spearman'
window_length

passed through to dpl.xdate (corr, bin_floor and p_val are ignored when preset="COFECHA"; window_length still applies). slide_period= is a deprecated alias of window_length.

'spearman'
bin_floor

passed through to dpl.xdate (corr, bin_floor and p_val are ignored when preset="COFECHA"; window_length still applies). slide_period= is a deprecated alias of window_length.

'spearman'
p_val

passed through to dpl.xdate (corr, bin_floor and p_val are ignored when preset="COFECHA"; window_length still applies). slide_period= is a deprecated alias of window_length.

'spearman'
output (both, screen, file)

where the full report goes: "screen" prints it, "file" writes a <name>.txt in out_dir, "both" does both. The report text is always in the returned dict regardless.

"both"
verbose bool

print a one-line per-file progress note and a final tally.

True
preset str or None

set to "COFECHA" (case-insensitive) to emulate the COFECHA program (see above).

None
spline_period int

spline stiffness (years) for the preset="COFECHA" detrend and variance stabilization (COFECHA's 32).

32
name str or None

Report label for a DataFrame input -- the report title, .txt filename, and key in the returned dict. Ignored for file paths. Defaults to "report".

None

Returns:

Type Description
dict

{path: {"text": str, "report": dict}} for files read successfully, or {path: {"error": str}} for files that failed.

References

.. [1] https://opendendro.github.io/dplPy/reference/crossdating/#xdate_report

xdate_plot

dplR-style crossdating overview for a set of series (see corr.rwl.seg).

A thin wrapper: crossdates data with xdate() (same parameters; slide_period= is a deprecated alias of window_length) and draws the green/blue/red segment plot -- green = series extent, blue = a segment that correlates significantly with the master, red = a flagged segment (p >= p_val). Returns the matplotlib Axes.

Note: this plot shows only the significance screen (dplR's corr.rwl.seg, i.e. the A flag) -- red marks a segment whose dated (lag-0) correlation is not significant. It does NOT show the B flag (a segment that correlates better at a non-dated lag -- a possible dating shift), which dplR's plot has no concept of. A B-flagged segment that still dates significantly is drawn blue here, so it looks fine on the plot even though xdate() reports it as a problem. Always read the text xdate() flag report alongside this plot to catch B (lag) flags.

References

.. [1] https://opendendro.github.io/dplPy/reference/crossdating/#xdate_plot

xdate_floater

Estimate the calendar dating of a floating (undated) ring-width series.

Slide the floating series against a dated master and score each offset by correlation, reporting the best-fit placement with crossdating statistics (t-value, effective df, Bonferroni-corrected p, isolation factor).

Parameters:

Name Type Description Default
data DataFrame or Series

The dated reference (year-indexed). A DataFrame is a collection of ring-width series, averaged into the master; a Series (or one-column DataFrame) is used directly as the master -- e.g. crn["std"] from dpl.read_crn. For an already-residual/prewhitened chronology, pass transform="none" to avoid prewhitening twice. Years with no measurement in any series are kept as NaN on the calendar grid (they are never dropped), and every offset is scored on its complete pairs.

required
series sequence or Series / DataFrame

The floating (undated) ring-width series -- ring values oldest to youngest; any calendar index is ignored. An interior NaN is an unmeasured ring and keeps its position (it is not compressed out).

required
series_name str or None

Label for the floating series. None takes it from the series itself (a named Series or one-column DataFrame header), falling back to "Unknown".

None
min_overlap int

Minimum overlapping (pairwise-complete) rings required to score an offset.

50
transform (pw, fd, none)

High-pass transform applied (after mean-normalization) before correlating, case-insensitive: "pw" AR prewhitening (xdate.normalize_for_crossdating(), the package-standard autoreg._prewhiten), "fd" first differencing, or "none". First differencing sharpens the high-frequency signal but sharply reduces the effective degrees of freedom.

"pw"
prewhiten bool

Deprecated alias: True -> transform="pw", False -> transform="none". Overrides transform if given.

None
biweight bool

Build the master with a Tukey biweight robust mean (else arithmetic mean).

True
corr (spearman, pearson, kendall)

Correlation used to score each offset (one-sided).

"spearman"
ar_max int or None

AR order ceiling for transform="pw". The default follows COFECHA/ARSTAN (cap at 10); None uses dplR's floor(10*log10(n)).

10
first_aic_min bool

Select the AR order at the FIRST local AIC minimum (COFECHA/ARSTAN rule) rather than the global AIC minimum (dplR's ar(); set False).

True
plot bool

Plot the sliding t-value against the series' end year, marking the best fit.

False
return_rwl bool

Also return the floating series placed at its best-fit years (placed) and combined with the reference (combined).

False
segmented bool

Also date each overlapping segment of the floater independently against the master and check that they agree (COFECHA UDATE-style). Adds result["segments"], result["consensus"], result["segment_candidates"] and result["internal_error"] (a dict describing a detected missing/false ring, or None). A coherent one-year step between older and younger segments flags an internal dating error the whole-series fit cannot see; at least four segments are needed for that verdict. The single-best-t placement remains the primary result.

False
segment_topk int

With segmented, how many best placements to record per segment in result["segment_candidates"].

5
verbose bool

Print a short summary of the best-fit dating.

True

Returns:

Type Description
dict

series_name; floater_cor_stats (a DataFrame with one row per offset, highest t first, columns min_year, max_year, r, t, eff_df, p_bonf, n); best (the top-scoring placement, incl. isolation_factor and one_over_p); and, when return_rwl=True, placed and combined.

Notes

Ports dplR's xdate.floater and adds crossdating statistics (t-value, autocorrelation-adjusted effective degrees of freedom, a Bonferroni-corrected p-value, and an isolation factor; Baillie & Pilcher 1973, Wigley et al. 1987, Wilson 2026). The correlation scoring reuses dplPy's crossdating internals, so results are consistent with dpl.xdate.

References

.. [1] https://opendendro.github.io/dplPy/reference/crossdating/#xdate_floater

xdate_undated

Crossdate a set of undated ("floating") ring-width series against one another and assemble a single relatively-dated floating chronology (COFECHA UFLOAT-style).

Use this when no dated reference exists but the samples overlap in time -- e.g. a stand of sub-fossil or river-drowned trees. The series are positioned relative to each other and the earliest inferred ring is set to anchor_year (default 1).

Parameters:

Name Type Description Default
data pandas.DataFrame, dict, or list

The undated collection. A DataFrame is read as one column per series (its index only orders the rings, oldest first); a dict maps names to ring-width arrays; a bare list is auto-named S1, S2, ... An interior NaN is an unmeasured ring and keeps its position.

required
transform (pw, fd, none)

High-pass transform applied before matching (case-insensitive): pw prewhitens (AR, via xdate.normalize_for_crossdating()), fd first-differences, none uses mean-normalized widths.

"pw"
corr (spearman, pearson, kendall)

Correlation used to score every offset.

"spearman"
min_overlap int

Minimum overlapping rings required to score a match.

50
crit_t float

A series joins the master only if its best t against the running mean reaches this.

4.0
anchor_year int

Relative calendar year assigned to the earliest ring of the assembled chronology.

1
min_isolation float

A placement whose leave-one-out isolation (the t gap to the next-best, well-separated position) falls below this is flagged for review.

1.0
ar_max int or None

AR order ceiling for transform="pw". The default follows COFECHA/ARSTAN (cap at 10); None uses dplR's floor(10*log10(n)).

10
first_aic_min bool

Select the AR order at the FIRST local AIC minimum (COFECHA/ARSTAN rule) rather than the global AIC minimum (dplR's ar(); set False).

True
plot bool

Draw a placement chart (each series on the relative axis, coloured by isolation) with the mean floating chronology beneath it.

False
verbose bool

Print a short summary.

True

Returns:

Type Description
dict with:

pairwise : DataFrame of every pair's best match (series_i, series_j, lag, n, r, t) -- the UFLOAT table. offsets : DataFrame (indexed by series) of start_year, end_year, n on the relative axis (earliest ring = anchor_year). placed : DataFrame of the raw ring widths positioned on the relative axis. chronology : the mean floating chronology (Series on the relative axis). order : DataFrame of the join order with the join_t at which each series was added. verify : DataFrame of the leave-one-out check per placed series (loo_best_pos, loo_t, isolation, agrees, review). unplaced : list of series that never reached crit_t.

References

.. [1] https://opendendro.github.io/dplPy/reference/crossdating/#xdate_undated

series_corr

Crossdate one series against the master built from all the others.

Produces (and, by default, plots) a moving correlation of the series against the leave-one-out master, the per-segment correlations, and a per-segment lag table (COFECHA-style) showing how the correlation changes when the master is shifted ± lag years past the dated segment -- an off-zero peak suggests a dating error in that segment.

Lag convention (lag_table and, by default, ccf): the dated segment stays fixed and the MASTER slides, so a negative lag means a missing ring in the series, as in COFECHA, dpl.xdate and dpl.check. The lag_table is xdate._lag_table(), the same scan behind xdate's B flags.

Parameters:

Name Type Description Default
data DataFrame

ring-width series (typically detrended RWI).

required
series_name str

the series to examine.

required
prewhiten

as in dpl.xdate() (window_length is the segment length; seg_length= is a deprecated alias).

True
corr

as in dpl.xdate() (window_length is the segment length; seg_length= is a deprecated alias).

True
window_length

as in dpl.xdate() (window_length is the segment length; seg_length= is a deprecated alias).

True
bin_floor

as in dpl.xdate() (window_length is the segment length; seg_length= is a deprecated alias).

True
p_val

as in dpl.xdate() (window_length is the segment length; seg_length= is a deprecated alias).

True
biweight

as in dpl.xdate() (window_length is the segment length; seg_length= is a deprecated alias).

True
lag

as in dpl.xdate() (window_length is the segment length; seg_length= is a deprecated alias).

True
ar_max

as in dpl.xdate() (window_length is the segment length; seg_length= is a deprecated alias).

True
first_aic_min

as in dpl.xdate() (window_length is the segment length; seg_length= is a deprecated alias).

True
plot bool

master switch for drawing any figure.

True
series_x bool

lag convention for the dplR-style ccf only (lag_table always uses the COFECHA convention). False (dplR's stock series.x=FALSE) calls ccf(x=master, y=series) so a negative lag marks a missing ring in the series, consistent with lag_table. True calls ccf(x=series, y=master), flipping the sign so a positive lag marks a missing ring.

False
which (both, moving, ccf)

when plot is True, which figure(s) to draw: both, only the moving-correlation plot (corr.series.seg), or only the per-segment ccf panels (ccf.series.rwl). Case-insensitive.

'both'

Returns:

Type Description
dict with keys ``moving_corr`` (Series), ``seg_corr`` (Series over bins),
``overall`` (DataFrame, one row for the series, columns ``rho`` and
``p_val`` -- the same shape as ``xdate()["overall"]``), ``lag_table``
(correlations at shifted master windows, lags x bins, using ``corr``),
``ccf`` (dplR-style Pearson cross-correlation per segment, lags x bins),
``ccf_bins``, ``bins`` and ``rwi`` (the transformed series used).
References

.. [1] https://opendendro.github.io/dplPy/reference/crossdating/#series_corr

interseries_corr

Mean interseries correlation (COFECHA's headline statistic).

For each series, correlate it against a master chronology built from every other series (leave-one-out), then average. This is dplR's interseries.cor(), and is distinct from rbar (rwi_stats), which averages the pairwise correlations between individual series.

Expects raw ring widths, not indices: each series is normalized by its own mean and, by default, prewhitened before the correlations are computed (the same transform as dpl.xdate, so the per-series values equal xdate()["overall"]["rho"]).

Parameters:

Name Type Description Default
data DataFrame

raw ring widths (as from dpl.readers), years as the index, series as columns.

required
prewhiten boolean

prewhiten each series with an autoregressive model before correlating (autoreg._prewhiten, Yule-Walker, first local AIC minimum up to ar_max).

True
biweight boolean

build each leave-one-out master with a Tukey biweight robust mean rather than the arithmetic mean.

True
corr (spearman, pearson, kendall)

correlation type (case-insensitive).

"spearman"
ar_max int or None

AR-order ceiling for prewhitening; None uses dplR's floor(10*log10(n)).

10
first_aic_min bool

AR order at the first local AIC minimum (ARSTAN rule) rather than the global minimum; ar_max=None, first_aic_min=False is dplR's interseries.cor.

True

Returns:

Type Description
dict

mean -- the collection-wide mean interseries correlation (3 decimals, NaN-skipping); table -- a DataFrame indexed by series (in the input's column order) with columns rho (3 decimals) and p_val (one-sided); n_series -- the number of series with a correlation; too_short -- the names of series that overlapped their master by fewer than 3 years and were given NaN.

Examples:

>>> import dplpy as dpl
>>> res = dpl.interseries_corr(dpl.readers("series.rwl"))
>>> res["mean"], res["table"]
References

.. [1] https://rdrr.io/cran/dplR/man/interseries.cor.html .. [2] https://opendendro.github.io/dplPy/reference/crossdating/#interseries_corr