#!/usr/bin/env python # coding: utf-8 # # Когнитивные технологии # # *Алла Тамбовцева* # ### Библиотека `scipy` для статистики # Библиотека `scipy` (сокращение от *scientific Python*) включает в себя разные модули, позволяющие выполнять научные расчеты, решать задачи оптимизации, генерировать выборки из (псевдо)случайных величин с заданными параметрами, реализовывать статистические тесты и создавать статистические модели. # Импортируем библиотеку `pandas` и модуль `stats` из библиотеки `scipy`. # In[5]: import scipy.stats as st import pandas as pd # Загрузим базу данных (датафрейм) из csv-файла. Описание данных см.[здесь](https://vincentarelbundock.github.io/Rdatasets/doc/datasets/swiss.html). # In[6]: df = pd.read_csv('https://raw.githubusercontent.com/allatambov/Py-programming-3/master/add/swiss.csv') # In[7]: df.head() # Далее мы попробуем реализовать различные статистические тесты (применить статистические критерии) для сравнения средних значений или распределений в двух выборках. # # Предположим, что нам необходимо сравнить средние значения уровня детской смертности в кантонах Швейцарии, где преобладает католическое население и где преобладает протестантское население. Сформируем две выборки на основе имеющихся данных: выберем соответствующие строки в таблице и возьмем столбец `Infant.Mortality`. # In[8]: df[df.Catholic > 50] # для иллюстрации # In[9]: sample1 = df[df.Catholic > 50]["Infant.Mortality"] # выборка 1 sample1 # In[10]: sample2 = df[df.Catholic <= 50]["Infant.Mortality"] # выборка 2 sample2 # Теперь приступим к формальной проверке гипотез. # ### T-test (критерий Стьюдента для двух выборок) # T-test используется для сравнения средних значений двух генеральных совокупностей в предположении, что обе выборки взяты из нормального распределения. Существует две разновидности t-теста: t-тест для независимых выборок и t-тест для связных (парных) выборок. В связных выборках объекты связаны друг с другом. Пример связных выборок: значения уровня смертности в одних и тех же кантонах до и после какой-нибудь реформы) # # *Нулевая гипотеза:* средние значения двух генеральных совокупностей, откуда взяты выборки, равны, то есть $H_0: a_1=a_2$. # # *Альтернативная гипотеза:* средние значения двух генеральных совокупностей не равны, то есть: $H_1: a_1 \ne a_2$ # # В `stats` в t-тесте в качестве альтернативной гипотезы используется двусторонняя альтернатива (средние *не равны*) и всегда выводится соответствующее p-value (*two-tailed*). То же будет характерно для всех последующих тестов. Так как наши выборки независимы, нам нужна функция `ttest_ind()`, от *independent*. # In[11]: st.ttest_ind(sample1, sample2) # Что возвращает эта функция? Наблюдаемое значение t-статистики и p-value. Результат понятный, только более лакончиный по сравнению с тем, что выводит R и другие статистические пакеты. # # *Выводы:* так как p-value больше любого конвенционального уровня значимости (1%, 5%, 10%), на имеющихся данных на любом разумном уровне значимости нет оснований отвергнуть нулевую гипотезу. Средний уровень детской смертности в католических и протестантских районах можно считать одинаковым. # По умолчанию считается, что дисперсии генеральных овокупностей равны. Часто это бывает не так, и такое предположение без формальной проверки и без содержательных соображений может казаться нереалистичным. Если мы предполагаем, что дисперсии генеральных совокупностей не равны, то это можно учесть, добавив аргумент `equal_var`: # In[12]: st.ttest_ind(sample1, sample2, equal_var = False) # Принципиальных отличий в данном случае в результатах не наблюдается. А формально проверить гипотезу о равенстве дисперсий двух генеральных совокупностей (которые описываются двумя случайными величинами) можно с помощью F-критерия. # # *Нулевая гипотеза:* дисперсии двух генеральных совокупностей равны, то есть $H_0: \sigma_1^2 = \sigma_2^2$ # # *Альтернативная гипотеза:* дисперсии двух генеральных совокупностей не равны, то есть $H_1: \sigma_1^2 \ne \sigma_2^2$. # # Реализовать F-тест, который нам нужен именно для этого случая, в Python сразу не получится: встроенная функция `f_oneway()` используется для однофакторного дисперсионного анализа (ANOVA), речь о котором пойдёт далее. Можно попробовать реализовать этот тест «вручную», рассчитав частное выборочных дисперсий и поработав с F-распределением, но давайте пойдём другим путём и воспользуемся другими критериями для сравнения дисперсий, которые явно встроены в Python. # # Например, тест Левена: # In[14]: st.levene(sample1, sample2) # На любом разумном уровне значимости нет оснований отвергнуть нулевую гипотезу о равенстве дисперсий. # ### Wilcoxon test & Mann-Whitney test (две выборки) # Если мы не можем считать распределение генеральных совокупностей, откуда взяты выборки, нормальным, то следует использовать методы, основанные не на самих наблюдениях в выборках, а на их рангах. Для сравнения распределений (иногда речь идет о сравнении медиан) используются тесты Уилкоксона и Манна-Уитни. Начнем с теста Уилкоксона (не проверяем, является ли распределение данных нормальным, просто для примера используем те же выборки). # In[15]: st.wilcoxon(sample1, sample2) # Неудача! Проблема в том, что реализация критерия Уилкоксона в `stats` требует, чтобы выборки были одинакового размера. Но это ограничение можно обойти, просто выбрав другой критерий – критерий Манна-Уитни, который используется для аналогичных задач. # # *Нулевая гипотеза:* выборки взяты из одного и того же распределения, то есть $H_0: F(x) = G(x)$ # # *Альтернативная гипотеза:* выборки взяты из разных распределений, то есть $H_1: F(x) \ne G(x)$. # In[16]: st.mannwhitneyu(sample1, sample2) # Опять же, на имеющихся данных на любом уровне значимости нет оснований отвергнуть нулевую гипотезу. Выборки взяты из одного и того же распределения. # ### ANOVA # Если выборок больше, чем две, то использовать указанные выше критерии нельзя. В предположении, что все выборки взяты из нормального распределения, для сравнения средних значений более чем в двух группах используется однофакторный дисперсионный анализ (ANOVA, *analysis of variance*). # # *Нулевая гипотеза*: средние значения по всем $k$ группам (во всех генеральных совокупностях) равны, то есть $H_0: a_1 = a_2 = \dots = a_k$ # # *Альтернативная гипотеза*: средние значения по всем группам (во всех генеральных совокупностях) не равны. # # Чтобы не создавать искусственные группы на основе данных в *swiss*, загрузим таблицу с весами цыплят, которых кормили разным кормом :) Описание см. [здесь](https://vincentarelbundock.github.io/Rdatasets/doc/datasets/chickwts.html). # In[7]: dat = pd.read_csv('https://raw.githubusercontent.com/allatambov/Py-programming-3/master/add/chickwts.csv') # In[8]: dat.head() # **Задание:** разбить датафрейм на группы с помощью `groupby` по переменной `feed` и сохранить значения `weight` в словарь. # *Решение:* # In[9]: wgt = {} for name, d in dat.groupby('feed'): wgt[name] = d.weight # In[10]: wgt # Теперь ANOVA (`f_oneway` от *One-Way ANOVA*): # In[11]: st.f_oneway(wgt['casein'], wgt['horsebean'], wgt['linseed'], wgt['meatmeal'], wgt['soybean'], wgt['sunflower']) # Функция возвращает наблюдаемое значение F-статистики и p-value. В данном случае p-value близко к 0, поэтому гипотезу о равенстве средних генеральных совокупностей по группам можно отвергнуть на 1% уровне значимости. Средний вес цыплят, которых кормили разным кормом, отличается (ещё бы, *horsebean* или *sunflower*!). # ### Kruskal-Wallis test # Критерий Краскела-Уоллиса используется, когда нам необходимо сравнить распределения более, чем в двух группах в предположении, что выборки взяты не из нормального распределения (распределения неизвестны). # # *Нулевая гипотеза*: выборки взяты из одного и того же распределения, то есть: $H_0: F(x) = G(x) = \dots = H(x)$ # # *Альтернативная гипотеза*: выборки взяты из разных распределений. # In[12]: st.kruskal(wgt['casein'], wgt['horsebean'], wgt['linseed'], wgt['meatmeal'], wgt['soybean'], wgt['sunflower'])