Skip to contents

This function compute the spatial scan test for Bernoulli and Multinomial categorical spatial process, and detect spatial clusters

Usage

scan.test(formula = NULL, data = NULL, fx = NULL, coor = NULL, case = NULL,
nv = NULL, nsim = NULL, distr = NULL, windows = "circular", listw = NULL,
alternative = "High", minsize = 1, control = list())

Arguments

formula

a symbolic description of the factor (optional).

data

an (optional) data frame or a sf object containing the variable to testing for.

fx

a factor (optional).

coor

(optional) coordinates of observations.

case

Only for Bernoulli distribution. A element of factor, there are cases and non-cases for testing for cases versus non-cases

nv

Maximum windows size, default nv = N/2. The algorithm scan for clusters of geographic size between 1 and the upper limit (nv) defined by the user.

nsim

Number of permutations.

distr

distribution of the spatial process: "bernoulli" for two levels or "multinomial" for three or more levels.

windows

a string to select the type of cluster "circular" (default) of "elliptic".

listw

only for flexible windows. A neighbours list (an object of the class listw, nb or knn frop spdep) or an adjacency matrix.

alternative

Only for Bernoulli spatial process. A character string specifying the type of cluster, must be one of "High" (default), "Both" or "Low".

minsize

Minimum number of observations inside of Most Likely Cluster and secondary clusters.

control

List of additional control arguments.

Value

A object of the htest and scantest class

methodThe type of test applied ().
fxFactor included as input to get the scan test.
MLCObservations included into the Most Likelihood Cluster (MLC).
statisticValue of the scan test (maximum Log-likelihood ratio).
NTotal number of observations.
nnWindows used to get the cluster.
nvMaximum number of observations into the cluster.
data.nameA character string giving the name of the factor.
coorcoordinates.
alternativeOnly for bernoulli spatial process. A character string describing the alternative hypothesis select by the user.
p.valuep-value of the scan test.
cases.expectExpected cases into the MLC.
cases.observObserved cases into the MLC.
nsimNumber of permutations.
scan.mca (nsim x 1) vector with the loglik values under bootstrap permutation.
secondary.clustersa list with the observations included into the secondary clusters.
loglik.seconda vector with the value of the secondary scan tests (maximum Log-likelihood ratio).
p.value.secondarya vector with the p-value of the secondary scan tests.
Alternative.MLCA vector with the observations included in another cluster with the same loglik than MLC.

Details

Two alternative sets of arguments can be included in this function to compute the scan test:

  • Option 1: A factor (fx) and coordinates (coor).

  • Option 2: A sf object (data) and the formula to specify the factor. The function consider the coordinates of the centroids of the elements of th sf object.

The spatial scan statistics are widely used in epidemiology, criminology or ecology. Their purpose is to analyse the spatial distribution of points or geographical regions by testing the hypothesis of spatial randomness distribution on the basis of different distributions (e.g. Bernoulli, Poisson or Normal distributions). The scan.test function obtain the scan statistic for two relevant distributions related with categorical variables: the Bernoulli and Multinomial distribution.
The spatial scan statistic is based on the likelihood ratio test statistic and is formulated as follows:
$$\Delta = { \max_{z \in Z,H_A} L(\theta|z) \over \max_{z \in Z,H_0} L(\theta|z)}$$ where Z represents the collection of scanning windows constructed on the study region, \(H_A\) is an alternative hypothesis, \(H_0\) is a null hypothesis, and \(L(\theta|z)\) is the likelihood function with parameter \(\theta\) given window Z
. The null hypothesis says that there is no spatial clustering on the study region, and the alternative hypothesis is that there is a certain area with high (or low) rates of outcome variables. The null and alternative hypotheses and the likelihood function may be expressed in different ways depending on the probability model under consideration.
To test independence in a spatial process, under the null, the type of windows is irrelevant but under the alternative the elliptic windows can to identify with more precision the cluster.

For big data sets (N >>) the windows = "elliptic" can be so slowly

Bernoulli version

When we have dichotomous outcome variables, such as cases and noncases of certain diseases, the Bernoulli model is used. The null hypothesis is written as $$H_0 : p = q \ \ for \ \ all \ \ Z$$ and the alternative hypothesis is
$$H_A : p \neq q \ \ for \ \ some \ \ Z$$ where p and q are the outcome probabilities (e.g., the probability of being a case) inside and outside scanning window Z, respectively. Given window Z, the test statistic is:

where cz and nz are the numbers of cases and observations (cases and noncases) within z, respectively, and C and N are the total numbers of cases and observations in the whole study region, respectively.
$$\Delta = $$ Multinomial version of the scan test

The multinomial version of the spatial scan statistic is useful to investigate clustering when a discrete spatial variable can take one and only one of k possible outcomes that lack intrinsic order information. If the region defined by the moving window is denoted by Z, the null hypothesis for the statistic can be stated as follows:
$$H_0: p_1 = q_1;p_2 = q_2;...;p_k = q_k$$ where \(p_j\) is the probability of being of event type j inside the window Z, and \(q_j\) is the probability of being of event type j outside the window. The alternative hypothesis is that for at least one type event the probability of being of that type is different inside and outside of the window.

The statistic is built as a likelihood ratio, and takes the following form after transformation using the natural logarithm: $$\Delta = \max_Z \{\sum_j \{ S_j^Z log({ S_j^Z \over S^Z }) + (S_j-S_j^Z) log({ {S_j-S_j^Z} \over {S-S^Z} })\}\}-\sum_j S_j log({ S_j \over S }) $$ where S is the total number of events in the study area and \(S_j\) is the total number of events of type j. The superscript Z denotes the same but for the sub-region defined by the moving window.
The theoretical distribution of the statistic under the null hypothesis is not known, and therefore significance is evaluated numerically by simulating neutral landscapes (obtained using a random spatial process) and contrasting the empirically calculated statistic against the frequency of values obtained from the neutral landscapes. The results of the likelihood ratio serve to identify the most likely cluster, which is followed by secondary clusters by the expedient of sorting them according to the magnitude of the test. As usual, significance is assigned by the analyst, and the cutoff value for significance reflects the confidence of the analyst, or tolerance for error.
When implementing the statistic, the analyst must decide the shape of the window and the maximum number of cases that any given window can cover. Currently, analysis can be done using circular or elliptical windows.
Elliptical windows are more time consuming to evaluate but provide greater flexibility to contrast the distribution of events inside and outside the window, and are our selected shape in the analyses to follow. Furthermore, it is recommended that the maximum number of cases entering any given window does not exceed 50% of all available cases.

Control arguments

seedinitNumerical value for the seed (only for boot version). Default value seedinit=123

References

  • Kulldorff M, Nagarwalla N. (1995). Spatial disease clusters: Detection and Inference. Statistics in Medicine. 14:799-810

  • Jung I, Kulldorff M, Richard OJ (2010). A spatial scan statistic for multinomial data. Statistics in Medicine. 29(18), 1910-1918

  • Páez, A., López-Hernández, F.A., Ortega-García, J.A., Ruiz, M. (2016). Clustering and co-occurrence of cancer types: A comparison of techniques with an application to pediatric cancer in Murcia, Spain. Spatial Analysis in Health Geography, 69-90.

  • Tango T., Takahashi K. (2005). A flexibly shaped spatial scan statistic for detecting clusters, International Journal of Health Geographics 4:11.

Author

Fernando Lópezfernando.lopez@upct.es
Román Mínguezroman.minguez@uclm.es
Antonio Páezpaezha@gmail.com
Manuel Ruizmanuel.ruiz@upct.es

Examples


# Case 1: Scan test bernoulli
data(provinces_spain)
sf::sf_use_s2(FALSE)
provinces_spain$Male2Female <- factor(provinces_spain$Male2Female > 100)
levels(provinces_spain$Male2Female) = c("men","woman")
formula <- ~ Male2Female
scan <- scan.test(formula = formula, data = provinces_spain, case="men",
nsim = 99, distr = "bernoulli")
print(scan)
#> 
#> 	Scan test. Distribution: bernoulli
#> 
#> data:  Male2Female
#> scan-loglik = 6.0359, p-value = 0.07
#> alternative hypothesis: High
#> sample estimates:
#>                                        
#> Total observations in the MLC =   17.00
#> Expected cases in the MLC =       11.84
#> Observed cases in the MLC =       16.00
#> 
summary(scan)
#> 
#> Summary of data:
#> Distribution....................: bernoulli
#> Type of cluster (alternative)...: High
#> Number of locations.............: 50
#> Cathegory case..................: men
#> Total number of observations....: 37
#> Names of cathegories............: men woman
#> Total per category..............: 37 13
#> Percent per category............: 0.74 0.26
#> ---------------------------------
#> 
#> Scan statistic: Most Likely Cluster
#> Total observations in the MLC........: 17
#> Names of cathegories.................: men woman
#> Percent per category total...........: 0.74 0.26
#> Percent per category inside cluster..: 0.94 0.06
#> Value of statisitic (loglik ratio)...: 6.0359
#> p-value..............................: 0.07
#> 
#> IDs of cluster detect:
#> Location IDs included...:  37 34 20 11 40 28 6 14 10 18 35 36 22 31 13 15 4
#> ---------------------------------
#> 
#> 
#> Secondary Cluster. Number 1 
#> Total observations in secondary cluster.:  23 
#> Names of cathegories.................: men woman
#> Percent per category total...........: 0.74 0.26
#> Percent per category inside cluster..: 0.61 0.39
#> Value of statisitic (loglik ratio)...: 5.4635
#> p-value..............................: 0.15
#> Location IDs included................:  41 25 19 1 9 30 48 39 50 27 46 16 42 33 38 45 43 5 21 12 2 44 13
#> 
#> 
#> Secondary Cluster. Number 2 
#> Total observations in secondary cluster.:  2 
#> Names of cathegories.................: men woman
#> Percent per category total...........: 0.74 0.26
#> Percent per category inside cluster..: 1 0
#> Value of statisitic (loglik ratio)...: 0.3047
#> p-value..............................: 0.99
#> Location IDs included................:  3 44
#> 
#> 
#> Secondary Cluster. Number 3 
#> Total observations in secondary cluster.:  2 
#> Names of cathegories.................: men woman
#> Percent per category total...........: 0.74 0.26
#> Percent per category inside cluster..: 0.5 0.5
#> Value of statisitic (loglik ratio)...: 0.3047
#> p-value..............................: 0.99
#> Location IDs included................:  7 8
#> 
#> 
#> Secondary Cluster. Number 4 
#> Total observations in secondary cluster.:  2 
#> Names of cathegories.................: men woman
#> Percent per category total...........: 0.74 0.26
#> Percent per category inside cluster..: 1 0
#> Value of statisitic (loglik ratio)...: 0.3047
#> p-value..............................: 0.99
#> Location IDs included................:  17 8
#> 
#> 
#> Secondary Cluster. Number 5 
#> Total observations in secondary cluster.:  2 
#> Names of cathegories.................: men woman
#> Percent per category total...........: 0.74 0.26
#> Percent per category inside cluster..: 0.5 0.5
#> Value of statisitic (loglik ratio)...: 0.3047
#> p-value..............................: 0.99
#> Location IDs included................:  24 49
plot(scan, sf = provinces_spain)

# \donttest{
## With maximum number of neighborhood
scan <- scan.test(formula = formula, data = provinces_spain, case = "woman",
nsim = 99, distr = "bernoulli")
print(scan)
#> 
#> 	Scan test. Distribution: bernoulli
#> 
#> data:  Male2Female
#> scan-loglik = 5.6358, p-value = 0.22
#> alternative hypothesis: High
#> sample estimates:
#>                                        
#> Total observations in the MLC =   11.00
#> Expected cases in the MLC =        7.02
#> Observed cases in the MLC =        7.00
#> 
plot(scan, sf = provinces_spain)



## With elliptic windows
scan <- scan.test(formula = formula, data = provinces_spain, case = "men", nv = 25,
nsim = 99, distr = "bernoulli", windows ="elliptic")
print(scan)
#> 
#> 	Scan test. Distribution: bernoulli
#> 
#> data:  Male2Female
#> scan-loglik = 7.038, p-value < 2.2e-16
#> alternative hypothesis: High
#> sample estimates:
#>                                        
#> Total observations in the MLC =   19.00
#> Expected cases in the MLC =       62.16
#> Observed cases in the MLC =       18.00
#> 
scan <- scan.test(formula = formula, data = provinces_spain, case = "men", nv = 15,
nsim = 99, distr = "bernoulli", windows ="elliptic", alternative = "Low")
print(scan)
#> 
#> 	Scan test. Distribution: bernoulli
#> 
#> data:  Male2Female
#> scan-loglik = 5.9143, p-value = 0.1
#> alternative hypothesis: Low
#> sample estimates:
#>                                        
#> Total observations in the MLC =    5.00
#> Expected cases in the MLC =       51.06
#> Observed cases in the MLC =        1.00
#> 
plot(scan, sf = provinces_spain)


# Case 2: scan test multinomial
data(provinces_spain)
provinces_spain$Older <- cut(provinces_spain$Older, breaks = c(-Inf,19,22.5,Inf))
levels(provinces_spain$Older) = c("low","middle","high")
formula <- ~ Older
scan <- scan.test(formula = formula, data = provinces_spain, nsim = 99, distr = "multinomial")
print(scan)
#> 
#> 	Scan test. Distribution: multinomial
#> 
#> data:  Older
#> scan-loglik = 4.2473, p-value = 0.9
#> sample estimates:
#>               low middle high Sum
#> cases.expect 2.24   2.88 2.88   8
#> cases.observ 0.00   6.00 2.00   8
#> 
plot(scan, sf = provinces_spain)


# Case 3: scan test multinomial
data(FastFood.sf)
sf::sf_use_s2(FALSE)
formula <- ~ Type
scan <- scan.test(formula = formula, data = FastFood.sf, nsim = 99,
distr = "multinomial", windows="elliptic", nv = 50)
print(scan)
#> 
#> 	Scan test. Distribution: multinomial
#> 
#> data:  Type
#> scan-loglik = 15.506, p-value < 2.2e-16
#> sample estimates:
#>                  H     P     S Sum
#> cases.expect 13.48 14.86 14.66  43
#> cases.observ 16.00  1.00 26.00  43
#> 
summary(scan)
#> 
#> Summary of data:
#> Distribution....................: multinomial
#> Number of locations.............: 877
#> Total number of cases...........: 877
#> Names of cathegories...........: H P S
#> Total cases per category........: 275 303 299
#> Percent cases per category......: 0.31 0.35 0.34
#> 
#> Scan statistic:
#> Total cases in the MLC.........: 43
#> Names of cathegories...........: H P S
#> Observed cases in the MLC......: 13.48 14.86 14.66
#> Expected cases in the MLC......: 16 1 26
#> Value of statistic (loglik ratio)....: 15.5058
#> p-value........................: 0
#> 
#> IDs of cluster detect:
#> Location IDs included.....:  68 849 152 499 630 763 827 765 617 600 607 48 58 588 743 843 74 122 750 115 645 61 226 796 876 699 610 597 596 721 751 53 186 659 778 63 106 229 585 738 612 131 208
#> 
#> 
#> Secondary Scan statistic. Number 1 
#> Total cases in secondary cluster......:  16 
#> Names of cathegories.................: H P S
#> Percent per category total...........: 0.31 0.35 0.34
#> Percent per category inside cluster..: 0.25 0.5 0.25
#> Value of statisitic (loglik ratio)....: 11.5285
#> p-value.........................: 0.1
#> Location IDs included..................:  677 311 781 128 108 436 551 576 21 374 319 717 561 6 629 547
#> 
#> 
#> Secondary Scan statistic. Number 2 
#> Total cases in secondary cluster......:  7 
#> Names of cathegories.................: H P S
#> Percent per category total...........: 0.31 0.35 0.34
#> Percent per category inside cluster..: 0.14 0.29 0.57
#> Value of statisitic (loglik ratio)....: 7.0038
#> p-value.........................: 0.96
#> Location IDs included..................:  158 709 335 801 749 545 856
#> 
#> 
#> Secondary Scan statistic. Number 3 
#> Total cases in secondary cluster......:  17 
#> Names of cathegories.................: H P S
#> Percent per category total...........: 0.31 0.35 0.34
#> Percent per category inside cluster..: 0.35 0.35 0.29
#> Value of statisitic (loglik ratio)....: 6.8747
#> p-value.........................: 0.98
#> Location IDs included..................:  521 782 162 643 297 220 267 265 104 530 312 523 783 157 531 848 680
#> 
#> 
#> Secondary Scan statistic. Number 4 
#> Total cases in secondary cluster......:  17 
#> Names of cathegories.................: H P S
#> Percent per category total...........: 0.31 0.35 0.34
#> Percent per category inside cluster..: 0.47 0.29 0.24
#> Value of statisitic (loglik ratio)....: 6.7961
#> p-value.........................: 0.98
#> Location IDs included..................:  190 837 555 711 646 216 17 390 742 563 307 4 353 197 254 192 66
#> 
#> 
#> Secondary Scan statistic. Number 5 
#> Total cases in secondary cluster......:  10 
#> Names of cathegories.................: H P S
#> Percent per category total...........: 0.31 0.35 0.34
#> Percent per category inside cluster..: 0.4 0.2 0.4
#> Value of statisitic (loglik ratio)....: 6.5951
#> p-value.........................: 0.98
#> Location IDs included..................:  78 365 668 228 170 857 306 708 651 187
plot(scan, sf = FastFood.sf)
#> Warning: bounding box has potentially an invalid value range for longlat data


# Case 4: DGP two categories
N <- 150
cx <- runif(N)
cy <- runif(N)
listw <- spdep::knearneigh(cbind(cx,cy), k = 10)
p <- c(1/2,1/2)
rho <- 0.5
fx <- dgp.spq(p = p, listw = listw, rho = rho)
scan <- scan.test(fx = fx, nsim = 99, case = "A", nv = 50, coor = cbind(cx,cy),
distr = "bernoulli",windows="circular")
print(scan)
#> 
#> 	Scan test. Distribution: bernoulli
#> 
#> data:  fx
#> scan-loglik = 16.273, p-value < 2.2e-16
#> alternative hypothesis: High
#> sample estimates:
#>                                       
#> Total observations in the MLC =   22.0
#> Expected cases in the MLC =       10.5
#> Observed cases in the MLC =       21.0
#> 
plot(scan)


# Case 5: DGP three categories
N <- 200
cx <- runif(N)
cy <- runif(N)
listw <- spdep::knearneigh(cbind(cx,cy), k = 10)
p <- c(1/3,1/3,1/3)
rho <- 0.5
fx <- dgp.spq(p = p, listw = listw, rho = rho)
scan <- scan.test(fx = fx, nsim = 19, coor = cbind(cx,cy), nv = 30,
distr = "multinomial", windows = "elliptic")
print(scan)
#> 
#> 	Scan test. Distribution: multinomial
#> 
#> data:  fx
#> scan-loglik = 9.0847, p-value = 0.2
#> sample estimates:
#>                 A    B    C Sum
#> cases.expect 2.68 2.64 2.68   8
#> cases.observ 0.00 0.00 8.00   8
#> 
plot(scan)


# Case 6: Flexible windows
data(provinces_spain)
sf::sf_use_s2(FALSE)
provinces_spain$Male2Female <- factor(provinces_spain$Male2Female > 100)
#> Warning: ‘>’ not meaningful for factors
levels(provinces_spain$Male2Female) = c("men","woman")
formula <- ~ Male2Female
listw <- spdep::poly2nb(provinces_spain, queen = FALSE)
#> although coordinates are longitude/latitude, st_intersects assumes that they are planar
scan <- scan.test(formula = formula, data = provinces_spain, case="men", listw = listw, nv = 6,
                  nsim = 99, distr = "bernoulli", windows = "flexible")
#> Error in scan.test(formula = formula, data = provinces_spain, case = "men",     listw = listw, nv = 6, nsim = 99, distr = "bernoulli", windows = "flexible"): The factor mut be have 2 levels for bernoulli
print(scan)
#> 
#> 	Scan test. Distribution: multinomial
#> 
#> data:  fx
#> scan-loglik = 9.0847, p-value = 0.2
#> sample estimates:
#>                 A    B    C Sum
#> cases.expect 2.68 2.64 2.68   8
#> cases.observ 0.00 0.00 8.00   8
#> 
summary(scan)
#> 
#> Summary of data:
#> Distribution....................: multinomial
#> Number of locations.............: 200
#> Total number of cases...........: 200
#> Names of cathegories...........: A B C
#> Total cases per category........: 67 66 67
#> Percent cases per category......: 0.34 0.33 0.34
#> 
#> Scan statistic:
#> Total cases in the MLC.........: 8
#> Names of cathegories...........: A B C
#> Observed cases in the MLC......: 2.68 2.64 2.68
#> Expected cases in the MLC......: 0 0 8
#> Value of statistic (loglik ratio)....: 9.0847
#> p-value........................: 0.2
#> 
#> IDs of cluster detect:
#> Location IDs included.....:  130 154 149 112 137 84 23 194
#> 
#> 
#> Secondary Scan statistic. Number 1 
#> Total cases in secondary cluster......:  8 
#> Names of cathegories.................: A B C
#> Percent per category total...........: 0.34 0.33 0.34
#> Percent per category inside cluster..: 0.25 0.25 0.5
#> Value of statisitic (loglik ratio)....: 7.9105
#> p-value.........................: 0.4
#> Location IDs included..................:  117 86 178 191 55 181 174 182
#> 
#> 
#> Secondary Scan statistic. Number 2 
#> Total cases in secondary cluster......:  13 
#> Names of cathegories.................: A B C
#> Percent per category total...........: 0.34 0.33 0.34
#> Percent per category inside cluster..: 0.23 0.46 0.31
#> Value of statisitic (loglik ratio)....: 5.8658
#> p-value.........................: 0.8
#> Location IDs included..................:  48 125 148 93 98 42 138 80 38 61 188 128 87
#> 
#> 
#> Secondary Scan statistic. Number 3 
#> Total cases in secondary cluster......:  5 
#> Names of cathegories.................: A B C
#> Percent per category total...........: 0.34 0.33 0.34
#> Percent per category inside cluster..: 0.4 0.4 0.2
#> Value of statisitic (loglik ratio)....: 4.4561
#> p-value.........................: 0.95
#> Location IDs included..................:  58 155 115 88 50
#> 
#> 
#> Secondary Scan statistic. Number 4 
#> Total cases in secondary cluster......:  5 
#> Names of cathegories.................: A B C
#> Percent per category total...........: 0.34 0.33 0.34
#> Percent per category inside cluster..: 0.4 0.2 0.4
#> Value of statisitic (loglik ratio)....: 4.4561
#> p-value.........................: 0.95
#> Location IDs included..................:  169 53 111 49 83
#> 
#> 
#> Secondary Scan statistic. Number 5 
#> Total cases in secondary cluster......:  11 
#> Names of cathegories.................: A B C
#> Percent per category total...........: 0.34 0.33 0.34
#> Percent per category inside cluster..: 0.64 0.18 0.18
#> Value of statisitic (loglik ratio)....: 4.4453
#> p-value.........................: 0.95
#> Location IDs included..................:  1 34 19 51 198 106 57 56 129 141 65
plot(scan, sf = provinces_spain)

# }