% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/validate.R
\name{validate_against_records}
\alias{validate_against_records}
\title{Validate suitability predictions against known presence/absence records}
\usage{
validate_against_records(
  predicted,
  records,
  presence_col = "presence",
  match_radius_deg = 0.002,
  plot = TRUE,
  verbose = TRUE
)
}
\arguments{
\item{predicted}{Dataframe from \code{\link[=predict_oyster]{predict_oyster()}} containing \code{lat}, \code{lon},
and \code{suitability} columns.}

\item{records}{Dataframe of known presence/absence records. Must contain:
\itemize{
\item Coordinate columns (\code{lat}/\code{lon} or equivalents)
\item A presence column: 1 = present, 0 = absent (or logical TRUE/FALSE).
}}

\item{presence_col}{Character. Name of the presence/absence column in
\code{records} (default \code{"presence"}).}

\item{match_radius_deg}{Numeric. Radius in decimal degrees within which a
prediction cell is matched to a record (default \code{0.002} approx. 220 m).
Records that fall outside all prediction cells at this radius are dropped
with a warning.}

\item{plot}{Logical. Print an ASCII ROC curve to the console (default \code{TRUE}).}

\item{verbose}{Logical. Print full metric summary (default \code{TRUE}).}
}
\value{
A named list:
\itemize{
\item \code{auc}: numeric [0,1]
\item \code{optimal_threshold}: numeric [0,1] -- Youden's J
\item \code{sensitivity}: numeric [0,1] at optimal threshold
\item \code{specificity}: numeric [0,1] at optimal threshold
\item \code{f1}: numeric [0,1] at optimal threshold
\item \code{tss}: numeric [-1,1] at optimal threshold
\item \code{brier_score}: numeric [0,1]
\item \code{n_presences}: integer
\item \code{n_absences}: integer
\item \code{n_unmatched}: integer -- records dropped due to no nearby prediction
\item \code{roc_df}: dataframe with columns \code{threshold}, \code{sensitivity},
\code{specificity} (for custom plotting)
}
}
\description{
Compares the suitability scores from \code{\link[=predict_oyster]{predict_oyster()}} against a
presence/absence dataset to quantify how well the model discriminates
between occupied and unoccupied habitat. Useful for assessing whether the
AHP-weighted scoring produces ecologically defensible outputs before using
them for site selection.

\strong{Metrics computed:}
\itemize{
\item \strong{AUC} (Area Under the ROC Curve): probability that a random presence
location scores higher than a random absence location. 0.5 = random;
1.0 = perfect discrimination.
\item \strong{Optimal threshold:} Youden's J statistic (maximises sensitivity +
specificity - 1). Used for the confusion-matrix-derived metrics below.
\item \strong{Sensitivity} (true positive rate): fraction of presences correctly
predicted above threshold.
\item \strong{Specificity} (true negative rate): fraction of absences correctly
predicted below threshold.
\item \strong{F1 score}: harmonic mean of precision and sensitivity.
\item \strong{Brier score}: mean squared error between predicted probability and
observed presence/absence; 0 = perfect, 0.25 = uninformative.
\item \strong{TSS} (True Skill Statistic = sensitivity + specificity - 1): values
above 0.4 generally indicate useful predictive skill.
}
}
\examples{
\donttest{
# Load known flat oyster records (e.g. from NBN Atlas CSV export)
records <- read.csv("nbn_ostrea_edulis.csv")

# Run prediction on same area
result <- predict_oyster(survey, "ostrea_edulis")

# Validate
val <- validate_against_records(result, records,
                                 presence_col = "presence")
val$auc           # overall discrimination power
val$tss           # skill score
val$roc_df        # data for custom ggplot
}
}
