Дисперсия больших чисел: почему E[x²] − E[x]² теряет точность
Датчик отдаёт большие абсолютные значения с небольшой вариацией. Для расчёта дисперсии написали mean(x*x) - mean(x)**2. На простых числах формула работает, но на данных около миллиарда результат получается нулевым или резко отличается от ожидаемого. Алгебра верна, вычисление с ограниченной точностью — нет.
В формуле вычитаются два близких больших числа. Полезные младшие разряды уже потеряны при вычислении квадратов и средних, поэтому вычитание не восстанавливает небольшую дисперсию. Расчёт отклонений от среднего позволяет работать с меньшими величинами.
Как изменить решение
import numpy as np
x = np.array([1_000_000_001, 1_000_000_002,
1_000_000_003], dtype=np.float64)
naive = np.mean(x*x) - np.mean(x)**2
stable = np.var(x, dtype=np.float64)
assert np.isclose(stable, 2/3)
assert not np.isclose(naive, stable)
print('naive:', naive)
print('var:', stable)Для трёх значений отклонения от среднего равны -1, 0 и 1, поэтому дисперсия с делением на N равна 2/3. np.var возвращает это значение, а результат прямой разности больших моментов расходится с ним.
Тип исходных данных тоже важен. Если значения сначала сохранены в float32 и маленькие различия уже округлились, dtype=float64 на этапе var их не вернёт. Выбирайте достаточную точность до хранения или преобразования измерений. Аналогично целочисленное x*x может переполниться до вызова mean.
Что проверить на своих данных
Для запуска установите python -m pip install numpy. Повторите пример после вычитания миллиарда из каждого значения: дисперсия должна сохраниться. Это хорошая проверка для собственного алгоритма, особенно если он агрегирует данные по частям. Затем проверьте ddof: np.var по умолчанию делит на N, а выборочная оценка часто требует ddof=1.
При потоковой обработке не объединяйте дисперсии простым средним. Нужны размеры групп, их средние и корректная поправка на различие средних между группами. Иначе даже точный расчёт каждой части даст неверный общий результат. Сохраните небольшой эталон с известными отклонениями и сравнивайте потоковый алгоритм с np.var на объединённых данных, включая группы с разным числом наблюдений.
Практика дальше
Численная точность начинается с выбора типа и формы массива. В тренажёре «NumPy и pandas» вы работаете с массивами, масками и вычислениями без циклов, затем переносите расчёты в таблицы. Короткие объяснения закрепляются задачами с кодом и автоматической проверкой.
Посмотреть программу и практику курса
Источник: Дисперсия больших чисел — официальная документация.
Другие задачи по теме: Календарь, пропуски и точность вычислений.
Продолжить на практике
- NumPy и pandas: практический тренажёр ↗
Бесплатный тренажёр NumPy и pandas для тех, кто знает основы Python. Научитесь очищать таблицы, объединять данные и собирать отчёты: 31 урок и 124 задачи с кодом и автоматической проверкой.
