| Title: | Probabilistic Decomposition of Archaeological Palimpsests |
|---|---|
| Description: | Probabilistic framework for the analysis of archaeological palimpsests based on the Stratigraphic Entanglement Field (SEF). Integrates spatial proximity, stratigraphic depth, chronological overlap, and cultural similarity to estimate latent depositional phases via diagonal Gaussian mixture Expectation-Maximisation (EM). Provides the Stratigraphic Entanglement Index (SEI), Excavation Stratigraphic Energy (ESE), and Palimpsest Dissolution Index (PDI) for quantifying depositional coherence, detecting intrusive finds, and measuring palimpsest formation. Includes simulation, diagnostics, phase-count selection, publication-quality plots, and Geographic Information System (GIS) export via 'sf'. Methods are described in Cocca (2026) <https://github.com/enzococca/palimpsestr>. |
| Authors: | Enzo Cocca [aut, cre] (ORCID: <https://orcid.org/0000-0002-8096-2810>) |
| Maintainer: | Enzo Cocca <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.24.1 |
| Built: | 2026-10-27 10:23:50 UTC |
| Source: | https://github.com/enzococca/palimpsestr |
Compares estimated phase assignments against known true labels using the Adjusted Rand Index (Hubert and Arabie, 1985). Values near 1 indicate perfect agreement; values near 0 indicate random agreement.
adjusted_rand_index(object, true_labels)adjusted_rand_index(object, true_labels)
object |
A |
true_labels |
Integer vector of known phase assignments. |
A single numeric value in .
Other validation:
bootstrap_sef(),
confusion_matrix(),
cv_sef(),
optimize_weights()
x <- archaeo_sim(n = 80, k = 3, seed = 1, mixing = 0.05) fit <- fit_sef(x, k = 3, seed = 1) adjusted_rand_index(fit, x$true_phase)x <- archaeo_sim(n = 80, k = 3, seed = 1, mixing = 0.05) fit <- fit_sef(x, k = 3, seed = 1) adjusted_rand_index(fit, x$true_phase)
Generates a synthetic excavation dataset with known latent phases, controlled spatial clustering, and configurable inter-phase mixing.
archaeo_sim(n = 150, k = 3, seed = NULL, mixing = 0.08)archaeo_sim(n = 150, k = 3, seed = NULL, mixing = 0.08)
n |
Number of observations (finds). |
k |
Number of latent depositional phases. |
seed |
Optional random seed for reproducibility. |
mixing |
Proportion of observations to perturb spatially and taphonomically, simulating post-depositional disturbance (0–1). |
A data.frame with columns: id, x, y, z,
context, date_min, date_max, class,
taf_score, true_phase. The date bounds date_min and
date_max are returned as integer-valued years (via floor()),
consistent with archaeological dating conventions.
fit_sef for fitting the SEF model to the output.
easy <- archaeo_sim(n = 100, k = 3, mixing = 0.05, seed = 1) table(easy$true_phase) hard <- archaeo_sim(n = 200, k = 4, mixing = 0.50, seed = 1) table(hard$true_phase)easy <- archaeo_sim(n = 100, k = 3, mixing = 0.05, seed = 1) table(easy$true_phase) hard <- archaeo_sim(n = 200, k = 4, mixing = 0.50, seed = 1) table(hard$true_phase)
Returns a data.frame combining dominant phase assignments, membership probabilities, entropy, local SEI, and energy.
as_phase_table(object)as_phase_table(object)
object |
A |
A data.frame with one row per find.
predict_phase, phase_diagnostic_table
Other diagnostics:
detect_intrusions(),
ese(),
pdi(),
phase_composition(),
phase_diagnostic_table(),
predict_phase(),
recommend_setup(),
type_longevity()
x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) head(as_phase_table(fit))x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) head(as_phase_table(fit))
Wraps ggplotly with archaeological tooltips showing
find ID, context, phase probability, dating, class, entropy, and energy.
as_plotly(gg, tooltip = "text", ...)as_plotly(gg, tooltip = "text", ...)
gg |
A ggplot object produced by any |
tooltip |
Character vector of aesthetics to show. Defaults to
|
... |
Additional arguments passed to |
A plotly htmlwidget object.
gg_phasefield, gg_entropy,
gg_energy, gg_intrusions
Other plotting:
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3) if (requireNamespace("ggplot2", quietly = TRUE) && requireNamespace("plotly", quietly = TRUE)) { p <- as_plotly(gg_phasefield(fit)) }x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3) if (requireNamespace("ggplot2", quietly = TRUE) && requireNamespace("plotly", quietly = TRUE)) { p <- as_plotly(gg_phasefield(fit)) }
Extracts the strongest pairwise SEI connections as sf line geometries.
as_sf_links(object, quantile_threshold = 0.9, crs = NA_integer_)as_sf_links(object, quantile_threshold = 0.9, crs = NA_integer_)
object |
A |
quantile_threshold |
Quantile for retaining strongest links (default: 0.9). |
crs |
CRS for the output geometry. |
An sf object with columns from, to,
sei, and geometry.
Other GIS:
as_sf_phase()
if (requireNamespace("sf", quietly = TRUE)) { x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) links <- as_sf_links(fit) }if (requireNamespace("sf", quietly = TRUE)) { x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) links <- as_sf_links(fit) }
Creates an sf point object with phase assignments and diagnostics
for use in QGIS or spatial analysis.
as_sf_phase(object, crs = NA_integer_, dims = c("XY", "XYZ"))as_sf_phase(object, crs = NA_integer_, dims = c("XY", "XYZ"))
object |
A |
crs |
CRS passed to |
dims |
Either |
An sf object.
as_sf_links, phase_diagnostic_table
Other GIS:
as_sf_links()
if (requireNamespace("sf", quietly = TRUE)) { x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) pts <- as_sf_phase(fit) }if (requireNamespace("sf", quietly = TRUE)) { x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) pts <- as_sf_phase(fit) }
Resamples the data with replacement, refits the model, and computes the distribution of key statistics (PDI, mean entropy, mean energy, ARI if true labels provided). Returns percentile confidence intervals.
bootstrap_sef( object, n_boot = 100, conf = 0.95, true_labels = NULL, verbose = TRUE )bootstrap_sef( object, n_boot = 100, conf = 0.95, true_labels = NULL, verbose = TRUE )
object |
A |
n_boot |
Number of bootstrap replicates (default: 100). |
conf |
Confidence level (default: 0.95). |
true_labels |
Optional integer vector of true phase labels for ARI. |
verbose |
Print progress (default: TRUE). |
A data.frame with columns statistic, estimate,
lower, upper, se.
fit_sef, pdi,
adjusted_rand_index
Other validation:
adjusted_rand_index(),
confusion_matrix(),
cv_sef(),
optimize_weights()
x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2, seed = 1) bootstrap_sef(fit, n_boot = 20)x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2, seed = 1) bootstrap_sef(fit, n_boot = 20)
Adapter that takes either an oxcAAR-calibrated dates object or a
generic data.frame of calibrated ranges and returns a data.frame compatible
with the chronology columns expected by fit_sef. It is the
OxCal counterpart of chronology_from_rcarbon.
chronology_from_oxcal( x, method = c("hpd", "median_iqr", "weighted_mean"), hpd = 0.95, ids = NULL, bce_negative = TRUE )chronology_from_oxcal( x, method = c("hpd", "median_iqr", "weighted_mean"), hpd = 0.95, ids = NULL, bce_negative = TRUE )
x |
Either an object of class |
method |
For the oxcAAR object input, one of |
hpd |
Probability covered by the HPD region when |
ids |
Optional character identifiers; default |
bce_negative |
Logical. If TRUE (default), dates are returned in the
BCE/CE convention with BCE negative; if FALSE, raw calBP
( |
A data.frame with columns id, date_min,
date_max, date_mid.
chronology_from_rcarbon, fit_sef
Other chronology:
chronology_from_rcarbon()
d <- data.frame(id = c("a", "b"), start = c(-700, -50), end = c(-600, 100)) chronology_from_oxcal(d)d <- data.frame(id = c("a", "b"), start = c(-700, -50), end = c(-600, 100)) chronology_from_oxcal(d)
Adapter that takes an rcarbon::CalDates object (produced by
rcarbon::calibrate()) and returns a data.frame compatible
with the chronology columns expected by fit_sef().
chronology_from_rcarbon( cal_dates, method = c("hpd", "median_iqr", "weighted_mean"), hpd = 0.95, ids = NULL, bce_negative = TRUE )chronology_from_rcarbon( cal_dates, method = c("hpd", "median_iqr", "weighted_mean"), hpd = 0.95, ids = NULL, bce_negative = TRUE )
cal_dates |
An object of class |
method |
One of |
hpd |
Numeric in (0, 1). Probability covered by the HPD region
when |
ids |
Optional character vector of length |
bce_negative |
Logical. If TRUE (default), dates are returned
in the BCE/CE convention with BCE as negative numbers (calBP
converted as |
A data.frame with columns id, date_min,
date_max, date_mid.
Other chronology:
chronology_from_oxcal()
## Not run: if (requireNamespace("rcarbon", quietly = TRUE)) { cal <- rcarbon::calibrate(x = c(2500, 2400), errors = c(30, 30)) chronology_from_rcarbon(cal) } ## End(Not run)## Not run: if (requireNamespace("rcarbon", quietly = TRUE)) { cal <- rcarbon::calibrate(x = c(2500, 2400), errors = c(30, 30)) chronology_from_rcarbon(cal) } ## End(Not run)
Fits the SEF model for each value of K and returns a summary table with BIC, PDI, entropy, energy, and other diagnostics.
compare_k(data, k_values = 2:6, ...)compare_k(data, k_values = 2:6, ...)
data |
Input data.frame. |
k_values |
Integer vector of candidate phase counts. |
... |
Additional arguments passed to |
For each candidate K the table reports several model-selection
quantities. The Bayesian Information Criterion (bic,
, where is the fitted mixture
likelihood, the number of free parameters and the number of
finds; Schwarz 1978) balances goodness of fit against model complexity and
is the primary selector — lower is better. The Palimpsest
Dissolution Index (pdi; see pdi) is a 0–1 measure of
how cleanly the phases separate, and mean_entropy is the mean Shannon
entropy of the soft assignments. The Integrated Completed
Likelihood (icl) augments the BIC with the classification entropy
(; Biernacki, Celeux &
Govaert 2000), penalising overlapping phases so that it favours partitions
with confident assignments. Statistical criteria should be combined with
archaeological judgement: the BIC minimum is not necessarily the most
interpretable partition.
A data.frame with one row per K value.
Schwarz, G. (1978). Estimating the dimension of a model. The Annals of Statistics, 6(2), 461–464. doi:10.1214/aos/1176344136
Biernacki, C., Celeux, G., & Govaert, G. (2000). Assessing a mixture model for clustering with the integrated completed likelihood. IEEE Transactions on Pattern Analysis and Machine Intelligence, 22(7), 719–725. doi:10.1109/34.865189
Other fitting:
fit_sef(),
reorder_phases(),
sef_summary()
x <- archaeo_sim(n = 100, k = 3, seed = 1) ck <- compare_k(x, k_values = 2:4) print(ck)x <- archaeo_sim(n = 100, k = 3, seed = 1) ck <- compare_k(x, k_values = 2:4) print(ck)
Cross-tabulates estimated phase assignments against known true labels. Phases are reordered to maximise diagonal agreement (Hungarian matching).
confusion_matrix(object, true_labels)confusion_matrix(object, true_labels)
object |
A |
true_labels |
Integer vector of known phase assignments. |
A matrix with estimated phases as rows and true phases as columns.
Other validation:
adjusted_rand_index(),
bootstrap_sef(),
cv_sef(),
optimize_weights()
x <- archaeo_sim(n = 80, k = 3, seed = 1, mixing = 0.05) fit <- fit_sef(x, k = 3, seed = 1) confusion_matrix(fit, x$true_phase)x <- archaeo_sim(n = 80, k = 3, seed = 1, mixing = 0.05) fit <- fit_sef(x, k = 3, seed = 1) confusion_matrix(fit, x$true_phase)
Splits the data into folds, fits on training folds, and evaluates log-likelihood on the held-out fold. Useful for comparing different K values or weight configurations.
cv_sef(data, k_values = 2:6, n_folds = 5, seed = 1, ...)cv_sef(data, k_values = 2:6, n_folds = 5, seed = 1, ...)
data |
A data.frame with archaeological find data. |
k_values |
Integer vector of candidate phase counts. |
n_folds |
Number of cross-validation folds (default: 5). |
seed |
Random seed for fold assignment. |
... |
Additional arguments passed to |
A data.frame with columns k, fold,
train_loglik, test_loglik, train_pdi. The
test_loglik carries the same Jacobian correction as
optimize_weights, so it is comparable across weight
configurations (not only across k); with default weights the
correction is zero.
Other validation:
adjusted_rand_index(),
bootstrap_sef(),
confusion_matrix(),
optimize_weights()
x <- archaeo_sim(n = 100, k = 3, seed = 1) cv <- cv_sef(x, k_values = 2:4, n_folds = 3) # Mean test log-likelihood per K aggregate(test_loglik ~ k, data = cv, FUN = mean)x <- archaeo_sim(n = 100, k = 3, seed = 1) cv <- cv_sef(x, k_values = 2:4, n_folds = 3) # Mean test log-likelihood per K aggregate(test_loglik ~ k, data = cv, FUN = mean)
Simulated dataset with 250 artefacts, 4 phases, and 50% mixing. Represents a heavily disturbed deposit where phases are difficult to separate.
demo_compresseddemo_compressed
A data.frame with 250 rows and 10 columns. See demo_easy
for column descriptions.
data(demo_compressed) ck <- compare_k(demo_compressed, k_values = 2:6) print(ck)data(demo_compressed) ck <- compare_k(demo_compressed, k_values = 2:6) print(ck)
Simulated dataset with 150 artefacts, 3 phases, and 5% mixing. Represents a site where occupation phases are clearly distinguishable.
demo_easydemo_easy
A data.frame with 150 rows and 10 columns:
Artefact identifier
Planimetric coordinates (metres)
Depth (metres)
Stratigraphic unit label
Chronological interval (CE)
Cultural class (ceramic, lithic, bone, metal)
Taphonomic disturbance score (0-1)
Known phase assignment (for validation)
data(demo_easy) fit <- fit_sef(demo_easy, k = 3) plot_phasefield(fit)data(demo_easy) fit <- fit_sef(demo_easy, k = 3) plot_phasefield(fit)
Simulated dataset with 200 artefacts, 3 phases, and 30% mixing. Represents a site with significant but resolvable depositional mixing.
demo_moderatedemo_moderate
A data.frame with 200 rows and 10 columns. See demo_easy
for column descriptions.
data(demo_moderate) fit <- fit_sef(demo_moderate, k = 3, tafonomy = "taf_score", context = "context") summary(fit)data(demo_moderate) fit <- fit_sef(demo_moderate, k = 3, tafonomy = "taf_score", context = "context") summary(fit)
Returns a per-find intrusion probability. When the model was fitted with
noise = TRUE, intrusion_prob is the posterior probability of
the uniform noise component (a genuine outlier probability). Otherwise it is
the heuristic composite of rescaled entropy, energy, and inverse local SEI.
detect_intrusions(object, envelope = c(0.05, 0.95), intrusion_threshold = 0.5)detect_intrusions(object, envelope = c(0.05, 0.95), intrusion_threshold = 0.5)
object |
A |
envelope |
Numeric vector of two probabilities giving the quantiles
of the other finds' dating bounds used as the leave-one-out unit
envelope for the directional classification. The default
|
intrusion_threshold |
Probability above which a find counts as flagged for the |
A data.frame with columns id, intrusion_prob,
direction (factor with levels older_than_context,
in_context, younger_than_context), and chrono_gap
(numeric, signed offset in years).
The data.frame also carries an intrusion_type factor combining the intrusion magnitude with the direction: not_flagged, residual (flagged and older-than-context), latent_feature (flagged and younger-than-context), or outlier_in_context.
gg_intrusions, fit_sef,
type_longevity
Other diagnostics:
as_phase_table(),
ese(),
pdi(),
phase_composition(),
phase_diagnostic_table(),
predict_phase(),
recommend_setup(),
type_longevity()
x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) di <- detect_intrusions(fit) head(di[order(di$intrusion_prob, decreasing = TRUE), ])x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) di <- detect_intrusions(fit) head(di[order(di$intrusion_prob, decreasing = TRUE), ])
Measures local depositional disruption for each find by summing weighted dissimilarities with neighbours.
ese( data, coords = c("x", "y", "z"), chrono = c("date_min", "date_max"), class_col = "class", beta = c(1, 1, 1, 1), neighbourhood = NULL )ese( data, coords = c("x", "y", "z"), chrono = c("date_min", "date_max"), class_col = "class", beta = c(1, 1, 1, 1), neighbourhood = NULL )
data |
Input data.frame. |
coords |
Character vector of coordinate column names. |
chrono |
Character vector with minimum and maximum dating columns. |
class_col |
Class column name. |
beta |
Numeric vector of length 4: weights for spatial, vertical,
temporal, and class mismatch. Since v0.14.0 each component is
normalised to |
neighbourhood |
Maximum XY distance for neighbour inclusion.
When |
A numeric vector of local energy values.
Other diagnostics:
as_phase_table(),
detect_intrusions(),
pdi(),
phase_composition(),
phase_diagnostic_table(),
predict_phase(),
recommend_setup(),
type_longevity()
x <- archaeo_sim(n = 30, k = 2, seed = 1) e <- ese(x) summary(e)x <- archaeo_sim(n = 30, k = 2, seed = 1) e <- ese(x) summary(e)
Writes phase assignments, intrusion scores, US summary, and model summary to CSV files in the specified directory.
export_results(object, dir, format = "csv", prefix = "palimpsestr")export_results(object, dir, format = "csv", prefix = "palimpsestr")
object |
A |
dir |
Output directory (created if it does not exist). Must be
specified explicitly; consider using |
format |
Export format: |
prefix |
File name prefix (default: |
Invisibly returns a character vector of written file paths.
us_summary_table, as_phase_table
Other export:
phase_transition_matrix(),
us_summary_table()
x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2, context = "context") export_results(fit, dir = tempdir())x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2, context = "context") export_results(fit, dir = tempdir())
Assembles a complete report from a fitted sef_fit: the interpretive
narrative of report_sef, all applicable gg_* diagnostic
plots, and diagnostic tables (US summary, phase-transition matrix, per-US
phase assignments, model statistics). The report is rendered to PDF and/or
DOCX via an RMarkdown template.
export_sef_report( fit, file, format = c("pdf", "docx"), lang = c("it", "en"), title = NULL, site = NULL, compare_k = NULL, intrusion_threshold = 0.5, quiet = TRUE )export_sef_report( fit, file, format = c("pdf", "docx"), lang = c("it", "en"), title = NULL, site = NULL, compare_k = NULL, intrusion_threshold = 0.5, quiet = TRUE )
fit |
A fitted |
file |
Output path. Any extension is stripped and replaced; sibling
files ( |
format |
Character vector, any of |
lang |
Report language: |
title |
Report title (a sensible localized default is used when
|
site |
Optional site name printed in the header. |
compare_k |
Optional result of |
intrusion_threshold |
Posterior threshold for flagging intrusions (default 0.5). |
quiet |
Passed to |
A markdown narrative sidecar (<file>.md) is always written
(used, for example, by the QGIS results panel). When pandoc is unavailable
(as can happen when R is launched outside RStudio), the function degrades
gracefully to that markdown narrative plus the figures saved as PNG, and tells
you how to enable full rendering.
(Invisibly) a character vector of the files written.
Estimates latent depositional phases from spatial, stratigraphic, chronological, and cultural evidence using diagonal Gaussian mixture EM.
fit_sef( data, coords = c("x", "y", "z"), chrono = c("date_min", "date_max"), class = "class", tafonomy = NULL, context = NULL, harris = NULL, k = 3, weights = c(ws = 1, wz = 1, wt = 1, wc = 1), seed = 1, em_iter = 100, em_tol = 1e-05, n_init = 5, chrono_precision = FALSE, taf_as_feature = FALSE, residuality = FALSE, class_scale = FALSE, subclass = NULL, var_structure = c("diagonal", "spherical"), class_model = c("multinomial", "gaussian"), class_smoothing = 1, chrono_uncertainty = FALSE, strat_dynamic = FALSE, strat_beta = 1, noise = FALSE, noise_prior = 0.05 )fit_sef( data, coords = c("x", "y", "z"), chrono = c("date_min", "date_max"), class = "class", tafonomy = NULL, context = NULL, harris = NULL, k = 3, weights = c(ws = 1, wz = 1, wt = 1, wc = 1), seed = 1, em_iter = 100, em_tol = 1e-05, n_init = 5, chrono_precision = FALSE, taf_as_feature = FALSE, residuality = FALSE, class_scale = FALSE, subclass = NULL, var_structure = c("diagonal", "spherical"), class_model = c("multinomial", "gaussian"), class_smoothing = 1, chrono_uncertainty = FALSE, strat_dynamic = FALSE, strat_beta = 1, noise = FALSE, noise_prior = 0.05 )
data |
A data.frame with archaeological find data. |
coords |
Character vector of length 3 naming the x, y, z coordinate columns. |
chrono |
Character vector of length 2 naming the min and max dating columns. |
class |
Character scalar naming the material class column. |
tafonomy |
Optional column name for taphonomic disturbance scores (0–1). |
context |
Optional column name for stratigraphic unit labels. |
harris |
Optional |
k |
Integer number of phases to estimate. |
weights |
Named numeric vector with components |
seed |
Random seed for reproducibility. |
em_iter |
Maximum number of EM iterations (default: 100). |
em_tol |
Convergence tolerance on the log-likelihood. |
n_init |
Number of random initialisations. The run with the highest EM objective is retained (default: 5). The EM objective is multimodal, so a single initialisation risks a poor local optimum. |
chrono_precision |
Logical. If TRUE, add 1/tspan as a feature giving higher weight to precisely dated finds (default: FALSE). |
taf_as_feature |
Logical. If TRUE and |
residuality |
Logical. If TRUE and |
class_scale |
Logical. If TRUE, scale one-hot class columns by
|
subclass |
Optional column name for sub-class labels. If provided,
used instead of |
var_structure |
Either |
class_model |
Either |
class_smoothing |
Non-negative Dirichlet pseudo-count for the
categorical class probabilities under |
chrono_uncertainty |
Logical (default |
strat_dynamic |
Logical (default |
strat_beta |
Non-negative strength of the dynamic stratigraphic field
(default 1); only used when |
noise |
Logical (default |
noise_prior |
Initial mixing weight of the noise component, strictly
between 0 and 1 (default 0.05); only used when |
An S3 object of class sef_fit.
archaeo_sim, compare_k,
pdi, detect_intrusions
Other fitting:
compare_k(),
reorder_phases(),
sef_summary()
x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) print(fit) x <- archaeo_sim(n = 150, k = 3, seed = 42) fit <- fit_sef(x, k = 3, tafonomy = "taf_score", context = "context") summary(fit)x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) print(fit) x <- archaeo_sim(n = 150, k = 3, seed = 42) fit <- fit_sef(x, k = 3, tafonomy = "taf_score", context = "context") summary(fit)
Visualises bootstrap confidence intervals for key SEF diagnostics (PDI, entropy, energy, log-likelihood, and optionally ARI).
gg_bootstrap(bs_result)gg_bootstrap(bs_result)
bs_result |
Data.frame returned by |
A ggplot object.
Other plotting:
as_plotly(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
Three-panel plot showing BIC, PDI, and Mean Entropy for different K values.
gg_compare_k(ck)gg_compare_k(ck)
ck |
Data.frame from |
A ggplot object.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
Plots a heatmap of the confusion matrix between estimated and known true phase assignments.
gg_confusion(object, true_labels)gg_confusion(object, true_labels)
object |
A |
true_labels |
Integer vector of known true phase assignments. |
A ggplot object.
confusion_matrix, adjusted_rand_index
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
if (requireNamespace("ggplot2", quietly = TRUE)) { x <- archaeo_sim(n = 80, k = 3, seed = 1, mixing = 0.05) fit <- fit_sef(x, k = 3, seed = 1) gg_confusion(fit, x$true_phase) }if (requireNamespace("ggplot2", quietly = TRUE)) { x <- archaeo_sim(n = 80, k = 3, seed = 1, mixing = 0.05) fit <- fit_sef(x, k = 3, seed = 1) gg_confusion(fit, x$true_phase) }
Plots the log-likelihood at each EM iteration to verify convergence.
gg_convergence(object)gg_convergence(object)
object |
A |
A ggplot object.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
if (requireNamespace("ggplot2", quietly = TRUE)) { x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3) gg_convergence(fit) }if (requireNamespace("ggplot2", quietly = TRUE)) { x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3) gg_convergence(fit) }
Plots mean test and training log-likelihood across K values from
cv_sef output.
gg_cv(cv_result)gg_cv(cv_result)
cv_result |
Data.frame returned by |
A ggplot object.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
Diverging lollipop of the signed chronological offset (chrono_gap)
for finds classified as residual (older-than-context) or latent
(younger-than-context) by detect_intrusions.
gg_direction(object, top_n = 20)gg_direction(object, top_n = 20)
object |
A |
top_n |
Maximum number of finds to show, by largest absolute gap (default: 20). |
A ggplot object. When every dated find is in context (e.g. unit-tied chronology) an explanatory placeholder plot is returned.
detect_intrusions, gg_intrusions
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2, context = "context") gg_direction(fit)x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2, context = "context") gg_direction(fit)
Shows ESE values across space. High energy indicates zones where neighbouring artefacts are dissimilar (depositional disruption).
gg_energy(object, xlabel = "Easting (m)", ylabel = "Northing (m)")gg_energy(object, xlabel = "Easting (m)", ylabel = "Northing (m)")
object |
A |
xlabel, ylabel
|
Axis labels. |
A ggplot object.
gg_entropy,
as_plotly for interactive version.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
Shows Shannon entropy of phase probabilities across the excavation area. High entropy = uncertain phase assignment (possible palimpsest zone).
gg_entropy(object, xlabel = "Easting (m)", ylabel = "Northing (m)")gg_entropy(object, xlabel = "Easting (m)", ylabel = "Northing (m)")
object |
A |
xlabel, ylabel
|
Axis labels. |
A ggplot object.
plot_entropy for base R version,
as_plotly for interactive version.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
Maps the probability that each find is an intrusion (displaced or redeposited). Top suspects are circled and labelled.
gg_intrusions( object, top_n = 5, xlabel = "Easting (m)", ylabel = "Northing (m)" )gg_intrusions( object, top_n = 5, xlabel = "Easting (m)", ylabel = "Northing (m)" )
object |
A |
top_n |
Number of top intrusions to label. |
xlabel, ylabel
|
Axis labels. |
A ggplot object.
detect_intrusions,
as_plotly for interactive version.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
Visualises the output of type_longevity() as a horizontal Gantt
chart: one segment per class spanning longevity_min to
longevity_max, ordered by longevity_min, coloured by
dominant_phase.
gg_longevity(longevity_table, fit = NULL)gg_longevity(longevity_table, fit = NULL)
longevity_table |
A data.frame as returned by |
fit |
Optional |
A ggplot object.
x <- archaeo_sim(n = 90, k = 3, seed = 1) fit <- fit_sef(x, k = 3, context = "context") tl <- type_longevity(fit) if (requireNamespace("ggplot2", quietly = TRUE)) gg_longevity(tl, fit)x <- archaeo_sim(n = 90, k = 3, seed = 1) fit <- fit_sef(x, k = 3, context = "context") tl <- type_longevity(fit) if (requireNamespace("ggplot2", quietly = TRUE)) gg_longevity(tl, fit)
Displays SEF analysis results directly on the excavation plan. Polygons represent stratigraphic units; points represent individual finds.
gg_map( object, geometries, layer = c("phase", "entropy", "energy", "intrusion"), show_labels = TRUE, show_points = TRUE, xlabel = NULL, ylabel = NULL )gg_map( object, geometries, layer = c("phase", "entropy", "energy", "intrusion"), show_labels = TRUE, show_points = TRUE, xlabel = NULL, ylabel = NULL )
object |
A |
geometries |
An sf object with excavation plan polygons.
Must contain a |
layer |
What to display: |
show_labels |
Show US labels on polygons. |
show_points |
Overlay individual finds as points. |
xlabel, ylabel
|
Axis labels (default: NULL = use CRS units). |
A ggplot object.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
Non-spatial ranking of the per-find intrusion probability (complements
gg_intrusions, which is the map). Uses the noise-component
posterior when the model was fitted with noise = TRUE, otherwise the
heuristic composite score.
gg_outliers(object, threshold = 0.5, top_n = 20)gg_outliers(object, threshold = 0.5, top_n = 20)
object |
A |
threshold |
Reference probability above which finds are highlighted (default: 0.5). |
top_n |
Number of highest-scoring finds to show (default: 20). |
A ggplot object.
detect_intrusions, gg_intrusions
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2, noise = TRUE) gg_outliers(fit)x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2, noise = TRUE) gg_outliers(fit)
Visualises the estimated per-phase categorical class profile
(object$cat_prob) from a multinomial-class fit.
gg_phase_composition(object, type = c("heatmap", "bar"), top_n = NULL)gg_phase_composition(object, type = c("heatmap", "bar"), top_n = NULL)
object |
A |
type |
Either |
top_n |
For |
A ggplot object.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3) gg_phase_composition(fit)x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3) gg_phase_composition(fit)
Plots finds along the depth (z) axis, coloured by phase assignment, to visualise the stratigraphic ordering of phases.
gg_phase_profile(object)gg_phase_profile(object)
object |
A |
A ggplot object.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
if (requireNamespace("ggplot2", quietly = TRUE)) { x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3) gg_phase_profile(fit) }if (requireNamespace("ggplot2", quietly = TRUE)) { x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3) gg_phase_profile(fit) }
Plots artefact positions coloured by dominant phase, with point size proportional to assignment confidence.
gg_phasefield(object, xlabel = "Easting (m)", ylabel = "Northing (m)")gg_phasefield(object, xlabel = "Easting (m)", ylabel = "Northing (m)")
object |
A |
xlabel, ylabel
|
Axis labels (default: "Easting (m)" / "Northing (m)"). |
A ggplot object.
plot_phasefield for base R version,
as_plotly for interactive version.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
Shows, per stratigraphic unit, whether the finds were assigned to a single
phase (coherent) or split across several (split). A coherent assignment is
expected because a unit is one depositional event. An optional second fit
(e.g. a class_model = "gaussian" fit) is shown side by side.
gg_unit_coherence(object, compare = NULL)gg_unit_coherence(object, compare = NULL)
object |
A |
compare |
Optional second |
A ggplot object.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3, context = "context") gg_unit_coherence(fit)x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3, context = "context") gg_unit_coherence(fit)
Plots the mean test log-likelihood across weight configurations
from optimize_weights output.
gg_weights(opt_result)gg_weights(opt_result)
opt_result |
List returned by |
A ggplot object.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
plot_energy(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
Infers vertical ordering between stratigraphic units from the mean depth of finds in each context, and builds a penalty matrix that discourages finds from different vertical zones being assigned to the same phase.
harris_from_contexts( data, z_col = "z", context_col = "context", penalty_scale = 0.5, exclude_contexts = NULL )harris_from_contexts( data, z_col = "z", context_col = "context", penalty_scale = 0.5, exclude_contexts = NULL )
data |
A data.frame with find data. |
z_col |
Name of the depth column. |
context_col |
Name of the context column. |
penalty_scale |
Penalty magnitude for cross-context assignments. |
exclude_contexts |
Optional character vector of context labels that are
exempt from the verticality penalty (e.g. fills of cuts, or multi-period
contexts where depth does not track chronology). Finds in these contexts
receive a zero cross-context penalty, so they neither impose nor are
subject to the depth-rank constraint. Defaults to |
An symmetric penalty matrix.
This function encodes a verticality rule: it assumes that the mean
depth of a context is a proxy for its relative chronological position
(deeper = earlier) and penalises same-phase assignment of finds whose
contexts differ in depth rank. The assumption holds for sub-horizontally
stratified deposits but is frequently violated in real
excavations — for inclined deposits and slope collapses, and especially
for the fills of cuts (pits, ditches, post-holes) and for single contexts
that contain material from several chronological periods. In such cases the
depth-rank penalty conflates genuinely problematic configurations with
perfectly normal archaeological features. Two escape hatches are provided:
pass the affected contexts to exclude_contexts to exempt them from
the penalty, or skip this helper entirely and supply an explicitly recorded
Harris matrix via read_harris.
Other harris:
read_harris(),
validate_phases_harris()
x <- archaeo_sim(n = 60, k = 2, seed = 1) H <- harris_from_contexts(x, z_col = "z", context_col = "context") dim(H) # Exempt a known multi-period fill from the verticality rule: H2 <- harris_from_contexts(x, exclude_contexts = "SU_3")x <- archaeo_sim(n = 60, k = 2, seed = 1) H <- harris_from_contexts(x, z_col = "z", context_col = "context") dim(H) # Exempt a known multi-period fill from the verticality rule: H2 <- harris_from_contexts(x, exclude_contexts = "SU_3")
Opens an interactive Shiny application for loading data, fitting the SEF model, and exploring results through plots, tables, and maps.
launch_app()launch_app()
The app requires the following packages (installed but not
imported by palimpsestr): shiny, shinydashboard, DT.
For interactive plots install plotly. For database connections
install DBI plus RSQLite or RPostgres. For GIS
maps install sf.
Launches the Shiny app (does not return).
## Not run: launch_app() ## End(Not run)## Not run: launch_app() ## End(Not run)
Reads stratigraphic unit polygons from Shapefile, GeoPackage,
GeoJSON, or a PostGIS database for use with gg_map.
load_geometries( source, layer = NULL, query = NULL, us_column = "us", crs = NULL )load_geometries( source, layer = NULL, query = NULL, us_column = "us", crs = NULL )
source |
File path or |
layer |
Layer name for multi-layer sources. |
query |
SQL query for database connections. |
us_column |
Column containing US/context identifiers. |
crs |
Target CRS for reprojection (optional). |
An sf object with a us column.
Other data-import:
read_db(),
read_pyarchinit()
Aggregates the SEI matrix by row, yielding a per-observation measure of total depositional coherence with all other finds.
local_sei(sei_mat)local_sei(sei_mat)
sei_mat |
A symmetric SEI matrix from |
A numeric vector of length nrow(sei_mat).
Other SEI:
sei_matrix(),
sei_sparse()
x <- archaeo_sim(n = 30, k = 2, seed = 1) S <- sei_matrix(x) lsei <- local_sei(S) summary(lsei)x <- archaeo_sim(n = 30, k = 2, seed = 1) S <- sei_matrix(x) lsei <- local_sei(S) summary(lsei)
Tests a grid of weight configurations and selects the one that maximises the mean held-out log-likelihood across folds. This provides a data-driven alternative to manual weight specification.
optimize_weights( data, k, weight_grid = NULL, n_folds = 3, seed = 1, verbose = TRUE, var_structure = "spherical", ... )optimize_weights( data, k, weight_grid = NULL, n_folds = 3, seed = 1, verbose = TRUE, var_structure = "spherical", ... )
data |
A data.frame with archaeological find data. |
k |
Number of phases. |
weight_grid |
A data.frame with columns |
n_folds |
Number of cross-validation folds (default: 3). |
seed |
Random seed. |
verbose |
Print progress (default: TRUE). |
var_structure |
Variance structure used for the weight-selection
fits (default: |
... |
Additional arguments passed to |
Since v0.14.0 the weights scale the feature dimensions and therefore
enter the mixture likelihood (in earlier versions they only affected
the SEI matrix, so the cross-validated objective was invariant to
them). Two technical points make the comparison meaningful. First,
free diagonal variances absorb any rescaling of the feature columns,
so weight selection is performed under
var_structure = "spherical", where the weights define the
relative precision of the dimensions and are identifiable; the
returned best_weights are therefore meant to be used with
fit_sef(..., var_structure = "spherical"). Second, held-out
log-likelihoods are mapped back to the common unweighted feature
scale via a Jacobian correction () so
that configurations with different weights are directly comparable.
A list with components:
Named numeric vector of optimal weights.
Data.frame with all tested configurations and their scores.
Other validation:
adjusted_rand_index(),
bootstrap_sef(),
confusion_matrix(),
cv_sef()
x <- archaeo_sim(n = 80, k = 3, seed = 1) opt <- optimize_weights(x, k = 3, n_folds = 3) opt$best_weightsx <- archaeo_sim(n = 80, k = 3, seed = 1) opt <- optimize_weights(x, k = 3, n_folds = 3) opt$best_weights
Measures global phase separability as , where
is the mean per-find Shannon entropy of the soft phase
assignments and the number of phases. Values close to 1 indicate
well-separated phases (low entropy); values near 0 indicate a compressed,
heavily mixed palimpsest. As a rule of thumb, PDI indicates
well-resolved phases. The index is an interpretable, dimensionless summary
reported alongside the BIC (see compare_k) during model
selection; unlike the BIC it does not penalise model complexity, so it
should not be used on its own to choose .
pdi(object)pdi(object)
object |
A |
A single numeric value between 0 and 1.
Other diagnostics:
as_phase_table(),
detect_intrusions(),
ese(),
phase_composition(),
phase_diagnostic_table(),
predict_phase(),
recommend_setup(),
type_longevity()
x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) pdi(fit)x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) pdi(fit)
Returns the estimated categorical class profile of each phase from a fit
made with class_model = "multinomial": row j gives the
probability that a find assigned to phase j belongs to each material
class. This is the model-based counterpart of cross-tabulating finds by
phase and class, and is directly interpretable (e.g. "phase 2 is dominated
by amphorae and fine ware").
phase_composition(object)phase_composition(object)
object |
A |
A data.frame with a phase column and one column per class
level; each row sums to 1.
Other diagnostics:
as_phase_table(),
detect_intrusions(),
ese(),
pdi(),
phase_diagnostic_table(),
predict_phase(),
recommend_setup(),
type_longevity()
x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3) phase_composition(fit)x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3) phase_composition(fit)
Combines the input data with dominant phase, phase probabilities, entropy, local SEI, and energy in a single data.frame.
phase_diagnostic_table(object)phase_diagnostic_table(object)
object |
A |
A data.frame with all input columns plus diagnostics.
Other diagnostics:
as_phase_table(),
detect_intrusions(),
ese(),
pdi(),
phase_composition(),
predict_phase(),
recommend_setup(),
type_longevity()
x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) pdt <- phase_diagnostic_table(fit) names(pdt)x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) pdt <- phase_diagnostic_table(fit) names(pdt)
Computes how often finds from phase are found directly
above finds from phase in the vertical sequence,
revealing the stratigraphic ordering of phases.
phase_transition_matrix(object)phase_transition_matrix(object)
object |
A |
A matrix where entry [i,j]
counts transitions from phase i (above) to phase j (below).
Other export:
export_results(),
us_summary_table()
x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3) phase_transition_matrix(fit)x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3) phase_transition_matrix(fit)
Plot local Excavation Stratigraphic Energy (base R)
plot_energy(object)plot_energy(object)
object |
A |
Invisibly returns the object.
gg_energy for the ggplot2 version.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_entropy(),
plot_phasefield(),
plot_sei_profile()
x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) plot_energy(fit)x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) plot_energy(fit)
Plot entropy across space (base R)
plot_entropy(object)plot_entropy(object)
object |
A |
Invisibly returns the object.
gg_entropy for the ggplot2/plotly version.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_phasefield(),
plot_sei_profile()
x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) plot_entropy(fit)x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) plot_entropy(fit)
Plot dominant phase assignment (base R)
plot_phasefield(object)plot_phasefield(object)
object |
A |
Invisibly returns the object.
gg_phasefield for the ggplot2/plotly version.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_sei_profile()
x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) plot_phasefield(fit)x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) plot_phasefield(fit)
Plot ordered SEI profile (base R)
plot_sei_profile(object)plot_sei_profile(object)
object |
A |
Invisibly returns the object.
Other plotting:
as_plotly(),
gg_bootstrap(),
gg_compare_k(),
gg_confusion(),
gg_convergence(),
gg_cv(),
gg_direction(),
gg_energy(),
gg_entropy(),
gg_intrusions(),
gg_map(),
gg_outliers(),
gg_phase_composition(),
gg_phase_profile(),
gg_phasefield(),
gg_unit_coherence(),
gg_weights(),
plot_energy(),
plot_entropy(),
plot_phasefield()
x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) plot_sei_profile(fit)x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) plot_sei_profile(fit)
Convenience alias for as_phase_table.
predict_phase(object)predict_phase(object)
object |
A |
A data.frame with probabilities and diagnostics.
Other diagnostics:
as_phase_table(),
detect_intrusions(),
ese(),
pdi(),
phase_composition(),
phase_diagnostic_table(),
recommend_setup(),
type_longevity()
x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) head(predict_phase(fit))x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) head(predict_phase(fit))
Loads find data from any DBI-compatible database and maps columns
to the format expected by fit_sef.
read_db( conn, query = NULL, table = NULL, col_id = "id", col_x = "x", col_y = "y", col_z = "z", col_context = "context", col_date_min = "date_min", col_date_max = "date_max", col_class = "class", col_taf = NULL, schema = "custom", sito = NULL )read_db( conn, query = NULL, table = NULL, col_id = "id", col_x = "x", col_y = "y", col_z = "z", col_context = "context", col_date_min = "date_min", col_date_max = "date_max", col_class = "class", col_taf = NULL, schema = "custom", sito = NULL )
conn |
A |
query |
SQL query returning data with the required columns. |
table |
Table name (alternative to |
col_id, col_x, col_y, col_z, col_context, col_date_min, col_date_max, col_class, col_taf
|
Column name mappings. |
schema |
Use |
sito |
Site filter for PyArchInit schema. |
A data.frame ready for fit_sef.
Other data-import:
load_geometries(),
read_pyarchinit()
Reads a CSV file with columns from, to, and optionally
weight, and converts it to an penalty
matrix aligned with the find-level data.
read_harris(file, contexts, default_weight = 1)read_harris(file, contexts, default_weight = 1)
file |
Path to CSV with columns |
contexts |
Character vector of context labels for each find (length = number of finds). |
default_weight |
Weight for edges without an explicit weight. |
An penalty matrix.
Other harris:
harris_from_contexts(),
validate_phases_harris()
Reads finds from a pyArchInit database and assembles a data.frame ready for
fit_sef. Finds come from inventario_materiali_table
(materials) and/or pottery_table (pottery), selectable via
source. A find uses its own plan coordinates when it is plotted as a
point in pyarchinit_reperti; otherwise it inherits its stratigraphic
unit's coordinates (centroid of the US polygon). Chronology comes from
us_table, while elevation follows archaeological practice (see
Details).
read_pyarchinit( con, us_geometry = NULL, sito = NULL, source = c("both", "materials", "pottery"), date_labels = NULL, taf = NULL, us_geom_field = NULL, synthetic_coords = TRUE, quote_table = "pyarchinit_quote", chronology_table = "palimpsest_chronology", pottery_class = c("ware", "material", "form"), reperti_table = "pyarchinit_reperti", reperti_geometry = NULL )read_pyarchinit( con, us_geometry = NULL, sito = NULL, source = c("both", "materials", "pottery"), date_labels = NULL, taf = NULL, us_geom_field = NULL, synthetic_coords = TRUE, quote_table = "pyarchinit_quote", chronology_table = "palimpsest_chronology", pottery_class = c("ware", "material", "form"), reperti_table = "pyarchinit_reperti", reperti_geometry = NULL )
con |
A |
us_geometry |
Optional |
sito |
Optional site filter (matched against the |
source |
One of |
date_labels |
Optional named list of period label -> |
taf |
Optional taphonomic score: a single value applied to all finds, or
a vector named by find |
us_geom_field |
Name of the US-identifier column in |
synthetic_coords |
Logical (default |
quote_table |
Name of the US-elevation point table
(default |
chronology_table |
Name of an optional per-US absolute-chronology table
(default |
pottery_class |
Character vector of |
reperti_table |
Name of the pyArchInit piece-plotted-finds point layer
(default |
reperti_geometry |
Optional pre-read |
Elevation (z) is sourced, in order of precedence:
the find's own quota_usm (materials only);
the mean US elevation from pyarchinit_quote (quota_q),
joined on (sito, area, us) with an (sito, us) fallback;
0 when neither is available.
The deprecated us_table.quota_abs form field is not used.
Pottery has no elevation column, so it always inherits its US elevation.
Dating is resolved from us_table.datazione (falling back to the find's
own dating) by an internal archaeological-date parser.
A data.frame with id, x, y, z,
date_min, date_max, class, context,
taf_score (plus find_source when source = "both"),
ready for fit_sef.
Other data-import:
load_geometries(),
read_db()
fit_sef() flagsExamines the structural properties of a palimpsestr-ready dataset and
returns a set of recommended fit_sef() options based on simple
heuristics. Useful when the dataset has subtle properties (US-centroid
coordinates, US-tied chronology, imbalanced classes, informative
taphonomic scores) that would otherwise be discovered only by trial
and error.
recommend_setup( data, coords = c("x", "y", "z"), chrono = c("date_min", "date_max"), class_col = "class", tafonomy = NULL, context = NULL )recommend_setup( data, coords = c("x", "y", "z"), chrono = c("date_min", "date_max"), class_col = "class", tafonomy = NULL, context = NULL )
data |
A data.frame with archaeological find data. |
coords |
Character vector of length 3 naming the x, y, z coordinate
columns (default: |
chrono |
Character vector of length 2 naming the min and max
chronology columns (default: |
class_col |
Character scalar with the class column name
(default: |
tafonomy |
Optional column name for taphonomic scores. |
context |
Optional column name for stratigraphic unit labels. |
An S3 object of class sef_setup with:
recommendations: named list with logical flags
class_scale, taf_as_feature, chrono_precision,
residuality.
diagnostics: list of structural properties detected in
the data (number of classes, singleton classes, whether coordinates
are at unit centroid, whether chronology is US-tied, whether taf is
informative, etc.).
warnings: character vector of caveats (e.g. directional
intrusion will be uninformative because chronology is US-tied).
suggested_call: string with the recommended
fit_sef() invocation.
Other diagnostics:
as_phase_table(),
detect_intrusions(),
ese(),
pdi(),
phase_composition(),
phase_diagnostic_table(),
predict_phase(),
type_longevity()
x <- archaeo_sim(n = 100, k = 3, seed = 1) recommend_setup(x, context = "context") data(villa_romana) recommend_setup(villa_romana, context = "context", tafonomy = "taf_score")x <- archaeo_sim(n = 100, k = 3, seed = 1) recommend_setup(x, context = "context") data(villa_romana) recommend_setup(villa_romana, context = "context", tafonomy = "taf_score")
Relabels phases so that phase 1 corresponds to the deepest (oldest) stratum and phase K to the shallowest (most recent). This ensures consistent, interpretable phase numbering across different runs.
reorder_phases(object)reorder_phases(object)
object |
A |
A sef_fit object with reordered phases.
Other fitting:
compare_k(),
fit_sef(),
sef_summary()
x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3) fit <- reorder_phases(fit)x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3) fit <- reorder_phases(fit)
Produces a structured Markdown report covering phase composition, intrusion detection, stratigraphic unit purity, and recommendations. Available in English and Italian.
report_sef(object, lang = c("en", "it"), file = NULL)report_sef(object, lang = c("en", "it"), file = NULL)
object |
A |
lang |
Language: |
file |
Optional file path to save the report. |
Character string with the report text (invisibly).
x <- archaeo_sim(n = 100, k = 3, seed = 1) fit <- fit_sef(x, k = 3) report_sef(fit, lang = "en")x <- archaeo_sim(n = 100, k = 3, seed = 1) fit <- fit_sef(x, k = 3) report_sef(fit, lang = "en")
Returns a named list with global diagnostics and phase counts.
sef_summary(object)sef_summary(object)
object |
A |
A named list.
Other fitting:
compare_k(),
fit_sef(),
reorder_phases()
x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) sef_summary(fit)x <- archaeo_sim(n = 60, k = 2, seed = 1) fit <- fit_sef(x, k = 2) sef_summary(fit)
Builds an symmetric matrix quantifying pairwise
depositional coherence from spatial, vertical, temporal, and cultural evidence.
sei_matrix( data, coords = c("x", "y", "z"), chrono = c("date_min", "date_max"), class_col = "class", weights = c(ws = 1, wz = 1, wt = 1, wc = 1), eps = 1e-09, z_floor = 0.25, max_dist = NULL )sei_matrix( data, coords = c("x", "y", "z"), chrono = c("date_min", "date_max"), class_col = "class", weights = c(ws = 1, wz = 1, wt = 1, wc = 1), eps = 1e-09, z_floor = 0.25, max_dist = NULL )
data |
Input data.frame. |
coords |
Character vector of coordinate column names (x, y, z). |
chrono |
Character vector with minimum and maximum dating columns. |
class_col |
Class column name. |
weights |
Named numeric vector with components |
eps |
Deprecated and ignored (kept for backward compatibility). Since v0.14.0 the spatial component uses a bounded exponential kernel with a data-driven bandwidth, so no epsilon guard is needed. |
z_floor |
Deprecated and ignored (kept for backward compatibility). Since v0.14.0 the vertical component uses a bounded exponential kernel with a data-driven bandwidth, so no floor is needed. |
max_dist |
Maximum spatial distance for pair inclusion. When
|
A symmetric numeric matrix with zero diagonal.
SEI values are normalised within each dataset. Absolute SEI values are not directly comparable across different excavations or datasets of different sizes. Use SEI for within-dataset ranking and relative comparisons only.
Other SEI:
local_sei(),
sei_sparse()
x <- archaeo_sim(n = 30, k = 2, seed = 1) S <- sei_matrix(x) dim(S)x <- archaeo_sim(n = 30, k = 2, seed = 1) S <- sei_matrix(x) dim(S)
Wrapper around sei_matrix that automatically sets
max_dist when the dataset exceeds a size threshold,
using the 25th percentile of pairwise distances as the cutoff.
sei_sparse( data, coords = c("x", "y", "z"), chrono = c("date_min", "date_max"), class_col = "class", weights = c(ws = 1, wz = 1, wt = 1, wc = 1), eps = 1e-09, z_floor = 0.25, n_threshold = 1000 )sei_sparse( data, coords = c("x", "y", "z"), chrono = c("date_min", "date_max"), class_col = "class", weights = c(ws = 1, wz = 1, wt = 1, wc = 1), eps = 1e-09, z_floor = 0.25, n_threshold = 1000 )
data |
Input data.frame. |
coords |
Character vector of coordinate column names (x, y, z). |
chrono |
Character vector with minimum and maximum dating columns. |
class_col |
Class column name. |
weights |
Named numeric vector with components |
eps |
Deprecated and ignored (kept for backward compatibility). Since v0.14.0 the spatial component uses a bounded exponential kernel with a data-driven bandwidth, so no epsilon guard is needed. |
z_floor |
Deprecated and ignored (kept for backward compatibility). Since v0.14.0 the vertical component uses a bounded exponential kernel with a data-driven bandwidth, so no floor is needed. |
n_threshold |
Datasets larger than this trigger sparse mode (default: 1000). |
A numeric matrix (same as sei_matrix).
Other SEI:
local_sei(),
sei_matrix()
x <- archaeo_sim(n = 50, k = 2, seed = 1) S <- sei_sparse(x)x <- archaeo_sim(n = 50, k = 2, seed = 1) S <- sei_sparse(x)
Summarise a fitted SEF model
## S3 method for class 'sef_fit' summary(object, ...)## S3 method for class 'sef_fit' summary(object, ...)
object |
A |
... |
Ignored. |
A named list.
For each cultural class (or sub-class), returns the temporal envelope of
the depositional phases in which the class has non-negligible posterior
membership. The longevity is the union of the per-phase chronological
envelopes for those phases whose mean posterior weight (averaged over the
finds of that class) exceeds posterior_threshold.
type_longevity( object, class_col = NULL, posterior_threshold = 0.1, sub_class = FALSE )type_longevity( object, class_col = NULL, posterior_threshold = 0.1, sub_class = FALSE )
object |
A |
class_col |
Optional. Column name to use as the class label. Defaults
to the |
posterior_threshold |
Numeric in |
sub_class |
Logical. If TRUE and |
A data.frame with columns class, longevity_min,
longevity_max, longevity_span, dominant_phase,
n_finds, and a list-column weight_matrix containing
the per-phase posterior weights (length K).
detect_intrusions, gg_longevity
Other diagnostics:
as_phase_table(),
detect_intrusions(),
ese(),
pdi(),
phase_composition(),
phase_diagnostic_table(),
predict_phase(),
recommend_setup()
x <- archaeo_sim(n = 90, k = 3, seed = 1) fit <- fit_sef(x, k = 3, context = "context") type_longevity(fit)x <- archaeo_sim(n = 90, k = 3, seed = 1) fit <- fit_sef(x, k = 3, context = "context") type_longevity(fit)
Aggregates finds by context (US), reporting the dominant phase, purity (proportion of finds in dominant phase), mean entropy, mean energy, and intrusion count.
us_summary_table(object)us_summary_table(object)
object |
A |
A data.frame with one row per stratigraphic unit.
export_results, phase_diagnostic_table
Other export:
export_results(),
phase_transition_matrix()
x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3, context = "context") us_summary_table(fit)x <- archaeo_sim(n = 80, k = 3, seed = 1) fit <- fit_sef(x, k = 3, context = "context") us_summary_table(fit)
Checks whether the estimated phases follow the expected vertical ordering within each stratigraphic unit pair.
validate_phases_harris(object)validate_phases_harris(object)
object |
A |
A data.frame with one row per context pair, indicating whether the dominant phase ordering is consistent with depth.
harris_from_contexts, us_summary_table
Other harris:
harris_from_contexts(),
read_harris()
x <- archaeo_sim(n = 60, k = 3, seed = 1) fit <- fit_sef(x, k = 3, context = "context") validate_phases_harris(fit)x <- archaeo_sim(n = 60, k = 3, seed = 1) fit <- fit_sef(x, k = 3, context = "context") validate_phases_harris(fit)
Real archaeological dataset from the site of Poggio Gramignano (Lugnano in Teverina, TR, Italy), a multi-period Roman villa with an annexed Late Antique infant cemetery. The dataset contains 615 inventoried finds from 54 stratigraphic units spanning from the Pre-Roman period (7th–4th c. BCE) through the Late Antique (5th–6th c. CE).
villa_romanavilla_romana
A data.frame with 615 rows and 9 variables:
Unique find identifier (VRPG_0001 to VRPG_0615)
Easting coordinate (metres, EPSG:3004)
Northing coordinate (metres, EPSG:3004)
Mean elevation of the stratigraphic unit (metres a.s.l.)
Stratigraphic unit label (e.g. US_76, US_117)
Start of chronological interval (BCE as negative, CE as positive)
End of chronological interval
Material class (e.g. Reperto Ceramico, Reperto Anforaceo)
Taphonomic integrity score (0 = fully disturbed, 1 = pristine in situ)
Chronological data derives from the site periodisation. Coordinates are US centroids in Monte Mario / Italy zone 2 (EPSG:3004). Elevation (z) is the mean of recorded quotes per US. Taphonomic scores were assigned by the excavator based on depositional context (primary, secondary, redeposition, backfill, disturbance).
Data from the pyArchInit database of Poggio Gramignano (VRPG 3004). Taphonomic scores compiled by Roberto Montagnetti.
data(villa_romana) str(villa_romana) table(villa_romana$class)data(villa_romana) str(villa_romana) table(villa_romana$class)