Когда изучается распределение вероятностей значений какого-нибудь признака чего-нибудь, например, высоты берез в лесу, то может показаться, что в массиве полученных данных значения будут представлены

  • равномерно, когда все встречаются с одинаковой частотой;

  • нормально — чем ближе к арифметически среднему, тем значений больше;

  • по принципу дизруптивного отбора, при котором много объектов имеют значения признака ближе к его минимуму и максимуму, а в районе среднего почти никого;

  • вдруг согласно закону Парето, когда 80% элементов массива будут иметь значения в диапазоне 20% от всех возможных значений признака;

  • или как либо еще.

В любом случае мы точно не знаем, какой будет график, и данные должны нас удивить. Только после их обработки можно будет сделать выводы и строить стратегию.

Проблема в том, что в большинстве случаев реальных данных нет, а почувствовать себя исследователем и творцом хочется. Поэтому в том же NumPy есть возможность симулировать то или иное распределение и потом использовать его в компьютерных программах (в машинном обучении, играх), которые как бы что-то делают без нашего ведома, но подчиняются определенным "законам". То есть не просто ради иллюстрации распределений. Этой ерундой займемся мы.

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

fig, axs = plt.subplots(1, 3, figsize=(15, 4.6))

a = np.random.uniform(low=-3, high=3, size=1000)
sns.histplot(a, ax=axs[0], bins=20, color='lightblue', alpha=0.8)

b = np.random.normal(scale=3, size=1000)
sns.histplot(b, ax=axs[1], kde=True)

c = np.random.pareto(size=1000, a=40)
sns.histplot(c, color='c', ax=axs[2], kde=True)

plt.tight_layout()
plt.show()
Высота каждого столбца — это количество элементов массива со значением соответствующим значению по оси X (или попадающих в диапазон, "захватываемый" столбцом). Всего в каждом массиве по тысяче элементов.
Высота каждого столбца — это количество элементов массива со значением соответствующим значению по оси X (или попадающих в диапазон, "захватываемый" столбцом). Всего в каждом массиве по тысяче элементов.

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

data = {
  'a': np.random.normal(loc=100, scale=1, size=1000),
  'b': np.random.normal(loc=100, scale=4, size=1000),
}

fig, axs = plt.subplots(1, 3, figsize=(15, 4.6))
sns.histplot(data['a'], ax=axs[0])
sns.histplot(data['b'], ax=axs[1], color='darkorange')
sns.kdeplot(data, ax=axs[2])
При scale=1 крайние значения находятся около 97-ми и 103-х. При scale=4 разброс больше, но количество значений близких к сотне и равных ей куда меньше, что видно на графике плотности.
При scale=1 крайние значения находятся около 97-ми и 103-х. При scale=4 разброс больше, но количество значений близких к сотне и равных ей куда меньше, что видно на графике плотности.

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

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

fig, axs = plt.subplots(1, 2, figsize=(10, 4.6))
colors = ('r', 'g')

for i in range(2):
    a = np.random.binomial(n=10, p=0.5, size=8)
    sns.histplot(a, ax=axs[i], color=colors[i], discrete=True)
    axs[i].set_title(f'{a}')
    axs[i].set_xticks(range(11))
    axs[i].set_yticks(range(6))
    axs[i].set_ylabel('Количество экспериментов')
В заголовки плотов выведены массивы, на основе которых строятся гистограммы. Так значение 5 во втором массиве встречается четыре раза, а в первом только два.
В заголовки плотов выведены массивы, на основе которых строятся гистограммы. Так значение 5 во втором массиве встречается четыре раза, а в первом только два.

Итак, было проведено два исследования с одинаковыми параметрами. В результате имеются два массива. В каждом исследовании было по 8 экспериментов (это количество элементов в массиве). В каждом эксперименте происходило по 10 попыток чего-то там такого, которое либо завершается "успешно", либо терпит "неудачу". Каждое число (понятно, что больше десяти оно быть не может) в массиве показывает количество успехов в определенном эксперименте.

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

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

Параметр p биномиального распределения задает вероятность успеха. Если его значение равно 0.5, то равновероятны оба исхода. При 0.8 успех ожидается в 80% случаев.

fig, axs = plt.subplots(2, 2, figsize=(10, 8))

a = np.random.binomial(n=10, p=0.5, size=1000)
sns.histplot(a, ax=axs[0,0], color='g', alpha=0.3, discrete=True)

b = np.random.binomial(n=10, p=0.8, size=1000)
sns.histplot(b, ax=axs[0,1], color='lightblue', discrete=True)

d = np.random.binomial(n=1000, p=0.5, size=20)
sns.histplot(d, ax=axs[1,0], color='lightgrey')

e = np.random.binomial(n=1000, p=0.5, size=1000)
sns.histplot(e, ax=axs[1,1], color='pink')
В верхних плотах в экспериментах одинаковое число попыток, однако пики находятся у разных значений по оси X.
В верхних плотах в экспериментах одинаковое число попыток, однако пики находятся у разных значений по оси X.

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

Что касается схожести графиков нормального и биномиального распределений, его можно объяснить, если представить, что успехи в эксперименте — это вариативное значение признака. Но что делать с параметрами? Они принципиально разные. В биномиальном распределении сложно влиять на разброс значений, то есть покатость "плечей". Так или иначе меняя параметры (size и n), мы лишь делаем данные определеннее или случайнее. Можно получить нормальное распределение только с конкретным стандартным отклонением. При этом путь к нормальности идет через большое количество экспериментов. Однако в условиях моделирования скорее будут интересны варианты с ограниченным количеством экспериментов, так как это внесет в программу случайность с определенными характеристиками.

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

Биномиальное не является каким-то особенным. Скорее наоборот — большинство также специфичны, хотя графики у них могут быть похожими. Например, распределение Парето похоже на экспоненциальное, но во втором случае имеются в виду замеры промежутка времени между двумя событиями. Есть вообще узко предметные, описывающие физические явления или свойства веществ (распределение Максвелла).

b = np.random.exponential(scale=2, size=1000)
sns.kdeplot(b, ax=axs[0])

d = np.random.pareto(a=10, size=1000)
sns.kdeplot(d, color='r', ax=axs[1])
То есть график без контекста — это ничто.
То есть график без контекста — это ничто.

Когда в XIX веке Грегор Мендель изучал наследование признаков гороха при дигибридном скрещивании, то во втором поколении он наблюдал соотношение фенотипов 9:3:3:1. При тригибридном это было бы 27:9:9:9:3:3:3:1. Оба варианта можно описать формулой (3:1)n и написать функцию, симулирующую такое распределение. Вопрос только "зачем". Ведь в конце концов было открыто, что за таким соотношением стоит независимое расхождение гомологичных хромосом. И если, допустим, в программе симулировать этот процесс, то третий закон Менделя будет неизбежным следствием.

С другой стороны, если в программах нет никакой материи, ее поведение и свойства можно только моделировать, то разные распределения вероятно позволяют это делать наиболее простым способом. Проблема может быть в их корректном использовании.

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