Generate \(K(\kappa)\)parent points via Poisson Point Process with intensity \(\lambda = \kappa\)
For each parent point \(\mathbf{s}_i \in \left\{\mathbf{s}_1, \ldots, \mathbf{s}_{K(\kappa)}\right\}\):
Generate \(N(\mu)\)offspring points via Poisson Point Process with intensity \(\lambda = \mu\), distributed uniformly within a circle of radius scale centered at \(\mathbf{s}_i\)
Offspring points form the outcome (parent points are thrown away)
Generate events \(\mathbf{S} = \{\mathbf{s}_1, \ldots, \mathbf{s}_{N(\lambda)}\}\) via Poisson point process with \(\lambda = \kappa\)
Generate events \(\mathbf{S} = \{\mathbf{s}_1, \ldots, \mathbf{s}_{N(\lambda)}\}\) via Poisson point process with \(\lambda = \kappa\), plus timestamp\(t_i \sim \mathcal{U}(0,1)\) for each \(\mathbf{s}_i\)
Delete all pairs of points \(\mathbf{s}_i\), \(\mathbf{s}_j\) for which \(\textsf{dist}(\mathbf{s}_i, \mathbf{s}_j) < \texttt{r}\)
For each pair of points \(\mathbf{s}_i\), \(\mathbf{s}_j\): if \(\textsf{dist}(\mathbf{s}_i, \mathbf{s}_j) < \texttt{r}\), delete the later point
Cox Process highlights a crucial simplifying assumption we’ve made up til now: homogeneous point processes
TipHomogeneous Point Process
Points are distributed such that intensity \(\lambda\), expected # of cases per unit area, is a constant across the study area
Allows “eyeballing” clusters: areas w/more points than expected under uniformity
But… in many cases we expect (e.g.) greater density of people in cities!
TipHeterogeneous Point Process
Distribution of points is determined by intensity function\(\lambda(\mathbf{s})\), expected # of cases per unit area around \(\mathbf{s}\)
The Point: Base Rates \(\Rightarrow\) Null Models!
A “baseline” intensity function, \(\lambda(\mathbf{s})\) (possibly \(c\) for all \(\mathbf{s}\)) defines a null model, from which we can simulate a bunch of point patterns…
Then we compare: How does an observed point pattern differ from these simulated point patterns?
Not expecting any clusters? Prior expectation = Uniform distribution \(\Rightarrow\) null model = homogeneous point process Do I see clustering? \(I \overset{\tiny{?}}{\gg} 0\)
Expecting clusters in cities? Prior expectation = Population density\(\Rightarrow\) null model = heterogeneous point process Do I see clustering above and beyond what I expected? \(I_{\text{obs}} \overset{\tiny{?}}{\gg} I_{\text{base}}\)
These Null Models Have Names!
Not expecting any clusters? Null model = Complete Spatial Randomness (CSR)
Expecting clusters (e.g.) in cities? Null model = Constant Risk Hypothesis
Why “constant risk”? From epidemiology:
\[
\underbrace{\rho(\mathbf{s})}_{\text{Risk near }\mathbf{s}}
~~=~~~~~~ \frac{
\overbrace{\lambda(\mathbf{s})}^{\mathclap{\text{Density of cases near }s}}
}{
\underbrace{\lambda_0(\mathbf{s})}_{\mathclap{\text{Population density near }\mathbf{s}}}
}
\]
Constant Risk Hypothesis: Motivation
Here is a (fictional) map of 2026 flu cases in the US “Midwest”
Are people in Chicago (pop. 2,731,585) and Detroit (pop. 649,095) equally “at risk”?
\(\Rightarrow \hat{\rho}(\mathbf{s}) = \frac{\hat{\lambda}(\mathbf{s})}{\alpha\hat{\lambda}_0(\mathbf{s})}\), where \(\alpha = \frac{N}{N_0}\)
Code
(alpha <- cases$n/controls$n)
[1] 0.2519868
Code
library(fields)
Loading required package: spam
Spam version 2.11-4 (2026-05-28) is loaded.
Type 'help( Spam)' or 'demo( spam)' for a short introduction
and overview of this package.
Help for individual functions is also obtained by adding the
suffix '.spam' to the function name, e.g. 'help( chol.spam)'.
Attaching package: 'spam'
The following objects are masked from 'package:base':
backsolve, forwardsolve
Loading required package: viridisLite
Loading required package: RColorBrewer
Try help(fields) to get started.
Attaching package: 'fields'
The following object is masked from 'package:leaflet':
addLegend
How do we know whether ↑ risk around North Newcastle due to “chance”? Maybe relative risk is actually constant, and people in Newcastle are just unlucky…
Thought experiment: Bro hands you a coin. You flip 10 times. HHHHHTHHHH…
Coin may be biased-towards-H, or could be fair (lots of heads by chance!)
Stats: Compare with null model! For fair coin, how likely is [9 Heads / 10 Flips]?
The main datatype used to represent Planar Point Patterns [spatstat book p. 41]
Unlike sf objects, which contain data+geometries for any desired collection of \(N\) entities, ppp objects are required to have at least an observation window!