Understanding Model Types

library(bayesiansurpriser)
library(sf)

Overview

The bayesiansurpriser package provides five model types that represent different hypotheses about how data might be distributed. Bayesian Surprise measures how much our beliefs about which model is correct change after observing data.

The Five Model Types

1. Uniform Model (bs_model_uniform)

The uniform model assumes all observations are equally likely regardless of any contextual factors.

model_uniform <- bs_model_uniform()
print(model_uniform)
#> <bs_model: uniform >
#>   Name: Uniform 
#>   Parameters:
#>     n_regions :

Use when: You want to detect any deviation from uniform distribution.

Likelihood: Each observation has probability 1/n where n is the number of observations.

2. Base Rate Model (bs_model_baserate)

The base rate model assumes observations follow a known baseline distribution (e.g., population proportions).

nc <- st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE)

# Create base rate model from birth counts
model_baserate <- bs_model_baserate(nc$BIR74)
print(model_baserate)
#> <bs_model: baserate >
#>   Name: Base Rate 
#>   Parameters:
#>     expected : [ 100 values]
#>     expected_prop : [ 100 values]
#>     normalize : TRUE

Use when: You have expected values (population, baseline rates) and want to detect deviations from them.

Likelihood: Probability proportional to expected values (normalized to sum to 1).

3. Gaussian Model (bs_model_gaussian)

The Gaussian model assumes observations follow a normal distribution, either with fixed parameters or fitted from the data.

# Fit from data
model_gaussian_fit <- bs_model_gaussian()

# Fixed parameters
model_gaussian_fixed <- bs_model_gaussian(mu = 100, sigma = 20, fit_from_data = FALSE)
print(model_gaussian_fixed)
#> <bs_model: gaussian >
#>   Name: Gaussian 
#>   Parameters:
#>     mu : 100 
#>     sigma : 20 
#>     fit_from_data : FALSE

Use when: You expect data to follow a normal distribution and want to detect outliers.

Likelihood: Based on normal PDF with given (or estimated) mean and standard deviation.

4. Sampled/KDE Model (bs_model_sampled)

The sampled model uses kernel density estimation (KDE) to create an empirical distribution from the data itself.

# Sample 20% of data for KDE
model_sampled <- bs_model_sampled(sample_frac = 0.2, kernel = "gaussian")
print(model_sampled)
#> <bs_model: sampled >
#>   Name: Sampled (KDE) 
#>   Parameters:
#>     sample_frac : 0.2 
#>     kernel : gaussian 
#>     bandwidth : nrd0 
#>     sample_indices :

Use when: You want a data-driven baseline without parametric assumptions.

Likelihood: Evaluated from kernel density estimate of sampled data.

5. de Moivre Funnel Model (bs_model_funnel)

The funnel model accounts for sampling variance, recognizing that smaller samples have higher natural variability.

# Create funnel model with sample sizes
model_funnel <- bs_model_funnel(nc$BIR74, type = "count")
print(model_funnel)
#> <bs_model: funnel >
#>   Name: de Moivre Funnel (paper) 
#>   Parameters:
#>     sample_size : [ 100 values]
#>     target_rate :  
#>     type : count 
#>     formula : paper 
#>     control_limits : 2, 3

Use when: Observations come from samples of different sizes and you want to account for sampling error.

Likelihood: Based on z-scores computed from expected standard error given sample size.

Combining Models: Model Space

Models are combined into a “model space” with prior probabilities:

# Default model space (uniform + baserate + funnel)
space_default <- default_model_space(nc$BIR74)
print(space_default)
#> <bs_model_space>
#>   Models: 3 
#>    1. Uniform (prior: 0.3333)
#>    2. Base Rate (prior: 0.3333)
#>    3. de Moivre Funnel (paper) (prior: 0.3333)
# Custom model space with explicit prior
space_custom <- model_space(
  bs_model_uniform(),
  bs_model_baserate(nc$BIR74),
  bs_model_gaussian(),
  bs_model_funnel(nc$BIR74),
  prior = c(0.1, 0.4, 0.2, 0.3),
  names = c("Uniform", "Population", "Normal", "Funnel")
)
print(space_custom)
#> <bs_model_space>
#>   Models: 4 
#>    1. Uniform (prior: 0.1)
#>    2. Population (prior: 0.4)
#>    3. Normal (prior: 0.2)
#>    4. Funnel (prior: 0.3)

How Models Affect Surprise

Different model combinations highlight different aspects of the data:

# Only uniform model - highlights any non-uniform distribution
result_uniform <- surprise(nc, observed = SID74,
                          models = model_space(bs_model_uniform()))

# Uniform + baserate - highlights deviation from population proportion
result_pop <- surprise(nc, observed = SID74, expected = BIR74,
                      models = model_space(
                        bs_model_uniform(),
                        bs_model_baserate(nc$BIR74)
                      ))

# Full default - accounts for sampling variance too
result_full <- surprise(nc, observed = SID74, expected = BIR74)

# Compare max surprise values
cat("Uniform only, max surprise:", max(result_uniform$surprise), "\n")
#> Uniform only, max surprise: 0
cat("With base rate, max surprise:", max(result_pop$surprise), "\n")
#> With base rate, max surprise: 0.9999773
cat("Full model space, max surprise:", max(result_full$surprise), "\n")
#> Full model space, max surprise: 0.6008295

Global Model Updates

Per-region surprise scores use the model-space priors for each observation. If you want a single global posterior over models after observing the full dataset, call bayesian_update() explicitly:

space <- default_model_space(nc$BIR74)
updated_space <- bayesian_update(space, nc$SID74)

cat("Prior:", updated_space$prior, "\n")
#> Prior: 0.3333333 0.3333333 0.3333333
cat("Posterior:", updated_space$posterior, "\n")
#> Posterior: 0.4275831 0.5650043 0.007412583

Guidelines for Model Selection

Scenario Recommended Models
Raw counts, no baseline Uniform, Gaussian
Counts with population data Uniform, Base Rate, Funnel
Rates/proportions Uniform, Base Rate
Exploratory count/exposure analysis Uniform, Base Rate, Funnel
Small samples prevalent Include Funnel
Unknown distribution Include Sampled/KDE