Skip to content

Crossdating

Statistical crossdating against a master chronology, COFECHA-style batch reporting, and floating-series dating. See the Crossdating & COFECHA guide.

xdate

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

The segment correlations mirror dplR's corr.rwl.seg(): each series is normalized (divided by its mean), optionally Yule-Walker prewhitened, and correlated against a biweight master built from all the other series, over segments of slide_period years that overlap by half; the A flag reproduces dplR exactly -- a segment is flagged A when it is not significant (one-tailed p >= p_val).

The B flag is not from dplR (corr.rwl.seg has no lag flag): it is a COFECHA-derived dating-shift screen, flagging a segment when it correlates better with the master at a non-dated lag (best_lag != 0, no margin), using COFECHA's SLSG convention of sliding the master past the fixed segment. The same B rule is used in preset="COFECHA". The per-segment lag table is printed for flagged segments.

Parameters:

Name Type Description Default
data DataFrame

ring-width series (typically detrended RWI from dpl.detrend()).

required
prewhiten bool

AR-prewhiten each series (Yule-Walker, matching dplR).

True
corr (spearman, pearson, kendall)

correlation method (case-insensitive).

'spearman','pearson','kendall'
slide_period int

segment length in years.

50
bin_floor int

the first segment is floored to a multiple of this.

100
p_val float

significance level for the segment flag.

0.05
biweight bool

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

True
lag int

maximum ± lag examined for the lag (B) flag / COFECHA table.

10
show_flags bool

print the flag summary and lag tables.

True
make_plot bool

draw the segment-correlation plot.

False
preset str or None

set to "COFECHA" to emulate the COFECHA program instead of dplR's corr.rwl.seg. This overrides the transform, correlation, master and segmentation machinery to match COFECHA's FORTRAN: Pearson correlation, an arithmetic leave-one-out master of z-scored series, 50-yr segments on a 25-yr grid anchored to each series' first and last year, a critical value derived from the 99%% one-tailed t rather than p_val, COFECHA's spline variance stabilization before AR, Cook/Krusic Burg AR prewhitening (ceiling 10, first-local-AIC-minimum, N>=8), and a lag search that slides the master against the fixed dated segment (COFECHA's SLSG). Returns the extra keys segments and n_problems. corr, biweight, bin_floor and p_val are ignored in this mode.

None
seg_lag int or None

segment step in years (segment overlap). None uses slide_period // 2 (COFECHA's 50%% overlap). Only used by the preset.

None
absent DataFrame or None

COFECHA preset only -- its "omit absent rings" option (QAC=Y). A boolean DataFrame (years x series) marking absent rings, or a raw ring-width DataFrame whose zeros mark them. Absent years are dropped from that series' segment correlations. Ring widths are non-zero after detrending, so pass a pre-detrend source, e.g. absent=raw_rwl == 0 (or just the raw frame). None disables the omission.

None
ar_max int or None

AR-order ceiling for prewhitening. None is dplR's floor(10*log10(n)); the COFECHA preset defaults it to 10 (Cook/Krusic's ARSTAN ceiling) and selects the order by the first-local-AIC-minimum rule.

None

Returns:

Type Description
dict

A dictionary 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'])
  • avg_seg_corr -- Series (bins) mean correlation across series
  • flags -- dict {series: {'A': [...], 'B': [...]}}
  • bins -- list of "start-end" bin labels
  • rwi -- DataFrame of the normalized/prewhitened series used

And, for preset="COFECHA" only:

  • segments -- dict {series: [{lo, hi, r0, best_lag, best_corr, n, crit, flag, lags}]}
  • n_problems -- int, COFECHA's "Segments, possible problems" count

Examples:

>>> rwi = dpl.detrend(ca533, fit="spline", plot=False)
>>> res = dpl.xdate(rwi, corr="spearman", slide_period=50, bin_floor=100)
>>> # COFECHA emulation (32-yr spline detrend, then the preset):
>>> rwi = dpl.detrend(rwl, fit="Spline", period=32, plot=False)
>>> res = dpl.xdate(rwi, preset="COFECHA")
>>> res["n_problems"]                      # COFECHA "possible problems" count
References

.. [1] https:/opendendro.org/dplpy-man/#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 (if write) save a <name>.txt report in out_dir. Built for batch QA of ITRDB submissions.

By default this is a dplR-faithful report styled after COFECHA (some columns match COFECHA closely, others differ by method). Pass preset="COFECHA" to emulate the COFECHA program instead: each file is detrended with a 32-year spline and cross-dated with dpl.xdate(preset="COFECHA") (Burg prewhitening, spline variance stabilization, an arithmetic z-scored master, Pearson correlation, COFECHA segment anchoring and critical value, and "omit absent rings" fed automatically from the raw frame), and the summary statistics are length-weighted by ring count as COFECHA reports them.

Parameters:

Name Type Description Default
files str or list of str

A .rwl path, or a list of them.

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"; slide_period still applies).

'spearman'
slide_period

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

'spearman'
bin_floor

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

'spearman'
p_val

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

'spearman'
write bool

write the .txt files; if False, only the text is returned.

True
verbose bool

print progress and a final tally.

True
preset str or None

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

None
spline_period int or None

spline stiffness (years) for the preset="COFECHA" detrend; defaults to COFECHA's 32.

None

Returns:

Type Description
dict

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

xdate_plot

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

A thin wrapper: crossdates data with :func:xdate (same parameters) 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.

xdate_floater

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

Parameters:

Name Type Description Default
data DataFrame

A dated reference collection (year-indexed ring widths), from which the master chronology is built.

required
series sequence or Series / DataFrame

The floating (undated) ring-width series -- just the ring values, oldest to youngest; any calendar index is ignored.

required
series_name str

A label for the floating series (used in outputs).

"Unknown"
min_overlap int

Minimum number of overlapping rings required to score an offset.

50
transform (pw, fd, none)

High-pass transform applied (after mean-normalization) before correlating: "pw" Yule-Walker AR prewhitening (residual series, the default), "fd" first differencing, or "none". First differencing can sharpen the common high-frequency signal but sharply reduces the effective degrees of freedom (Wilson 2026).

"pw"
prewhiten bool

Deprecated back-compatible alias: prewhiten=True -> transform="pw", prewhiten=False -> transform="none". If given, it overrides transform.

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, alternative "greater").

"spearman"
make_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 calendar years (placed) and combined with the reference (combined).

False
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 (default n=NULL mean normalization) and adds the crossdating statistics of Baillie & Pilcher (1973), Wigley et al. (1987) and Wilson (2026): the t-value, autocorrelation-adjusted effective degrees of freedom, a Bonferroni-corrected p-value, and an isolation factor. The correlation scoring reuses dplPy's crossdating internals, so results are consistent with dpl.xdate.

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 segment is shifted ± lag years -- large off-zero peaks suggest a dating error in that segment.

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() (seg_length is the segment length).

True
corr

as in dpl.xdate() (seg_length is the segment length).

True
seg_length

as in dpl.xdate() (seg_length is the segment length).

True
bin_floor

as in dpl.xdate() (seg_length is the segment length).

True
p_val

as in dpl.xdate() (seg_length is the segment length).

True
biweight

as in dpl.xdate() (seg_length is the segment length).

True
lag

as in dpl.xdate() (seg_length is the segment length).

True
make_plot bool

master switch for drawing any figure.

True
which (both, moving, ccf)

when make_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).

'both'
series_x bool

lag convention for the dplR-style ccf. True calls ccf(x=series, y=master) so a positive lag marks a missing ring in the series (Bunn's intuitive convention); False matches dplR's stock series.x=FALSE (negative lag = missing ring).

True

Returns:

Type Description
dict with keys ``moving_corr`` (Series), ``seg_corr`` (Series over bins),
``overall`` ((rho, p_val)), ``lag_table`` (Spearman rank correlations at
shifted windows, lags x bins), ``ccf`` (dplR-style Pearson cross-correlation
per segment, lags x bins), ``ccf_bins`` and ``bins``.
References

.. [1] https:/opendendro.org/dplpy-man/#series_corr

interseries_corr

Mean interseries correlation

Extended Summary

For every series in the dataset, calculates the correlation between that series and a master chronology built from every other series in the dataset (leave-one-out principle). This is the same quantity commonly reported by COFECHA as the mean interseries correlation, and computed by dplR's interseries.cor().

This is a fundamentally different statistic from rbar (reported by dpl.rwi_stats() and used by dpl.chron_stabilized()): rbar is the mean of the pairwise correlations between every series and every OTHER series individually -- a stricter test to pass -- while the interseries correlation computed here is the mean of the correlation between each series and the composite chronology built from the rest. dplR's own documentation for interseries.cor() draws this same distinction explicitly.

Each series is first normalized by dividing by its own mean (dplPy's "horizontal" detrend -- equivalent to dplR's normalize.xdate() without its optional Hanning-filter alternative, which dplPy does not currently implement), then optionally prewhitened with an autoregressive model (matching dplR's default). Note also that, unlike dplR's rwi.stats()/rwi.stats.running(), this function does not exclude series with very few (three or fewer) valid observations from contributing to other series' composite chronologies; for typical dendrochronological datasets this is not expected to matter, but a pathologically short series could be weighted differently here than in dplR.

Parameters:

Name Type Description Default
data pandas dataframe

a dataframe of raw ring widths, as produced by dpl.readers(). Unlike chron() or chron_stabilized(), this function expects raw measurements rather than already-detrended ring-width indices -- it performs its own normalization internally, matching dplR's interseries.cor().

required
prewhiten boolean

whether to prewhiten each series with an autoregressive model before computing correlations.

True
biweight boolean

whether to use Tukey's biweight robust mean (rather than the arithmetic mean) when building each series' leave-one-out composite chronology.

True
corr str

correlation type to use: "Spearman" or "Pearson".

"Spearman"

Returns:

Name Type Description
result pandas dataframe with one row per series (indexed by series

name), containing the interseries correlation and its (one-sided, "greater") p-value.

Examples:

>>> import dplpy as dpl
>>> data = dpl.readers("../tests/data/csv/file.csv")
>>> dpl.interseries_corr(data)
>>> dpl.interseries_corr(data, prewhiten=False, corr="Pearson")
References

.. [1] https://rdrr.io/cran/dplR/man/interseries.cor.html