--- title: "A workflow for multiple spatial cluster detection" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{A workflow for multiple spatial cluster detection} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 6, fig.height = 4) library(multiflexscan) ``` This workflow starts with region-level counts and ends with a selected cluster set, a global test, model diagnostics, and a map. The synthetic data keep the example self-contained and quick; they do not represent a disease study. ## Prepare aligned spatial inputs Every vector and the neighbor list must use the same region order. Observations are nonnegative integer counts; expected counts are positive and should reflect the appropriate population standardization. Coordinates must be Cartesian. Here, a regular grid supplies coordinates and neighbors sharing an edge. ```{r data} grid <- expand.grid(x = 1:5, y = 1:5) nb <- lapply(seq_len(nrow(grid)), function(i) which( abs(grid$x - grid$x[i]) + abs(grid$y - grid$y[i]) == 1)) observed <- rep(10, 25) observed[c(1, 2, 6, 7)] <- 30 expected <- rep(mean(observed), 25) stopifnot(length(nb) == length(observed), all(expected > 0)) ``` For real polygons, `sf::st_transform()` can project centroids and `spdep::poly2nb()` can construct contiguity. Check substantively whether shared borders imply connectivity, and exclude uninhabited regions when appropriate. ## Search and test ```{r fit} fit <- multiflexscan(grid$x, grid$y, name = as.character(seq_len(25)), observed = observed, expected = expected, nb = nb, scanmethod = "CIRCULAR", clustersize = 4, maxclusters = 3, simcount = 99, cores = 1, seed = 20261002) print(fit) ``` `clustersize` limits each candidate window and `maxclusters` limits the number of candidates considered jointly. Use `scanmethod = "FLEXIBLE"` for connected, noncircular candidates and `stattype = "RESTRICTED"` for restricted likelihood ratios. The latter uses `ralpha`. Set `clustertype = "COLD"` for reduced-risk clusters. These choices change the candidate search, so compare scientifically plausible settings rather than choosing them to obtain a small p-value. The example uses only 99 simulations, so its p-values have a resolution of 0.01. Increase `simcount` for an applied analysis. A fixed `seed` makes repeated runs reproducible with the same software and parallel configuration. Set `cores` explicitly; `cores = 1` uses sequential simulation, while larger values use a parallel backend (PSOCK by default). Record both settings. ## Extract and interpret results ```{r results} nclusters(fit) pvalue(fit) clusters(fit) get_setting(fit)[c("simcount", "cores", "seed")] ``` `pvalue()` tests the selected set as a whole against no clustering. The `P` column of `clusters()` contains scan-statistic p-values for individual candidates; these are different tests. A significant global test does not establish significance of every selected cluster. `clusters(fit, selected = FALSE)` also returns candidates not selected by the information criterion. ```{r criteria} criteria <- model_criteria(fit) criteria model_criteria(fit, selected = TRUE) logLik(fit) AIC(fit) BIC(fit) C_criterion(fit) RDC(fit) plot(C ~ K, data = criteria, type = "b", xlab = "Number of clusters (K)", ylab = "C(K)") ``` The selected model minimizes `C`, equivalently maximizes `RDC`; it need not minimize AIC or BIC. The likelihood describes the Poisson model conditional on the candidate regions. Minus twice that likelihood is available as `-2 * as.numeric(logLik(fit))` or the `neg2logLik` column. `coef(fit)` returns cluster summaries, not regression coefficients; use `clusters()` explicitly. If the selected count reaches `maxclusters`, repeat with a larger limit. ## Visualize and save ```{r graph} plot(fit, xlab = "Grid x", ylab = "Grid y") ``` If polygons are available, `choropleth(fit, regions)` displays selected clusters on an `sf` object with matching region order. Both plot methods highlight the selected clusters by default. The following optional code saves the result and session information without needing to rerun simulations during reporting. ```{r save, eval=FALSE} saveRDS(list(fit = fit, session = sessionInfo()), "analysis.rds") saved <- readRDS("analysis.rds") clusters(saved$fit) ``` Inspect sensitivity to plausible neighborhood definitions, `clustersize`, `maxclusters`, and, for restricted scans, `ralpha`. A spatial association does not identify its cause. Expected counts and the Poisson assumption require substantive assessment; the package does not fit covariate effects or space-time models through its analysis interface.