---
title: "`ssp.glm`: Optimal Subsampling for Generalized Linear Models"
output: rmarkdown::html_vignette
bibliography: references.bib
vignette: >
  %\VignetteIndexEntry{`ssp.glm`: Optimal Subsampling for Generalized Linear Models}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

This vignette introduces `ssp.glm()`, the main function for optimal subsampling
with generalized linear models (GLMs). We use logistic regression as the main
example because it is the most common use case and because all three key
choices in `ssp.glm()`--`criterion`, `sampling.method`, and `likelihood`--are
available for logistic models.

```{r setup}
library(subsampling)
```

## Model and Objective

Let $(y_i, x_i)$, $i = 1, \ldots, N$, denote the full data, where $x_i$ includes
the intercept. For a GLM with canonical parameter $\theta_i = x_i^\top\beta$,
the full-data objective can be written as

$$
\ell_N(\beta)
= \frac{1}{N}\sum_{i=1}^N
\left\{y_i x_i^\top\beta - \psi(x_i^\top\beta)\right\}.
$$

For logistic regression,

$$
p_i(\beta) = P(Y_i = 1 \mid x_i)
= \psi'(x_i^\top\beta)
= \frac{\exp(x_i^\top\beta)}{1 + \exp(x_i^\top\beta)},
$$

where $\psi(t) = \log\{1 + \exp(t)\}$. The objective becomes

$$
\ell_N(\beta)
= \frac{1}{N}\sum_{i=1}^N
\left[
y_i x_i^\top\beta -
\log\{1 + \exp(x_i^\top\beta)\}
\right].
$$

Fitting this objective on all $N$ observations may be expensive when $N$ is
large. Subsampling replaces the full-data fit with a smaller, informative
subsample. The central question is how to assign a sampling probability to each
observation so that the resulting subsample estimator is statistically efficient.

## Two Ways `ssp.glm()` Works

Most choices of `criterion` use a two-stage procedure.

1. Draw a pilot subsample of size `n.plt`.
2. Fit a pilot estimator $\widehat{\beta}$.
3. Use $\widehat{\beta}$ to compute second-stage subsampling probabilities.
4. Draw the second-stage subsample with expected size `n.ssp`.
5. Fit the GLM on the second-stage subsample and form an information-weighted
   average of the pilot and second-stage estimators.

This two-stage path is used when

``` r
criterion %in% c("optA", "optL", "LCC")
```

The exception is `criterion = "uniform"`. In that case, no pilot estimator is
needed. Uniform means that all $N$ observations have the same sampling
probability. The function draws one uniform subsample of size `n.plt + n.ssp`,
and the returned `subsample.size.expect` is therefore `n.plt + n.ssp` rather
than `n.ssp`.

## Pilot Estimator

The pilot sample provides an initial estimate of the unknown parameter. Let
$S_{\mathrm{plt}}$ denote the pilot sample and let
$\pi_i^{\mathrm{plt}}$ be the pilot sampling probability for observation $i$.
The pilot estimator is obtained from the weighted pilot objective

$$
\widehat{\beta}
= \arg\max_\beta
\sum_{i \in S_{\mathrm{plt}}}
\frac{1}{\pi_i^{\mathrm{plt}}}
\left\{
y_i x_i^\top\beta - \psi(x_i^\top\beta)
\right\}.
$$

The fitted mean from this pilot estimator is

$$
\widehat{p}_i = p_i(\widehat{\beta}),\qquad i = 1,\ldots,N.
$$

This fitted mean appears in the raw importance scores used below.

For binomial and quasibinomial families, `ssp.glm()` draws the pilot sample by
case-control sampling with replacement:

$$
\pi_i^{\mathrm{plt}}
=
\begin{cases}
\dfrac{1}{2N_1}, & y_i = 1,\\[6pt]
\dfrac{1}{2N_0}, & y_i = 0,
\end{cases}
$$

where $N_1 = \sum_i y_i$ and $N_0 = N - N_1$. The pilot model is then fit by
weighted likelihood. For other GLM families, the pilot sample is drawn uniformly
with replacement.

## Choosing `criterion`

The `criterion` argument controls the raw importance score $m_i$ assigned to
each observation. These scores are not probabilities yet. They measure the
relative importance of observations, and then `ssp.glm()` turns them into valid
sampling probabilities by normalization for with-replacement sampling, or by
truncation and scaling for Poisson subsampling. For logistic regression, the
common factor is the pilot residual magnitude $|y_i - \widehat{p}_i|$.

### `criterion = "optA"`

The A-optimal criterion uses

$$
m_i
= |y_i - \widehat{p}_i|
\left\lVert
\widehat{M}^{-1} x_i
\right\rVert,
$$

where $\widehat{M}$ is the pilot estimate of the information matrix. This criterion
aims to minimize the asymptotic variance of the subsample estimator
[@wang2018optimal; @wang2019more]. It targets smaller asymptotic variance but is
computationally heavier because it uses $\widehat{M}^{-1}$.

### `criterion = "optL"`

The L-optimal criterion uses

$$
m_i = |y_i - \widehat{p}_i| \, \lVert x_i \rVert.
$$

This is the default. It keeps the main idea of assigning larger scores to
observations with large pilot residuals and large covariate magnitude, but it is
computationally cheaper than `optA` because it removes the matrix
$\widehat{M}^{-1}$ before $x_i$ inside the norm.

### `criterion = "LCC"`

Local case-control sampling uses

$$
m_i = |y_i - \widehat{p}_i|.
$$

It favors observations whose responses are surprising under the pilot model.
This is a simple baseline related to local case-control sampling
[@fithian2014local].

### `criterion = "uniform"`

Uniform sampling assigns equal probability to all observations and does not use
the pilot stage:

$$
m_i = 1,\qquad i = 1,\ldots,N.
$$

This is useful as a baseline because it separates the effect of subsample size
from the effect of optimal probability construction.

## Choosing `sampling.method`

After $m_i$ is computed, `sampling.method` controls how the actual subsample is
drawn.

### `sampling.method = "withReplacement"`

With replacement sampling draws exactly `n.ssp` observations from the full data,
allowing repeated indices. The sampling probability is

$$
p_i
= (1-\alpha)\frac{m_i}{\sum_{j=1}^N m_j}
+ \alpha\frac{1}{N},
$$

where `alpha` is a small mixing weight controlled by `control = list(alpha =
...)`. The default is `alpha = 0.01`. This mixture prevents extremely small
sampling probabilities.

### `sampling.method = "poisson"`

Poisson subsampling includes each observation independently. The realized
subsample size is random, but its expectation is controlled by `n.ssp`.

The implementation first truncates large scores,

$$
m_i^* = \min(m_i, H),
$$

where $H$ is estimated from the pilot sample and depends on the control
parameter `b`. Then the inclusion probability is

$$
\pi_i
= \min\left[
n_{\mathrm{ssp}}
\left\{
(1-\alpha)\frac{m_i^*}{\widehat\Phi}
+ \alpha\frac{1}{N}
\right\},
1
\right],
$$

where $\widehat\Phi$ estimates the full-data total of the truncated score. The
parameter `b` controls the truncation level; smaller values push the sampling
probabilities closer to uniform.

## Choosing `likelihood`

The `likelihood` argument controls how the model fit corrects for unequal
subsampling probabilities.

### `likelihood = "weighted"`

The weighted likelihood is the general-purpose option. For a subsample $S$, let
$\pi_i$ denote the relevant sampling probability: the drawing probability for
with-replacement sampling or the inclusion probability for Poisson subsampling.
The weighted objective is

$$
\ell_S^{\mathrm{w}}(\beta)
= \sum_{i \in S}
\frac{1}{\pi_i}
\left[
y_i x_i^\top\beta - \psi(x_i^\top\beta)
\right].
$$

This likelihood works for all supported GLM families and both sampling methods.

### `likelihood = "logOddsCorrection"`

The log odds correction likelihood is a specialized option for logistic
regression with Poisson subsampling [@wang2022maximum]. Instead of fitting a
weighted likelihood, it fits a sampled conditional likelihood with an offset.
For an observation in the Poisson subsample,

$$
P(Y_i = 1 \mid x_i, i \in S)
=
\frac{
\exp(x_i^\top\beta + a_i)
}{
1 + \exp(x_i^\top\beta + a_i)
},
$$

where

$$
a_i = \log\left(\frac{\pi_i^{(1)}}{\pi_i^{(0)}}\right).
$$

Here $\pi_i^{(1)}$ and $\pi_i^{(0)}$ are the inclusion probabilities that would
be assigned to observation $i$ if its response were 1 or 0, respectively, with
the same covariates and pilot fit. In `ssp.glm()`, this option is implemented
only for binomial or quasibinomial families with
`sampling.method = "poisson"`.

## Method Summary

| Component | Setting |
|---|---|
| `criterion` | `optA` |
| `criterion` | `optL` |
| `criterion` | `LCC` |
| `criterion` | `uniform` |
| `sampling.method` | `withReplacement` |
| `sampling.method` | `poisson` |
| `likelihood` | `weighted` |
| `likelihood` | `logOddsCorrection` |

The main restrictions are:

- `logOddsCorrection` is implemented only for binomial and quasibinomial
  families.
- `logOddsCorrection` requires `sampling.method = "poisson"`.
- `criterion = "uniform"` uses a one-stage uniform sample of size
  `n.plt + n.ssp`.

## Example Data

We simulate a logistic regression problem with $N = 10^4$ observations and six
covariates.

```{r}
set.seed(1)
N <- 1e4
beta0 <- rep(-0.5, 7)
d <- length(beta0) - 1
corr <- 0.5
sigmax <- matrix(corr, d, d) + diag(1 - corr, d)
X <- MASS::mvrnorm(N, rep(0, d), sigmax)
colnames(X) <- paste0("V", 1:d)
P <- 1 - 1 / (1 + exp(beta0[1] + X %*% beta0[-1]))
Y <- rbinom(N, 1, P)
data <- data.frame(Y = Y, X)
formula <- Y ~ .
head(data)
```

## Example 1: Default Optimal Subsampling

The default choice is `criterion = "optL"` with Poisson subsampling and weighted
likelihood. This is a good starting point for large logistic regression
problems.

```{r}
set.seed(2)
n.plt <- 200
n.ssp <- 600

fit_optL <- ssp.glm(
  formula = formula,
  data = data,
  n.plt = n.plt,
  n.ssp = n.ssp,
  family = "quasibinomial",
  criterion = "optL",
  sampling.method = "poisson",
  likelihood = "weighted"
)

summary(fit_optL)
```

## Example 2: Logistic Poisson Correction

For logistic regression with Poisson subsampling, `logOddsCorrection` uses the
sampled conditional likelihood described above.

```{r}
set.seed(18)

fit_logodds <- ssp.glm(
  formula = formula,
  data = data,
  n.plt = n.plt,
  n.ssp = n.ssp,
  family = "quasibinomial",
  criterion = "optA",
  sampling.method = "poisson",
  likelihood = "logOddsCorrection"
)

summary(fit_logodds)
```

## Example 3: Uniform Baseline

Uniform sampling is useful as a baseline. Notice that the expected subsample
size is `n.plt + n.ssp`, because the pilot and second-stage sizes are combined
into one uniform draw.

```{r}
set.seed(4)

fit_uniform <- ssp.glm(
  formula = formula,
  data = data,
  n.plt = n.plt,
  n.ssp = n.ssp,
  family = "quasibinomial",
  criterion = "uniform",
  sampling.method = "withReplacement",
  likelihood = "weighted"
)

fit_uniform$subsample.size.expect
length(fit_uniform$index)
summary(fit_uniform)
```

## Returned Object

The returned object stores the model call, coefficient estimates, covariance
estimates, and row indices of the drawn samples.

```{r}
names(fit_optL)
```

Important components include:

- `coef.plt`: the pilot estimator.
- `coef.ssp`: the estimator from the second-stage subsample.
- `coef`: the combined estimator printed by `summary()`.
- `cov.ssp`: covariance estimate for `coef.ssp`.
- `cov`: covariance estimate for `coef`.
- `index.plt`: row indices for the pilot subsample, when a pilot sample is used.
- `index`: row indices for the final drawn subsample.
- `subsample.size.expect`: the expected subsample size reported by the method.

The coefficients and standard errors printed by `summary()` are based on `coef`
and `sqrt(diag(cov))`.

## Other Families

`ssp.glm()` also supports Poisson, quasipoisson, Gaussian, and Gamma GLMs with
the weighted likelihood. The `logOddsCorrection` likelihood is specific to
logistic regression and should be used only with binomial or quasibinomial
families and Poisson subsampling.

For binomial and Poisson models, `survey::svyglm()` recommends the quasi
families, such as `quasibinomial()` and `quasipoisson()`, to avoid warnings
about non-integer successes after weighting. The quasi families give the same
point estimates for these models.

## References
