Demo: Detection Theory Examples

Switch between two common textbook calculations: a signed mean-shift statistic and an energy statistic for a variance change. The point is to notice that different alternatives call for different sufficient statistics.

Mathematical setup

Mean-shift example:

\[H_0:X_i\sim N(0,1), \qquad H_1:X_i\sim N(\mu_1,1), \qquad T=\bar X.\]

For the threshold rule $T>\gamma$,

\[P_{\mathrm{FA}}=1-\Phi(\gamma\sqrt n), \qquad P_D=1-\Phi((\gamma-\mu_1)\sqrt n).\]

Variance-change example:

\[H_0:X_i\sim N(0,1), \qquad H_1:X_i\sim N(0,\sigma_1^2), \qquad T=\sum_{i=1}^n X_i^2.\]

Under $H_0$, $T\sim\chi_n^2$; under $H_1$, $T/\sigma_1^2\sim\chi_n^2$, or equivalently $T\sim\sigma_1^2\chi_n^2$. For the rule $T>\gamma$,

\[P_{\mathrm{FA}}=\Pr(\chi_n^2>\gamma), \qquad P_D=\Pr\left(\chi_n^2>\frac{\gamma}{\sigma_1^2}\right).\]

The displayed variance-change probabilities use these chi-squared survival functions.

What to try

  • In mean-shift mode, raise $\mu_1$ and see detection improve without changing the false-alarm calculation for a fixed threshold.
  • In variance-change mode, raise $\sigma_1$. The energy distribution under $H_1$ shifts right, so large-energy tests become more powerful.
  • Compare threshold scales across the two modes. A reasonable threshold for $\bar X$ is not a reasonable threshold for $\sum_i X_i^2$.

For the variance-change example, the energy statistic uses the scaled chi-squared distribution: under $H_0$, $\sum_i X_i^2\sim\chi_n^2$, and under the larger-variance alternative, $\sum_i X_i^2/\sigma_1^2\sim\chi_n^2$.

Try it in Python

This cell covers both widget modes: the signed mean-shift statistic and the energy statistic for a variance change.

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

mode = "mean"   # "mean" or "variance"
n = 12
gamma = 1.8
mu1 = 0.8
sigma1 = 1.8

if mode == "mean":
    se = 1 / np.sqrt(n)
    p_fa = stats.norm.sf(gamma, loc=0, scale=se)
    p_d = stats.norm.sf(gamma, loc=mu1, scale=se)

    grid = np.linspace(min(-1, gamma - 1), max(mu1 + 1, gamma + 1), 400)
    plt.plot(grid, stats.norm.pdf(grid, 0, se), label="H0")
    plt.plot(grid, stats.norm.pdf(grid, mu1, se), label="H1")
    plt.axvline(gamma, color="black", linestyle="--", label="threshold")
    plt.xlabel("sample mean")
else:
    p_fa = stats.chi2.sf(gamma, df=n)
    p_d = stats.chi2.sf(gamma / sigma1**2, df=n)

    grid = np.linspace(0.001, max(35, sigma1**2 * n * 3), 500)
    plt.plot(grid, stats.chi2.pdf(grid, df=n), label="H0")
    plt.plot(grid, stats.chi2.pdf(grid / sigma1**2, df=n) / sigma1**2, label="H1")
    plt.axvline(gamma, color="black", linestyle="--", label="threshold")
    plt.xlabel("energy statistic")

plt.ylabel("density")
plt.legend()
plt.show()

print(f"mode = {mode}")
print(f"P_FA = {p_fa:.4f}")
print(f"P_D  = {p_d:.4f}")

Back to topic notes