Математический практикум по Питону.
Исследуется использование компьютера для вычисления статистических параметров, например, вероятности события. Также рассматриваются статистики, которые используется при проверки гипотез.
Это предварительная версия! Любые замечания приветсвуются.
import numpy as np
from matplotlib import pyplot as plt
import scipy.stats as models # Загружаем модуль знающий о сложных распределениях.
import scipy.special as sp
Формирование случайной величины
gen_data = np.random.randn( 10000 ) * 3.3 + 0.5 # Вывода нет потому что мы присвоили результат переменой.
gen_data.shape
# Строим гистограмму по заданному массиву чисел.
hist = plt.hist( gen_data, 50); # В переменную hist сохраняются сами значения гистограммы.
x = np.linspace(-10,10, 100)
p = models.norm.pdf( x, scale=3.3, loc=0.5 )
plt.plot(x, p)
plt.hist( gen_data, 50, density=True)
plt.plot( x, p, 'r')
Восстановление/оценка параметров случайной величины по выборке
mm, ss = models.norm.fit( gen_data ) # Вычислим параметры распределения по выборке.
mm, ss # Корректно когда искомое распределение известно.
np.mean( gen_data ), np.std( gen_data )
# Зная параметры распределения можно вычислить всякие вещи.
models.norm.cdf( -0.3, scale=ss, loc=mm ) # Например, можно вычислить вероятность события: < -0.3.
Мат ожидание от произвольной функции
def moment(x, n): # Вычислим моменты вручную.
return x**n
models.norm.expect( lambda x:moment(x,1), scale=3.3, loc=0.5) # Через нашу функцию.
models.norm.moment( 1, scale=3.3, loc=0.5), models.norm.mean( scale=3.3, loc=0.5) # Аналитически.
models.norm.expect( lambda x:moment(x,2), scale=3.3, loc=0.5) # Через явную функцию.
models.norm.moment( 2, scale=3.3, loc=0.5) # Аналитически, т.е. по формуле.
Хм.. Какие же точки используются для интегрирования...
l = []
def dd(x):
l.append(x)
return x
models.norm.expect( dd, scale=3.3, loc=0.5)
plt.plot(l, np.zeros_like(l), '*')
models.norm.stats( scale=3.3, loc=0.5 ) # По формуле.
Сравнение с численным
np.mean( gen_data < -0.3 ) # Если же нам неизвестно распределедние, то можно по данным.
models.norm.cdf( -0.3, scale=3.3, loc=0.5 ) # Если мы знаем случайную величину
gen_data = np.random.randn( 10000 ) * 3.3 + 0.5 # Вывода нет потому что мы присвоили результат переменой.
gen_data.shape
Вычисления вдоль оси
a = np.array( [[2, 3], [-3, 4], [3, -2]]) # Введем матрицу.
a
# В Numpy есть ряд функций, которые выполняют операцию reduce.
np.sum( a ) # Например, вычисляет сумму массива a: 3 + -6 + -1 + 2.
Такой вариант вызова выполняет операцию для всех элементов массива как для единого массива, т.е. без учета размерности. Но можно и указать вдоль какой именно размерности делать вычисления.
a[0,:], a[1,:], a[2,:]
a.sum( axis=1 )
a[:, 0], a[:, 1], a[:, 2] # Будет ошибка. Матрица то не квадратная.
a[:, 0], a[:, 1]
a.sum( axis=0 )
Для закрепления
a = np.array( [[[2, 3], [-3, 4]], [[-5, 7], [-9, 11]] ])
a.shape
a[:, 1, 0]
np.sum( a, axis = 0) # Вдоль нулевой делаем и сохраняем результат в другие размерности ( матрица 2 на 2).
# Например, -3 + -9 равно 12 сохранено в элемент матрицы 1, 0.
a[0, :, 1]
np.sum( a, axis = 1) # Суммируем вдоль первой и сохранаяем результат в оставшуюся размерность.
# Например, 3 + 4 равно 7 сохранено в элемент матрицы 0, 1.
Теперь продолжим наши изыскания...
n = 12
dat = np.random.randn(100000, n)*2 + 0.3
mm = dat.mean( axis=1 )
std = dat.std( axis=1 )
mm.shape, std.shape
def tstat_equal_var( means, stds, n ):
s = np.sqrt( (stds[0]**2 + stds[1]**2)/n )
v = (means[0] - means[1])/s
return v
# Хотим получить доступ одновременно к двум выборкам (значениям их статистики).
mm = mm.reshape(-1,2) # Иначе прилось бы обращатся так: i*2:(i+1)*2
mm.shape
mm[0]
std = std.reshape(-1,2)
stats = list(zip( mm, std ))
stats[0]
tt = list( map( lambda x: tstat_equal_var( x[0], x[1], n ) ,stats))
tt = np.array( tt )
plt.hist( tt, 30 );
np.mean( tt < -0.3 )
models.t.cdf( -0.3, 2 * n - 2 )
def gen_stat( dat ):
mm = np.mean(dat, axis=1)
std = np.std(dat, axis=1)
mm = mm.reshape(-1,2)
std = std.reshape(-1,2)
stats = list(zip( mm, std ))
tt = list( map( lambda x: tstat_equal_var( x[0], x[1], dat.shape[1] ) ,stats))
return np.array( tt )
tt = gen_stat(np.random.randn(100000, n) + 5.3)
np.mean( tt < -0.3 )
tt = gen_stat(np.random.randn(100000, n)*3.5 - 5.5)
np.mean( tt < -0.3 )
Сравнение двух нормальных распределений с одинаковой дисперсией.
dat1 = np.random.randn(10000)*2 + 0.5
dat2 = np.random.randn(10000)*2 + 3.5
plt.hist( dat1, 50 );
plt.hist( dat2, 50 );
dat1 = np.random.randn(n)*2 + 0.5
dat2 = np.random.randn(n)*2 + 3.5
meann = np.mean( dat1 ), np.mean( dat2 )
stdd = np.std( dat1 ), np.std( dat2 )
v = tstat_equal_var( meann, stdd, n )
v
vv = np.abs( v )
prob = np.mean( tt < -vv ) + np.mean( tt > vv )
prob
models.t.ppf( prob/2, 2 * n - 2 )
(1-models.t.cdf( vv, 2 * n - 2 ))
models.t.ppf( 0.0032, 2 * n - 2 )
Упражнение. (a/b test) Сделайте аналог для распределения Бернули. Рассматривайте, когда много зачений (считайте n большим). Тогда сработает закон больших чисел.
# Изучим Хи-квадрат численно.
gen_data = np.random.randn( 1000, 5) # 1000 раз по 5 нормальных распределений.
gen_data[0] # Так выглядит строчка матрицы.
# Убедимся в правильности размера массива (матрицы).
gen_data.shape # В скобках перечислены размер массива вдоль каждого из измерений.
gen_data[0].shape, gen_data[999].shape
sq_data = gen_data * gen_data # Массив состоящий их квадратов нормального распределения.
sq_data.shape
gen_data2 = np.sum( sq_data, axis = 1 ) # сумируем вдоль оси с индекосм 1 (нумирация начинается с 0).
gen_data2.shape # получаем массив состоящий из суммы 5 квадратов номального распределения
# т.е. выборка из Хи-квадрат с параметром 5.
Теперь забывае как мы его создали. Считаем, что данные откуда-то взялись.
hist = plt.hist( gen_data2, 50); # Сторим гистограмму. Она должна напоминать гистограмму Хи-квадрат.
cnt = np.mean( gen_data2 < 7 ) # Данная строчка посчитает сколько числе в выборке меньше 7.
cnt
# Можно воспользоватся явной функцией (chi2), которая использует формулу.
models.chi2.cdf( 7, 5 ) # Первое число указывает точку (7), воторое значение параметра (5) распределения.
# Замечаем, что значение близко к рассчетному.
models.chi2.mean( 5 )
it = models.chi2.interval( 0.95, 5 ) # Строим доверительный интервал.
it
models.chi2.cdf( it[0], 5 ), 1.0 - models.chi2.cdf( it[1], 5 ) # Проверяем доверительный интервал.
models.chi2.cdf( it[1], 5 ) - models.chi2.cdf( it[0], 5 ) # Проверяем его иначе.
np.mean( (it[0] <= gen_data2) & (gen_data2 <= it[1]) ) # Проверяем численно.
Критерий согласия Пирсона
N = 15
dat = models.poisson.rvs( mu=2.5, size=N )
dat
nmax = np.max( dat )
hh = np.histogram( dat, range(nmax+2), density=True )
hh
pp = models.poisson.pmf( mu=2.5, k=range(nmax+1) )
pp
hh[0].shape, pp.shape
ss = np.sum( (hh[0] - pp)**2/pp )
ss
ss * N # Значение статистики Пирсона.
# То с чем должны сравнить при 95% доверии. Если больше то отбрасываем гипотезу.
models.chi2.ppf( 0.95, N-1 )
dat = models.poisson.rvs( mu=2.5, size=N )
nmax = np.max( dat )
hh = np.histogram( dat, range(nmax+2), density=True )
pp = models.poisson.pmf( mu=1.5, k=range(nmax+1) ) # Распределение с другим параметром.
ss = np.sum( (hh[0] - pp)**2/pp )
ss * N
models.binom.rvs( p=0.3, n=3, size=N)
dat = models.randint.rvs( low=0, high=7, size=N) #binom.rvs( p=0.4, n=5, size=N)
nmax = np.max( dat )
hh = np.histogram( dat, range(nmax+2), density=True )
pp = models.poisson.pmf( mu=1.5, k=range(nmax+1) ) # Распределение с другим параметром.
ss = np.sum( (hh[0] - pp)**2/pp )
ss * N