---
title: "Sampling the score"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Sampling the score}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 6, fig.height = 4)
```

`CoxDeviance$sample_score()` draws the score of the Cox model at a linear
predictor, by one of four methods, and `CoxDeviance$wild_score()` gives the
wild draw for multipliers of your choosing. This vignette compares the
methods on data with heavy ties. For the basics of `CoxDeviance`, see
`vignette("coxdev", package = "coxdev")`. The Python package has the same
notebook, `doc/sampling.md`.

```{r}
library(coxdev)
library(survival)
```

## A survival response

Survival times on a coarse grid, so that there are many ties, with delayed
entry and censoring.

```{r}
set.seed(1)
n <- 500
p <- 3
X <- matrix(rnorm(n * p), n, p, dimnames = list(NULL, c("x1", "x2", "x3")))
beta <- c(0.5, -0.5, 0.25)
time <- rexp(n, exp(drop(X %*% beta)))
cens <- rexp(n, 0.5)
stop <- ceiling(pmin(time, cens) * 4) / 4
status <- as.integer(time <= cens)
start <- floor(runif(n, 0, 0.5) * stop * 8) / 8
y <- Surv(start, stop, status)

cox <- CoxDeviance$new(y, tie_breaking = "efron")
cox
```

We sample at the estimate of the coefficients, from `coxph`:

```{r}
b <- coef(coxph(y ~ X, ties = "efron"))
b
```

## The methods

Each draw `r` has one entry per observation, and `crossprod(X, r)` is a
draw of the score of the coefficients. We draw at the estimate `b`, by four
methods:

- `"multinomial"`: the failures at each event time drawn with replacement,
  with probabilities proportional to `exp(eta)`. This is the sampling model
  of Breslow's likelihood, so the covariance is the Breslow information.
- `"fisher"`: the set of failures at each event time drawn from Fisher's
  noncentral hypergeometric distribution. This is the sampling model of the
  exact partial likelihood, whose information is smaller than Breslow's when
  ties are heavy.
- `"bernoulli"`: each subject at risk fails independently, with
  probability `min(1, d p)`, where `d` is the number of failures at the
  event time and `p` the subject's share of the risk. Its mean is zero only
  while `d p < 1`: under heavy ties the cap binds, and the draws are biased.
- `"wild"`: the score residuals times independent normals. Its covariance is
  the sandwich, which does not rely on the model being right.

```{r draws}
eta_hat <- drop(X %*% b)
methods <- c("multinomial", "fisher", "bernoulli", "wild")
draws <- lapply(methods, function(m) {
  crossprod(X, cox$sample_score(eta_hat, method = m, n_draws = 4000, seed = 1))
})
names(draws) <- methods
```

Here are the means of the draws, in standard deviations, and their
variances relative to the Breslow information:

```{r}
breslow <- CoxDeviance$new(y, tie_breaking = "breslow")
info_breslow <- crossprod(X, breslow$information(eta_hat) %*% X)
summary_table <- t(sapply(draws, function(d) {
  c(rowMeans(d) / sqrt(diag(info_breslow)), diag(cov(t(d))) / diag(info_breslow))
}))
colnames(summary_table) <- c(paste("mean", colnames(X)), paste("variance", colnames(X)))
knitr::kable(summary_table, digits = 2, caption = paste(
  "Means of the draws of the score, in units of sqrt(Breslow information),",
  "and variances relative to the Breslow information"))
```

The multinomial draws have the Breslow information as covariance, and the
Fisher draws a smaller one, the information of the exact likelihood. The
Bernoulli draws are biased: at these ties, `d p` often exceeds 1, so the
cap `min(1, d p)` gives too few failures to the subjects most at risk.

The wild draws estimate the sandwich, `crossprod(U)` for the score
residuals `U` of the design, the middle of the robust variance
`solve(I) %*% crossprod(U) %*% solve(I)` of `coxph(robust = TRUE)`:

```{r}
U <- cox$score_residuals(eta_hat, X)
sandwich <- crossprod(U)
fit_robust <- coxph(y ~ X, ties = "efron", id = seq_len(n), robust = TRUE)
I_hat <- solve(fit_robust$naive.var)
rbind(wild = diag(cov(t(draws$wild))), sandwich = diag(sandwich),
      coxph = diag(I_hat %*% fit_robust$var %*% I_hat))
```

The distributions of the first coefficient's score:

```{r, fig.alt = "Densities of 4000 draws of the score of x1, by method, with the normal density with the Breslow information as variance."}
cols <- c(multinomial = "black", fisher = "firebrick", bernoulli = "darkorange", wild = "steelblue")
dens <- lapply(draws, function(d) density(d[1, ]))
plot(NULL, xlim = c(-1, 1) * 4 * sqrt(info_breslow[1, 1]),
     ylim = c(0, 1.35 * max(sapply(dens, function(d) max(d$y)))),
     xlab = "score of x1", ylab = "density")
for (m in methods) lines(dens[[m]], col = cols[m], lwd = 2)
curve(dnorm(x, sd = sqrt(info_breslow[1, 1])), add = TRUE, lty = 2)
legend("topleft", c(methods, "N(0, Breslow info.)"), cex = 0.8,
       col = c(cols, "black"), lty = c(rep(1, 4), 2), lwd = c(rep(2, 4), 1), bty = "n")
```

## Choosing the multipliers

`wild_score()` gives the wild draw for multipliers you choose: the sum over
observations of the multiplier times the score residual, `G %*% U`. With multipliers
all one it is the score itself. Other multipliers give other wild
bootstraps, here Rademacher signs in place of normals:

```{r}
all.equal(cox$wild_score(eta_hat, rep(1, n)), -cox$compute(eta_hat)$gradient / 2)
rademacher <- replicate(4000, {
  drop(crossprod(X, cox$wild_score(eta_hat, sample(c(-1, 1), n, replace = TRUE))))
})
rbind(rademacher = diag(cov(t(rademacher))), sandwich = diag(sandwich))
```

## Reproducibility

The same seed gives the same draws on every platform, and from the Python
package. With `seed = NULL`, the seed comes from R's generator, so
`set.seed()` makes the draws reproducible.

```{r}
identical(cox$sample_score(eta_hat, method = "fisher", seed = 7),
          cox$sample_score(eta_hat, method = "fisher", seed = 7))
```
