Условие
В файле sleep.csv приведено время засыпания одного человека в разные дни. Нужно:
- Указать момент времени суток (с точностью до секунды), после которого с вероятностью 0.9 этот человек уже спит.
- Какова ошибка определения этого момента?
- Сколько дней наблюдения нужно добавить, чтобы уменьшить эту ошибку в 10 раз?
Решение
Подход
«Время, после которого с вероятностью 0.9 уже спит» = 0.9-квантиль распределения времени засыпания. Решение в три шага:
- Оценить квантиль по выборке.
- Оценить ошибку оценки квантиля — bootstrap-доверительный интервал или асимптотическая формула.
- Использовать связь «ошибка ∝ 1/√n» для оценки нужного числа наблюдений.
Реализация
import pandas as pd
import numpy as np
df = pd.read_csv("sleep.csv", parse_dates=["sleep_time"])
# приводим к "секундам с полуночи" для скалярного анализа
def time_to_seconds(ts):
return ts.dt.hour * 3600 + ts.dt.minute * 60 + ts.dt.second
x = time_to_seconds(df["sleep_time"]).values
n = len(x)
# 1. Точечная оценка квантиля
q90 = np.quantile(x, 0.9)
print(f"q90 = {q90:.0f} sec ({q90/3600:.2f}h)")
# 2. Bootstrap-CI для квантиля
def bootstrap_quantile_ci(x, q=0.9, B=10000, alpha=0.05):
n = len(x)
boot = np.empty(B)
for i in range(B):
boot[i] = np.quantile(np.random.choice(x, n, replace=True), q)
return np.percentile(boot, [100*alpha/2, 100*(1-alpha/2)])
lo, hi = bootstrap_quantile_ci(x, 0.9, B=10000)
half_width = (hi - lo) / 2
print(f"95% CI: [{lo:.0f}, {hi:.0f}], half-width = {half_width:.0f} sec")Сколько дней нужно для уменьшения ошибки в 10 раз
Стандартная ошибка квантиля при асимптотике:
где f(q_p) — плотность распределения в точке квантиля. Главный множитель — 1/√n. Чтобы ошибка уменьшилась в 10 раз, нужно увеличить n в 100 раз:
# Текущая ошибка half_width при n наблюдений
# Цель: half_width / 10
# n_new = n * 100
n_new = n * 100
extra_days = n_new - n
print(f"Текущее n={n}, нужно n={n_new}, добавить {extra_days} дней")Если в sleep.csv 30 дней наблюдения — потребуется 3000 дней (~8 лет). Это и есть ответ — обычно вызывает удивление, но 1/√n даёт именно такую асимптотику.
Альтернативная (более точная) формула
Если плотность f(q_p) оценить через KDE:
from scipy.stats import gaussian_kde
kde = gaussian_kde(x)
f_at_q = kde(q90)[0]
SE = np.sqrt(0.9 * 0.1 / n) / f_at_q
print(f"Asymptotic SE = {SE:.0f} sec")И сравнить с bootstrap-эстимейтом — должны быть в одном порядке.
Анализ результата
- Bootstrap менее чувствителен к форме распределения, чем асимптотика. На малом n (например, 30 дней) лучше bootstrap.
- 0.9-квантиль — экстремальная точка; оценка хвоста требует больше наблюдений, чем медиана. Поэтому даже 100 дней может быть мало.
- Если человек ложится «обычно в 23:00, иногда в 02:00» — выборка многомодальная, обычная экспонента/нормаль не подойдёт, а медиана и квантили распределены хорошо.
Подводные камни
- Время по кругу. Если человек ложится после полуночи (01:30), а другие дни — в 23:00, нельзя усреднять «секунды с полуночи». Сдвиньте «начало дня» на 18:00 или работайте с разностями относительно опорной точки.
- Малая выборка. На n=10 квантиль 0.9 — это фактически 9-я из 10 точек. CI огромный.
- Зависимость наблюдений. Дни недели коррелированы (выходные — позже). Bootstrap iid недостоверен; используйте block bootstrap.
- n×100 — формальная асимптотика. В реальности это нижняя граница; на конечном распределении может потребоваться ещё больше.
- Ошибка ≠ CI. «Ошибка» неоднозначна: standard error, half-width CI, RMSE? Уточняйте; в собеседовании предпочтительно — half-width CI.
- Точность до секунды. Современные часы измеряют поминутно; не имеет смысла указывать секунды, если в данных округление.
Эталонный ответ
q90 = np.quantile(x, 0.9)— точечная оценка.- Ошибка — half-width 95%-bootstrap-CI (~10000 ресемплов). Альтернативно — асимптотическая формула
sqrt(0.9·0.1/n)/f(q90). - Чтобы ошибка уменьшилась в 10 раз, нужно
nв 100 раз больше — это следствиеSE ∝ 1/√n. Если сейчас 30 дней — потребуется 3000.