Implement Bootstrap and Jackknife Estimates on a DataFrame
PinterestData Scientist · OnsiteLast updated
Tests practical statistical computing skills by requiring the implementation of bootstrap confidence intervals and jackknife variance estimation for arbitrary metrics evaluated on pandas DataFrames.
Problem Statement
At Pinterest, many key user engagement metrics (such as the ratio of Pin saves to impressions, or click-through rates) are complex ratio statistics where analytical closed-form standard errors are difficult or impossible to derive. In these scenarios, non-parametric resampling methods like the Bootstrap and the Jackknife are widely used to quantify metric variance and construct confidence intervals.
Implement two statistical functions that operate on a pandas DataFrame:
bootstrap_ci(df, stat_fn, n_boot=1000, ci=0.95, random_state=None): Computes a two-sided empirical percentile confidence interval for the statistic returned bystat_fn(df).jackknife_variance(df, stat_fn): Computes the leave-one-out jackknife variance estimate ofstat_fn(df).
Mathematical Definitions
- Bootstrap Percentile Interval: Draw
n_bootsamples of sizenwith replacement fromdf(wheren = len(df)). Evaluatestat_fnon each sample. For confidence levelci, the lower quantile isalpha / 2and the upper quantile is1 - alpha / 2, wherealpha = 1 - ci. - Jackknife Variance: Given
nobservations, computestat_i = stat_fn(df_{-i})for eachi in 0, ..., n - 1, wheredf_{-i}denotesdfwith rowiomitted. The jackknife variance estimate is:Var_jack = ((n - 1) / n) * sum((stat_i - stat_mean)**2)wherestat_mean = (1 / n) * sum(stat_i).
Function Signatures
import pandas as pd
from typing import Callable, Tuple
def bootstrap_ci(
df: pd.DataFrame,
stat_fn: Callable[[pd.DataFrame], float],
n_boot: int = 1000,
ci: float = 0.95,
random_state: int | None = None
) -> Tuple[float, float]:
...
def jackknife_variance(
df: pd.DataFrame,
stat_fn: Callable[[pd.DataFrame], float]
) -> float:
...
Constraints
dfcontainsnrows where2 <= n <= 10_000.stat_fnaccepts a DataFrame and returns a scalar float.- Ensure sampling in bootstrap is done positionally to prevent row index alignment artifacts.
- Output should be rounded or comparable to within standard numerical precision tolerances (
1e-4).
Example
import pandas as pd
import numpy as np
data = pd.DataFrame({
"saves": [10, 20, 15, 30, 25],
"impressions": [100, 200, 150, 250, 200]
})
def save_rate(d: pd.DataFrame) -> float:
return d["saves"].sum() / d["impressions"].sum()
# Jackknife variance
j_var = jackknife_variance(data, save_rate)
# j_var is approximately 0.000015
# Bootstrap confidence interval
ci_low, ci_high = bootstrap_ci(data, save_rate, n_boot=2000, ci=0.90, random_state=42)
# ci_low, ci_high will bracket the point estimate (~0.111) reasonably
Edge Cases
- Handling
n < 2: raiseValueErrorforjackknife_variance. - Preserving reproducibility when
random_stateis provided. - Non-contiguous or duplicate indices in input
dfmust not break leave-one-out drops or bootstrap resampling.
Hints
Hint 1
Reset or ignore existing DataFrame index labels by using integer positional slicing (e.g., `iloc` or drawing positional indices via `np.random.default_rng`) to avoid unintended index-alignment bugs.
Hint 2
For the jackknife variance, note the normalization factor `(n - 1) / n`, which differs from standard sample variance.
Hint 3
For percentile bootstrap intervals, compute quantiles using `np.percentile` with `100 * (1 - ci) / 2` and `100 * (1 + ci) / 2`.
Follow-up questions
- Why does the classic delete-1 jackknife fail when applied to non-smooth statistics like the sample median, and what modifications can fix it?
- If rows represent user actions and multiple rows belong to the same `user_id`, how would you adapt these methods to perform cluster bootstrap or cluster jackknife?
- When scaling to hundreds of millions of events, standard bootstrap resampling is computationally prohibitive; how does the Poisson bootstrap solve this?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.