В этой заметке я хочу разобрать несколько «парадоксов» в данных, о которых полезно знать как начинающему аналитику данных, так и любому человеку, кто не хочет быть введенным в заблуждение некорректными статистическими выводами.

За рассматриваемыми примерами не кроется сложной математики помимо базовых свойств выборки (таких, как среднее арифметическое и дисперсия), зато такие кейсы могут встретиться и на собеседовании, и в жизни.

«Новозеландцы, эмигрирующие в Австралию, повышают IQ обеих стран»

Эту цитату приписывают сэру Роберту Малдуну, премьер-министру Новой Зеландии. А может ли такое быть с точки зрения математики?

Итак, феноменом Уилла Роджерса называют кажущийся парадокс, заключающийся в том, что перемещение (численного) элемента из одного множества в другое может увеличить среднее значение обоих множеств. Начнем с очевидного примера: рассмотрим множества A = \{0, 1, 2\}и B = \{100, 1000\}. У Aсреднее арифметическое элементов составляет 1, а у Bсреднее арифметическое элементов составляет 550. Если взять число 100 и переместить его из второго множества в первое, то получатся множества A' = \{0, 1, 2, 100\}со средним арифметическим 25.75 > 1 и B' = \{1000\}со средним арифметическим 1000 > 550.

Тем не менее, совсем не обязательно, чтобы множества располагались так далеко друг от друга на числовой прямой, и не обязательно, чтобы перемещаемый элемент был минимальным во втором множестве и/или стал максимальным в первом.

Например, пусть A = \{1, 3, 5, 7, 9\}, B = \{4, 6, 8, 10\}. Среднее элементов множества Aсоставляет 5, а элементов множества B— 7. Теперь переместим 6 из второго множества в первое, и получим множества A' = \{1, 3, 5, 6, 7, 9\}со средним значением 5.1(6) и B' = {4, 8, 10}со средним значением 7.(3).

На самом деле такое увеличение обоих средних происходит при следующих условиях:

  • перемещаемый элемент строго меньше среднего значения элементов второго множества до его удаления;

  • перемещаемый элемент строго больше среднего значения первого множества до его добавления;

  • как следствие, изначально среднее значение элементов первого множества (куда перемещают) должно быть строго меньше, чем у второго (откуда перемещают).

Вывод: подобная ситуация может встретиться в различных областях. Например, при улучшении диагностики заболеваний на ранней стадии ожидаемая продолжительность жизни среди здоровых и среди больных может увеличиться в одной и той же выборке, если некоторые из «здоровых» (а на самом деле плохо обследованных) перейдут в категорию «больных», и многие из них успешно вылечатся благодаря раннему выявлению болезни.

И да, внимательный читатель скажет, что никакого парадокса тут нет, и будет абсолютно прав. Это явление звучит немного контринтуитивно на словах, но примеры выше делают его очевидным.

Парадокс Симпсона

Представьте, что вы работаете в фирме, которая продает два типа продуктов, допустим, сепульки и бирюльки. (Допустим для простоты модели, что сепульки и бирюльки всегда учитываются в чеках по отдельности.) Утром к вам в кабинет забегает радостный стажёр-аналитик и сообщает, что за последний месяц средний чек в категории сепулек вырос на 5%, средний чек в категории бирюлек — на 7%. Общий средний чек он не проверял, но логично предположить, что он тоже вырос на некоторую величину в интервале между 5 и 7 процентов. Что могло пойти не так?

Февраль

Март

Сепульки, средний чек

200 рублей

210 рублей (+5%)

Бирюльки, средний чек

100 рублей

107 рублей (+7%)

Вы открываете систему аналитики, смотрите подробнее и понимаете, что общий средний чек фирмы уменьшился, хотя цены на товары не менялись (то есть средний чек пропорционален количеству товаров в чеке), и скидок не было. Добавим в нашу оптимистичную таблицу дополнительные данные, без которых нельзя вычислить общий средний чек:

Февраль

Март

Сепульки, средний чек

200 рублей

210 рублей (+5%)

Бирюльки, средний чек

100 рублей

107 рублей (+7%)

Доля покупок сепулек

50%

35%

Доля покупок бирюлек

50%

65%

Общий средний чек

150 рублей

145,05 рублей (-4,63%)

Средний чек в марте вычислен так: 0,35 \times 210 + 0,65 \times 107 = 145,05 < 150.

Вывод: не стоит делать выводы по отдельным показателям, если ключевую метрику задают не только они (в данном случае стажёр не учел, что в среднем чеке по всем покупкам категории фактически взвешиваются пропорционально доле покупок в них). Понимание парадокса Симпсона может уберечь от неверных выводов в том числе и при AB-тестировании.

Квартет Энскомба

А теперь история о том, почему визуализация данных бывает буквально необходима. Представьте, что вам рассказывают о четырёх множествах точек (x, y), про которые известно следующее: среднее значение переменной x, дисперсия переменной x, среднее значение переменной y, дисперсия переменной y и корреляция между переменными x, y у них совпадают* для каждого из множеств. А также совпадают* коэффициенты, задающие прямую линейной регрессии.

*с точностью до двух-трёх знаков после запятой

Казалось бы, выборки должны быть очень похожи между собой. Но здесь подвох кроется в том, что по умолчанию многие представляют себе что-то вроде нормального распределения (или другой из основных типов), хотя об этом изначально ничего не сказано. Воспользуемся датасетом из библиотеки seaborn и визуализируем эти данные:

import seaborn as sns
sns.set_theme(style="ticks")
# Load the example dataset for Anscombe's quartet
df = sns.load_dataset("anscombe")

# Show the results of a linear regression within each dataset
sns.lmplot(
    data=df, x="x", y="y", col="dataset", hue="dataset",
    col_wrap=2, palette="muted", ci=None,
    height=4, scatter_kws={"s": 50, "alpha": 1}
)
Очень разные наборы данных, но прямая линейной регрессии у всех (почти!) одна и та же
Очень разные наборы данных, но прямая линейной регрессии у всех (почти!) одна и та же

Посчитаем характеристики этих наборов точек:

mean_1 = df[df["dataset"] == "I"].mean()
mean_2 = df[df["dataset"] == "II"].mean()
mean_3 = df[df["dataset"] == "III"].mean()
mean_4 = df[df["dataset"] == "IV"].mean()
mean_1, mean_2, mean_3, mean_4

Этот код для подсчета средних по координатам xи yвыводит следующий результат:

(x    9.000000
 y    7.500909
 dtype: float64,
 x    9.000000
 y    7.500909
 dtype: float64,
 x    9.0
 y    7.5
 dtype: float64,
 x    9.000000
 y    7.500909
 dtype: float64)

Код для остальных характеристик кладу под кат, чтобы не делать статью слишком длинной:

Hidden text

Дисперсия

std_1 = df[df["dataset"] == "I"].std()
std_2 = df[df["dataset"] == "II"].std()
std_3 = df[df["dataset"] == "III"].std()
std_4 = df[df["dataset"] == "IV"].std()
std_1, std_2, std_3, std_4
(x    3.316625
 y    2.031568
 dtype: float64,
 x    3.316625
 y    2.031657
 dtype: float64,
 x    3.316625
 y    2.030424
 dtype: float64,
 x    3.316625
 y    2.030579
 dtype: float64)

Коэффициент корреляции xи y

import numpy as np

corr_1 = np.corrcoef(df[df["dataset"] == "I"]["x"], df[df["dataset"] == "I"]["y"])[0, 1]
corr_2 = np.corrcoef(df[df["dataset"] == "II"]["x"], df[df["dataset"] == "II"]["y"])[0, 1]
corr_3 = np.corrcoef(df[df["dataset"] == "III"]["x"], df[df["dataset"] == "III"]["y"])[0, 1]
corr_4 = np.corrcoef(df[df["dataset"] == "IV"]["x"], df[df["dataset"] == "IV"]["y"])[0, 1]
corr_1, corr_2, corr_3, corr_4
(0.81642051634484, 0.8162365060002428, 0.8162867394895984, 0.8165214368885028)

Прямая линейной регрессии y = kx + b

k1, b1 = np.polyfit(df[df["dataset"] == "I"]["x"], df[df["dataset"] == "I"]["y"], 1)
k2, b2 = np.polyfit(df[df["dataset"] == "II"]["x"], df[df["dataset"] == "II"]["y"], 1)
k3, b3 = np.polyfit(df[df["dataset"] == "III"]["x"], df[df["dataset"] == "III"]["y"], 1)
k4, b4 = np.polyfit(df[df["dataset"] == "IV"]["x"], df[df["dataset"] == "IV"]["y"], 1)

k1, k2, k3, k4, b1, b2, b3, b4
(0.5000909090909095,
 0.5000000000000002,
 0.499727272727273,
 0.4999090909090908,
 3.0000909090909076,
 3.0009090909090905,
 3.0024545454545457,
 3.0017272727272712)

Вывод: иногда и взгляда на все основные статистики не будет достаточно, и тогда необходима визуализация. При этом зачастую даже самого простого scatter plot (графика с точками на плоскости) хватит, чтобы заметить различия в выборках.

The Datasaurus Dozen

Квартет Энскомба наглядно демонстрирует, почему визуализация данных важна, но подобрать 4 множества по 11 точек — ничего особенного, не так ли? Более интересным примером может быть «датазаврова дюжина», состоящая из 13 наборов точек, которые складываются в различные фигуры.

Твит автора датасета The Datasaurus Dozen в качестве иллюстрации
Твит автора датасета The Datasaurus Dozen в качестве иллюстрации

Подход авторов заключался в том, что изначально был взят «динозавр» из точек, а затем данные итеративно незначительно менялись так, чтобы значения средних, дисперсий и коэффициента корреляции оставались такими же с точностью до двух знаков после запятой, до тех пор, пока не получалась очередная фигура (овал, звезда и так далее). Для каждой из полученных фигур потребовалось около 200 тысяч итераций алгоритма.

Псевдокод алгоритма для генерации таких наборов точек имеет следующий вид:

current_ds ← initial_ds
for x iterations, do:
    test_ds ← perturb(current_ds, temp)
    if similar_enough(test_ds, initial_ds):
        current_ds ← test_ds

function perturb(ds, temp):
    loop:
        test ← move_random_points(ds)
        if fit(test) > fit(ds) or temp > random():
            return test
  • initial_ds — изначальный набор точек

  • current_ds — набор точек на данный момент

  • fit() — функция, проверяющая, насколько набор точек на данный момент напоминает нужную фигуру

  • similar_enough() — функция, проверяющая, что значения статистик достаточно близки

  • move_random_points() — функция, случайным образом сдвигающая точки

Заключение

Все эти примеры подводят нас к важности применения разведочного анализа данных (exploratory data analysis) — это выражение обозначает подход к работе с данными через анализ всех основных показателей и (практически всегда) их визуализацию. Критический взгляд на выводы, сделанные по паре показателей — важная черта как аналитика данных, так и любого человека, который не хочет позволить себя обмануть.

Спасибо, что дочитали! Буду рада дополнениям и вопросам в комментариях.

Комментарии (3)


  1. ksbes
    04.04.2024 09:17
    +6

    Главная загадка любого статистика: "что, блин, вообще эти люди хотят посчитать на самом деле?"


    1. NechkaP Автор
      04.04.2024 09:17
      +2

      Вот-вот) Так же как и загадка любого статистика, который видит в жизни чей-то вывод из данных: "что, блин, вообще эти люди посчитали на самом деле, чтобы сделать очередной заголовок про среднюю температуру зарплату по больнице 100 тысяч? Медиану, хитрое среднее с весами?"
      Вспоминается книга "Как лгать с помощью статистики", но это уже другая история)


    1. Ivan22
      04.04.2024 09:17

      Главная загадка любого статистика: "что, блин, вообще эти люди хотят посчитать на самом деле?"

      Да, верно. Потому что когда знаешь какой результат ты хочешь получить, подогнать нужные данные под него -раз плюнуть