Katkat

Implement Jackknife and Random Choice

PinterestData Scientist · Technical Screen

Last updated

Implement weighted random selection from scratch and compute delete-1 jackknife estimates of parameter bias and standard error for an arbitrary statistical estimator.

Problem Description

Resampling and probabilistic sampling are fundamental techniques in empirical data science, used for confidence intervals, variance estimation, and experimentation. In this problem, you will implement two core components:

  1. weighted_sample: A sampling function that draws k items from a list according to relative probability weights, supporting both with-replacement (replace=True) and without-replacement (replace=False) sampling.
  2. jackknife_estimator: A statistical resampling utility that computes the delete-1 jackknife estimates of standard error and bias for an arbitrary statistical function estimator_func applied to a 1D dataset.

Part 1: weighted_sample

Implement the function:

def weighted_sample(items: list, weights: list[float], k: int, replace: bool = True) -> list:
    pass
  • items: A list of unique or non-unique items of length n.
  • weights: A list of non-negative float weights of length n. Weights are unnormalized and sum to W > 0.
  • k: Number of samples to draw (1 <= k). When replace=False, k <= n.
  • replace: If True, items can be selected multiple times. If False, items are drawn without replacement (once an item is selected, its probability is zeroed out and remaining weights are renormalized).

Do not use random.choices, random.sample, or numpy.random.choice.


Part 2: jackknife_estimator

Implement the function:

def jackknife_estimator(data: list[float], estimator_func) -> dict[str, float]:
    pass

Given a dataset of n observations x = [x_1, ..., x_n] and an estimator theta_hat = estimator_func(x):

  • Let theta_i be the estimator evaluated on the dataset leaving out observation x_i (sample size n - 1).
  • Let theta_dot = sum(theta_i) / n be the mean of the leave-one-out estimates.
  • Jackknife estimate of bias: bias_jack = (n - 1) * (theta_dot - theta_hat).
  • Jackknife estimate of standard error: se_jack = sqrt(((n - 1) / n) * sum((theta_i - theta_dot) ** 2 for i in 1..n)).

Return a dictionary with keys 'theta_hat', 'bias', and 'se'.


Examples

Example 1: weighted_sample

items = ['a', 'b', 'c']
weights = [1.0, 3.0, 6.0]
samples = weighted_sample(items, weights, k=2, replace=False)
# Possible valid output: ['c', 'b']
# Item 'c' is selected first with probability 6/10 = 0.6.
# If 'c' is selected, 'b' is selected next with probability 3 / (1 + 3) = 0.75.

Example 2: jackknife_estimator

def sample_mean(arr):
    return sum(arr) / len(arr)

data = [1.0, 2.0, 3.0, 4.0, 5.0]
result = jackknife_estimator(data, sample_mean)
# Output:
# {
#   'theta_hat': 3.0,
#   'bias': 0.0,
#   'se': 0.7071067811865476
# }
# Explanation: The sample mean is unbiased, so bias is 0.0. Standard error equals s / sqrt(n) = sqrt(2.5 / 5) = 0.7071.

Constraints

  • len(items) == len(weights) == n where 2 0 and all weights[i] >= 0
  • len(data) >= 2
  • For weighted_sample with replace=False, k <= count_nonzero(weights)

Hints

Hint 1

For with-replacement sampling, compute cumulative weights and use binary search (bisect) with a random float in [0, total_weight) for O(log n) per draw.

Hint 2

For without-replacement sampling, either repeatedly sample and zero out selected weights, or use the Efraimidis-Spirakis algorithm (key = random() ** (1 / weight)) to pick the top-k in O(n log k).

Hint 3

In the jackknife function, ensure you create leave-one-out slices data[:i] + data[i+1:] carefully without modifying the original data in place.

Follow-up questions

  • How does the time complexity of delete-1 jackknife compare to bootstrap resampling, and when would you prefer jackknife over bootstrap?
  • How would you scale `weighted_sample` without replacement when `k` and `n` are very large (e.g., millions of items)?
  • Jackknife is known to fail (inconsistent variance estimator) for non-smooth statistics like the median. Why does this happen, and what modification (e.g., delete-d jackknife) can fix it?