Skip to contents

Applies the Kulldorff circular spatial scan statistic to detect geographic clusters of disease excess. The most-likely cluster and any significant secondary clusters are identified by Monte Carlo hypothesis testing. The analysis requires a tabular dataset with case counts and population denominators at the municipality level, plus an sf polygon layer providing municipality boundaries.

Usage

sus_mod_spatial_scan(
  df,
  cases,
  population,
  municipalities,
  expected = NULL,
  max_pop_frac = 0.5,
  n_simulations = 999L,
  alpha = 0.05,
  lang = c("pt", "en", "es"),
  verbose = TRUE
)

Arguments

df

A data.frame (or climasus_df) containing at minimum: a code_muni column (character or integer) and the columns named by cases, population, and optionally expected.

cases

Character. Name of the column in df with case counts (non-negative integer or numeric).

population

Character. Name of the column in df with the at-risk population denominator.

municipalities

An sf object with POLYGON or MULTIPOLYGON geometry and a code_muni column used to join to df.

expected

Character or NULL. Name of the column in df with the expected number of cases under the null model. When NULL (default), SpatialEpi::kulldorff() computes expected counts internally from the overall rate.

max_pop_frac

Numeric in (0, 1]. Maximum fraction of the total population that a single cluster window may contain. Default 0.5.

n_simulations

Positive integer. Number of Monte Carlo simulations for hypothesis testing. Default 999. Increase to 9999 for publication-quality p-values.

alpha

Numeric in (0, 1). Significance level for secondary cluster filtering. Default 0.05.

lang

Character. Output language: "pt" (default), "en", "es".

verbose

Logical. Print progress messages. Default TRUE.

Value

A climasus_spatial_scan object (named list) with:

$most_likely_cluster

Named list: location_ids (character vector of code_muni in the cluster), observed (numeric), expected (numeric), RR (relative risk), log_lik (log-likelihood ratio), p_value (Monte Carlo p-value).

$secondary_clusters

List of lists with the same structure as most_likely_cluster, one per significant secondary cluster. Empty list when no secondary cluster is significant.

$n_clusters

Integer. Total number of significant clusters (1 + length of secondary_clusters), or 0 if the most-likely cluster itself is not significant.

$data

Tibble with one row per municipality: code_muni, cases, population, expected, and a logical column in_mlc indicating membership in the most-likely cluster.

$meta

Named list of analysis parameters.

$call

The matched call.

Methodology

The scan statistic imposes circular windows of variable radius over the map. For each window the likelihood ratio (LR) under a Poisson model (observed vs. expected cases inside vs. outside the window) is computed. The window with the maximum LR is the most-likely cluster. Its p-value is obtained by comparing to the empirical distribution of max-LR under n_simulations Monte Carlo permutations. Secondary clusters are non- overlapping windows with LR > the alpha-level critical value from the same Monte Carlo distribution.

Computations are delegated to SpatialEpi::kulldorff(). Municipality centroids are derived from the polygon geometry using sf::st_centroid() after reprojecting to WGS 84 (EPSG 4326) so that longitude/latitude coordinates are passed to kulldorff().

References

Kulldorff, M. & Nagarwalla, N. (1995). Spatial disease clusters: detection and inference. Statistics in Medicine, 14(8), 799–810. doi:10.1002/sim.4780140809

Kulldorff, M. (1997). A spatial scan statistic. Communications in Statistics - Theory and Methods, 26(6), 1481–1496. doi:10.1080/03610929708831995

Kim, A. Y. & Wakefield, J. (2010). R data and methods for spatial epidemiology: the SpatialEpi package. University of Washington.

Examples

if (FALSE) { # \dontrun{
library(geobr)
library(dplyr)

# Download municipality polygons for Minas Gerais (state code 31)
muni_sf <- geobr::read_municipality(code_muni = 31, year = 2020)
muni_sf <- dplyr::rename(muni_sf, code_muni = code_muni)

# Build a synthetic data.frame
set.seed(42)
df_cases <- tibble::tibble(
  code_muni  = as.character(muni_sf$code_muni),
  cases      = rpois(nrow(muni_sf), lambda = 5),
  population = sample(5000:200000, nrow(muni_sf), replace = TRUE)
)

result <- sus_mod_spatial_scan(
  df            = df_cases,
  cases         = "cases",
  population    = "population",
  municipalities = muni_sf,
  n_simulations = 199,
  lang          = "pt"
)

print(result)
result$most_likely_cluster
result$secondary_clusters
} # }