Математический практикум по Питону.
Построение гипотез, проверка на нормальность и принадлежность распределению.
Это предварительная версия! Любые замечания приветсвуются.
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as models
Используем распределение Бернули
Задается вероятностью p происхождения события. Например, вероятность кликнуть на банер, зайти на сайт, прийти в магазин или купить товар. Это все разные вероятности. Важное, что некое событие может произойти и неоднократно.
models.bernoulli.rvs( 0.7, 10) # Так нельзя, так как 10 будет использоватся как сдвиг.
В питоне функции могут использовать два типа аргументов или иначе параметров, которые ей передаются. Первый тип позиционый, который является стандартным для большинства языков программиования. Суть в том, что параметры присваиваются в порядке и задания при вызове. А бывают аргументы именные, которые можно присвоить вне очереди.
Рассмотреть документацию по данной функции https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.bernoulli.html
Конкретно изучим описание функции rvs: rvs(p, loc=0, size=1, random_state=None)
Последнее к сожалению позволит понять какие значения были присвоены аргументам по умолчанию. часть аргументов просто перечислена (а данном случае только p), а часть через знак '='.
# Вообще у любого объекта, функции по соглашению может быть поле __doc__ которое дает краткую документацию по нему.
# Так как это поле является строчкой, то для аккуратного вывода лучше применить функцию print
# Подробнее о ней будет позже. Сейчас она используется для вывода самоописания функции.
print( models.bernoulli.rvs.__doc__ )
Из данного описания следует, что сначала идут позиционные параметры (arg1, arg2, ...), а потом именные (loc, size, random_state). Тем не менее, если они идут подряд, то присвоение идет подряд.
# В данном случае:
models.bernoulli.rvs( 0.7, 10) # 0.7 это параметр распределения, а 10 присвоилось аргументу loc.
models.bernoulli.rvs( 0.7, 10, 5) # Вот теперь очередь дошла до размера, которому было присвоено значение 5.
# Но можно было бы и явно присвоить переменной отвечащей за размер размер:
models.bernoulli.rvs( 0.7, size = 10 ) # Аргументу size присвоено значение вне очереди.
# Нельзя позиционный аргумент подавать после именных. Последнее выдаст соответствующую ошибку:
models.bernoulli.rvs( size = 10, 0.7 )
# Но если аргументы заданы по имени, то их можно подавать функции в произвольном порядке.
models.bernoulli.rvs( size = 10, p = 0.7 )
# В данном случае было задано и значение параметра loc, положения.
models.bernoulli.rvs( size = 10, p = 0.7, loc = 5 )
Закон больших чисел
Нам нужно взять среднее количество произошедших событий. Точнее понять как это среднее рапределено. Теория говрит, что оно должно стремится к нормальному распределению.
# Имитация закона больших чисел:
data = [] # Содали пустой список
for i in range( 1000 ): # Мы явно указали размер нешей выборки
lap = models.bernoulli.rvs( 0.7, size=100 ) # а также количество элементов в устреднение.
data.append( np.sum( lap )/100 )
plt.hist( data );
# Хотим цикл ещё раз запустить с другим количеством эллементов в сумме.
Продолжаем с Бернули
# Вводим понятие функции, т.е. сами пишем.
def buildBernSum( p, cycles = 1000, elems = 100 ): # Два аргумента. Оба необязательны. Имеют значение по умолчанию.
data = [] # Содали пустой список
for i in range( cycles ):
lap = models.bernoulli.rvs( p, size=elems ) # 0.7
data.append( np.sum( lap )/elems )
return data # Вернули результат.
bern = buildBernSum( 0.7 )
plt.hist( bern );
bern = buildBernSum( 0.7, elems = 10000 )
plt.hist( bern );
bern = buildBernSum( elems = 10000, p = 0.7)
plt.hist( bern );
bern = buildBernSum( 0.7, 100000, 10000 ) # Позиционное присвоение параметров
plt.hist( bern );
bern = buildBernSum( 0.7, 4000, 3000 )
plt.hist( bern );
#С увеличением параметров распределение будет стремится к нормальному
Статистика Шапиро-Уилка
np.random.seed( 2018 ) # Инициализация генератора случайных чисел.
dat = models.norm.rvs( 2, 3, size=10 ) # 2 и 3 это параметры нормального распределения.
plt.hist( dat );
t = models.shapiro( dat ) # Шапиро-Уилка тест для нормальности. Предполагается, что данных не очень много ( < 5000).
t
# t[0] -- Это значение самой статистики.
# t[1] -- Это значение p-value
print( "Статистика -- ", t[0], ", p-значение ", t[1])
Смотрим на p-значение. Если оно больше, например, 5%, то говорим, что с такой значимостью не можем отвергнуть гипотезу о нормальности даных.
plt.hist( dat * dat ); # Строим гистограмму для квадратов нормального распределения. Для данного распределения
models.shapiro( dat * dat ) # p-значение очень маленькое. Поэтому гипотезна о нормальности отвергнута.
plt.hist( 1/dat ); # Строим гистограмму для обратной величины от нормального распределения.
models.shapiro( 1/dat ) # Она тем более не являются нормальными. p-значение ну очень маленькое.
dat = models.norm.rvs( 2, 3, 10 ) # Возьмем новый набор данных.
plt.hist( dat * dat );
models.shapiro( dat * dat ) # p-значение чуть повыше, но все-равно меньше традиционной значимости 0.05.
plt.hist( 1/dat ); # При молой выборке конечно возможны ложные результаты
models.shapiro( 1/dat ) # Действительно смахивает на нормальное распределение. p-значение значительно выше 5%.
dat = models.norm.rvs( 2, 3, 100 ) # Если взять больше данных, то вероятность такого мала.
plt.hist( dat ); # С увеличением данных, статестический тест может давать странные результаты.
models.shapiro( dat ) # В данном случае хоть тест и пройден, все-таки 8% не сильно далеко от 5%.
plt.hist( dat * dat ); # Для произведения нормальных распределений.
models.shapiro( dat * dat ) # p-значение совсем маленькое. Поэтому, тест не пройден.
plt.hist( 1/dat ); # При большой выборке вероятность ложного результата для сильных отклонений от нормального мала.
models.shapiro( 1/dat ) # p-значение совсем маленькое. Поэтому, тест не пройден.
dat = models.uniform.rvs( size = 10 ) # Возьмем проcто-напросто другой тип раcпределения, а именно равномерное.
plt.hist( dat );
models.shapiro( dat ) # Тем не менее тест ломается на равномерном распределении, так как p-значение пройдено.
dat = models.uniform.rvs( size = 100 ) # Теперь увеличим объем выборки для равномерного распределения.
plt.hist( dat );
models.shapiro( dat ) # p-значение не пройдено, поэтому гипотезу отвергаем.
dat = models.f.rvs( 5, 7, size = 100 ) # f распределение с параметрами 5 и 7.
plt.hist( dat );
models.shapiro( dat ) # f распределении отвергаем, так как p-значение существенно меньше.
dat = models.f.rvs( 20, 35, size = 100 ) # f распределение с параметрами 20 и 35.
plt.hist( dat );
models.shapiro( dat ) # Для данных параметров оно тоже отвергнуто.
dat = models.norm.rvs( 2, 3, 1000 )
plt.hist( dat );
models.shapiro( dat ) # Все хорошо с p-значением.
Anderson-Darling
Предыдущая статистика работает только для нормального распределения. Для данного же теста можно задать используемое распределение: norm, expon, logistic, gumbel, gumbel_l, gumbel_r, extreme1. Пока будем оставатся в рамках нормального.
В Шапиро-Уилка граница была единственной. Той что обычно принято пользоваться. В методе же Андерсона таких границ считается несколько для различных уровней значимости.
dat = models.uniform.rvs( size = 100 ) # Пвторно проверим на равномером распределении.
plt.hist( dat );
models.anderson( dat, dist='norm' ) # Тест отвергает данные как принадлежащие равномерному распределению.
# Статистику нужно возвести в квадрат и сравнить с критерием. Если больше, то гипотеза отвергается.
dat = models.norm.rvs( 2, 3, 100 ) # Для начала прверим для нормального распределения.
res = models.anderson( dat )
res
res[1] # Значение значимости критических уровней.
res[2] # Сами уровни.
# Можно представить их матрицей.
table = np.array([res[1], res[2]])
table
# В res[0] у нас значение статистики.
res[0] # Для проверки гипотезы нужно сравнить это число с критическими значениями.
Бернули
bern = buildBernSum( 0.7, 4000, 3000 )
res = models.anderson( bern ) # Второй параметр указывает на распределение. Поумолчанию проверка на нормальность.
res # Дествительно стремимся к нормальному распределению.
params = models.norm.fit( bern )
params
models.norm.interval( 0.95, params[0], params[1] )
models.bernoulli.interval( 0.7, 0.7 ) # Системе плохо.
models.norm.interval( 0.95 )
pp = np.mean( bern )
pp
dd = np.sqrt(pp*(1-pp)/3000)
dd
lim = models.norm.interval( 0.95, 0, 1 )
lim
pp + dd*lim[0], pp + dd*lim[1]
models.laplace.rvs()