Перевод статьи «Using For Loops in Python: Calculating Probabilities».
Из этой статьи вы узнаете, почему циклы незаменимы в построении статистических моделей.
Циклы — одно из фундаментальных понятий в программировании на Python. Особенно важны эти знания при работе с большим массивом данных.
У многих занимающихся статистикой и дата сайенс, возникает мысль: «А зачем нам все это? Ведь привести наш код в порядок могут программисты!».
Но порой человек, пишущий код, должен знать и статистику, и способы обработки выходных данных. Эти процессы неразделимы!
Вот один из примеров, когда умелое применение for
облегчает и улучшает статистический анализ.
Кумулятивные биномиальные вероятности — краткий экскурс
Для вычисления вероятности нам необходимы две переменные: N (число наблюдений) и λ (лямбда — количество попаданий в цель/количество благоприятных исходов в серии экспериментов). Кумулятивная биномиальная вероятность означает следующее: с увеличением количества испытаний возрастает шанс возникновения какого-либо события.
probability = 1-((1-λ)^N)
Пример. Возьмем «честный» шестигранный кубик и вычислим вероятность выпадения 6. Она равна 1/6. Но мы проведем этот эксперимент 10 раз подряд:
1-((1-0.1667)^10) = 0.8385
Как видите, шанс выпадения 6 увеличивается до 83.85%.
Закон больших чисел гласит: увеличиваем количество испытаний — увеличивается шанс возникновения какого-либо события. Это утверждение справедливо даже в том случае, если вероятность события в рамках одного испытания очень низкая. А теперь мы разберем, как увеличивается вероятность события с увеличением количества испытаний.
Модель без циклов
Ниже — скрипт, вычисляющий кумулятивную биномиальную вероятность без циклов:
import numpy as np import pandas as pd l = 0.02 m = 0.04 n = 0.06 p=np.arange(0, 100, 1) h = 1 - l j = 1 - m k = 1 - n q = 1-(h**p) r = 1-(j**p) s = 1-(k**p)
l
,m
иn
— вероятности определенных событий.p
— количество испытаний (до 100)q
,r
иs
— кумулятивные биномиальные вероятности. Их значение будет расти вместе с количеством испытаний.
Вывод:
>>> q array([0., 0.02, 0.0396, 0.058808, 0.07763184, 0.0960792, 0.11415762, 0.13187447, 0.14923698, 0.16625224, ..., 0.8532841, 0.85621842, 0.85909405, 0.86191217, 0.86467392]) >>> r array([0., 0.04, 0.0784, 0.115264, 0.15065344, 0.1846273, 0.21724221, 0.24855252, 0.27861042, 0.307466, ..., 0.97930968, 0.9801373, 0.9809318, 0.98169453, 0.98242675]) >>> s array([0., 0.06, 0.1164, 0.169416, 0.21925104, 0.26609598, 0.31013022, 0.35152241, 0.39043106, 0.4270052, 0.46138489, 0.49370179, 0.52407969, 0.5526349, 0.57947681, ..., 0.99720008, 0.99736807, 0.99752599, 0.99767443, 0.99781396])
Как видим, вероятности q
, r
и s
растут прямо пропорционально количеству испытаний.
Но у модели без использования циклов есть существенный недостаток — вероятности событий указываются конечным пользователем. А что, если мы захотим перебрать вероятности от 0.01 до 0.99?
Модель с циклами: генераторы списков и двумерные массивы
В этот раз мы рассмотрим другую модель: у нас будет лишь одно событие и его вероятность. Значения будут перебираться от 0.01 до 0.99. Количество испытаний все то же — 100.
import numpy as np import pandas as pd # генератор списка probability = [x*0.01 for x in range(1,100)] probability = np.array(probability) h = 1 - probability # создаем двумерный массив result = 1-h[:, np.newaxis] ** np.arange(1,100) print(result)
Вывод:
>>> probability array([0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, ... 0.96, 0.97, 0.98, 0.99])
Обратите внимание, что использование генератора списков обязательно. Причина проста — функция range() работает только с int
, а не с float
.
Возможно, вы заметили, что предпоследняя строка кода строит двумерный массив для вычисления кумулятивных биномиальных вероятностей. Если бы мы не использовали двумерный массив, результат был бы другой: мы бы вычислили значения, но они не были бы отсортированы.
>>> for i in range(1,100,1): >>> print(1-(h**i)) [0.01 0.02] [0.0199 0.0396] ... [0.62653572 0.86191217] [0.63027036 0.86467392]
Вместо этого мы бы хотели видеть отсортированный массив: [0.01, 0.0199, …, 0.62653572, 0.63027036] и [0.02, 0.0396, …, 0.86191217, 0.86467392]
.
Что интересно — транспонирование не даст нужного результата, так как h
— одномерный массив.
Можно поступить так: вычислить двумерный массив и вывести его в консоль напрямую:
>>> result = 1-h[:, np.newaxis] ** np.arange(1,100) >>> result array([[0.01, 0.0199, 0.029701, ..., 0.62653572, 0.63027036], [0.02, 0.0396, 0.058808, ..., 0.86191217, 0.86467392], [0.03, 0.0591, 0.087327, ..., 0.94946061, 0.9509768], ..., [0.97, 0.9991, 0.999973, ..., 1., 1., 1.], [0.98, 0.9996, 0.999992, ..., 1., 1., 1.], [0.99, 0.9999, 0.999999, ..., 1., 1.,1.]])
Как видите, мы только что вычислили кумулятивную биномиальную вероятность для интервала от 0.01 до 0.99.
Также использование цикла for
облегчило нам задачу, ведь мы автоматически вычислили нужные нам вероятности — от 0.01 до 0.99! Делать все это вручную — задача сложная и неблагодарная.
Вывод
Итак, мы научились:
- вычислять кумулятивную биномиальную вероятность.
- использовать циклы for для перебора огромного диапазона значений.
- работать с float при помощи генератора списков.
- работать с двумерными массивами, если транспонирование одномерного не имеет смысла.