Рассмотрим процедуру проверки гипотез о равенстве средних на основании двух выборок. Задача состоит в том, чтобы проверить равны ли средние значения этих выборок. Напомним, что среднее значение является очень важной величиной, характеризующей выборку и процесс, ее генерирующий. Поэтому задача сравнения средних очень часто возникает в самых разных областях.
Для начала сгенерируем выборки с известными параметрами и на их примере пройдем все этапы решения задачи.
'rvs2': stats.norm.rvs(loc=5, scale=10, size=500),
'rvs3': stats.norm.rvs(loc=8, scale=10, size=500)})
df.head()
rvs1 | rvs2 | rvs3 | |
---|---|---|---|
0 | 14.487964 | 23.363967 | 18.002069 |
1 | -0.218182 | -1.492428 | 17.301648 |
2 | 16.216604 | -11.324697 | 8.825380 |
3 | -5.000411 | 15.589422 | 11.170270 |
4 | -5.786298 | 13.074362 | 13.508006 |
Для того чтобы использовать двухвыборочный критерий Стьюдента, сначала нужно убедиться, что распределения выборок существенно не отличаются от нормального. Для этого давайте построим Q-Q plot для каждого из выборок.
Shapiro-Wilk normality test, W-statistic: 0.996880, p-value: 0.453802 Shapiro-Wilk normality test, W-statistic: 0.995878, p-value: 0.216082 Shapiro-Wilk normality test, W-statistic: 0.997995, p-value: 0.828128
В случае применения критерия Стьюдента имеем следующую нулевую гипотезу:среднее значение рассматриваемых выборок не отличаются. Альтернативная гипотеза:
средние по выборкам отличаются. Для того чтобы воспользоваться готовой
реализацией двухвыборочного теста Стьюдента в случае независимых выборок, нам
понадобится библиотека scipy, модуль stats. В данном случае мы используем функцию
ttest ind, от слова independent. В метод мы передаем данные, связанные с одной
выборкой, с другой выборкой, а также указываем параметр equal var (equal variance)
равняется True, потому что мы при генерации выборок задали одинаковые дисперсии.
Тест для первой и второй выборке
scipy.stats.ttest_ind(df.rvs1, df.rvs2, equal_var = True)
Ttest_indResult(statistic=-0.4344204949394242, pvalue=0.6640770241090136)Видим, что pvalue очень большое и мы не можем отвергнуть нулевую гипотезу о
равенстве средних.
Тест для первой и третье выборке
scipy.stats.ttest_ind(df.rvs1, df.rvs2, equal_var = True)
Ttest_indResult(statistic=-2.647855653309303, pvalue=0.008228159764104553)В этом случае pvalue=0.008, что меньше 0.05 и мы можем отвергнуть нулевую гипотезу
о равенстве средних, т.е. это говорит о том, что процессы, генерирующие выборки
статистически значимо отличаются.
Давайте интервально оценим разность средних по двум выборкам. Для этого
воспользуемся методом CompareMeans.
Для первой и второй выборке
cm = CompareMeans(DescrStatsW(df.rvs1), DescrStatsW(df.rvs2))
print("95%% confidence interval: [%f, %f]" % cm.tconfint_diff(usevar='unequal'))
95% confidence interval: [-2.271100, 0.195447]
Видим, что доверительный интервал содержит ноль, что также не дает отклонить
нулевую гипотезу о равенстве средних.
Для первой и третьей
cm = CompareMeans(DescrStatsW(df.rvs1), DescrStatsW(df.rvs3))
print("95%% confidence interval: [%f, %f]" % cm.tconfint_diff(usevar='unequal'))
95% confidence interval: [-5.806954, -3.345423]
Видим, что весь доверительный интервал находится левее нуля, а значит можно
говорить о том, что процесс, генерирующий третью выборку выдает "лучший" результат.
Теперь перейдем к некоторым "практическим" примерам. Надо сравнить продажи
определенного продукта при его размещении в проходе и специальной зонах магазина.
Пример взят из Levine D.M., Stephan D.F., Szabat K.A. Statistics for Managers Using
Microsoft Excel
df = pd.DataFrame({'Special Front': [224,189,248,285,273,190,243,215,280,317], 'In-Aisle': [192,236,164,154,189,220,261,186,219,202]}) df.head()
Special Front In-Aisle 0 224 192 1 189 236 2 248 164 3 285 154 4 273 189 Для начала посмотрим на данные, отобразим гистограммы распределения продаж.Видим, что гистограммы разные, видно, что, минимальное значение продаж в проходе меньше, чем в специальной зоне и максимальное значение больше в специальной,однако это все равно не дает нам возможности формально сказать, что продажив специальной зоне лучше.Проверим нормальность распределения с помощью графиков и тестовpylab.figure(figsize=(12,8))pylab.subplot(2,2,1)stats.probplot(df['Special Front'], dist="norm", plot=pylab)pylab.subplot(2,2,2)stats.probplot(df['In-Aisle'], dist="norm", plot=pylab)print("Shapiro-Wilk normality test, W-statistic: %f,p-value: %f" % stats.shapiro(df['Special Front']))Shapiro-Wilk normality test, W-statistic: 0.956779, p-value: 0.748618print("Shapiro-Wilk normality test, W-statistic: %f,p-value: %f" % stats.shapiro(df[''In-Aisle'']))Shapiro-Wilk normality test, W-statistic: 0.976863, p-value: 0.946202Подтвердили нормальность, переходим к тесту, в этом случае у нас нет информации
о равенстве дисперсий, поэтому ставим False.
scipy.stats.ttest_ind(df['Special Front'], df['In-Aisle'], equal_var = False)
Ttest_indResult(statistic=2.604123851375045, pvalue=0.018619333503526472)Как видим, pvalue <0.05 , поэтому мы отвергаем нулевую гипотезу о независимости
продаж от расположения товара.
Также выводим доверительный интервал для разности средних
cm = CompareMeans(DescrStatsW(df['Special Front']), DescrStatsW(df['In-Aisle'])) print("95%% confidence interval: [%f, %f]" % cm.tconfint_diff(usevar='unequal'))
95% confidence interval: [8.345485, 79.854515]Видим, что весь доверительный интервал находится правее нуля, а значит можно
говорить о том, что продажи при размещении в специальной области дали
статистически значимо лучший результат.
До сих пор мы рассматривали процедуры проверки гипотез на двух независимых
выборках. Далее разберем примеры с зависимыми выборками. Например показатели
пациентов до и после приема лекарства или оценка вкуса некоего продукта одними и
теми же дегустаторами. Рассмотрим пример из Levine D.M., Stephan D.F., Szabat K.A.
Statistics for Managers Using Microsoft Excel
Девять экспертов оценили два бренда кофе в эксперименте по тестированию вкуса.
Оценка по 7-ми бальной шкале (1 - чрезвычайно неприятный, 7 - чрезвычайно
приятный). Оценка дается по четырем характеристикам : вкус, аромат, яркость и
кислотность. В результате получается сумма баллов по каждому показателю.
df = pd.DataFrame({'EXPERT': ['C.C.','S.E.','E.G.','B.L.','C.M.','C.N.','G.N.','R.M.','P.V.'],
'A': [24,27,19,24,22,26,27,25,22],
'B': [26,27,22,27,25,27,26,27,23]})
df.head()
EXPERT A B 0 C.C. 24 26 1 S.E. 27 27 2 E.G. 19 22 3 B.L. 24 27 4 C.M. 22 25 Построим один интересный график, который достаточно убедительно продемонстрирует
отличие оценок у двух брендов. По осям X и Y отметим бренд А и В соответственно и
отметим точки, соответствующие оценке брендам. Также проведем диагональную
прямую и увидим, что практически все точки лежат выше этой прямой. Это дает
нам основания предполагать, что бренд В получил более высокую оценку.
df.plot.scatter('A', 'B', c = 'r', s = 30) pylab.grid() pylab.plot(range(100), c = 'black') pylab.xlim((15, 30)) pylab.ylim((15, 30)) pylab.show()
Но, конечно, мы не можем это оценивать просто по графику. Нам нужен некоторый более строгий критерий для проверки таких гипотез. Вот ровно этим критерием будет выступать критерий Стьюдента для
двух зависимых выборок.
Для того чтобы этот критерий использовать, нам также нужно
убедимся, что распределение попарных разностей существенно не отличается от
нормального. Давайте снова нарисуем Q-Q plot, сделаем это с помощью метода
probplot, и убедимся, что наши точки находятся очень близко к прямой, значит
распределение, скорее всего, похоже на нормальное.
stats.probplot(df.A - df.B, dist = "norm", plot = pylab)
Снова применим критерий Шапиро-Уилка, в данном случае нулевая гипотеза — попарные разности распределены нормально, альтернатива — это не так. Итак, применяем критерий с помощью метода stats.shapiro, передаем туда разности и смотрим на значения.
print("Shapiro-Wilk normality test, W-statistic: %f,
p-value: %f" % stats.shapiro(df.A - df.B))
Shapiro-Wilk normality test, W-statistic: 0.899034, p-value: 0.246453
Видим, что pvalue получилось большое, 0.25, а значит, нулевую гипотезу отвергать
нельзя, данные распределены нормально.
Поэтому можно смело применять критерий Стьюдента В данном случае наша нулеваягипотеза имеет следующий вид: средние значения оценок бренда А и В
одинаковы. Соответственно, альтернатива: средние значения оценок отличаются.
Воспользуемся реализацией из модуля stats библиотеки scipy, функцияназывается ttest rel, от слова relative (зависимые) и передаем внутрь функции данные,связанные с оценками бреда А и В. Видим, что значение pvalue=0.01. Это значит,что мы можем откинуть нулевую гипотезу, отвергнуть ее,и прийти к выводу, что все-таки оценки выставленные брендам кофе отличаются.scipy.stats.ttest_rel(df.A, df.B)
Ttest_relResult(statistic=-3.277152121491656, pvalue=0.011235500528711504)Оценим доверительный интервал разности, однако будем
помнить, что мы работаем со связанными выборками, поэтому будем использовать
соответствующую функциональность, и увидим, что весь доверительный интервал
находится левее нуля, что подтверждает более высокую оценку, данную бренду В
print("95%% confidence interval: [%f, %f]" % DescrStatsW(df.A - df.B).tconfint_mean())
95% confidence interval: [-2.650139, -0.460972]Рассмотрим еще один пример из области розничной торговли.
df = pd.DataFrame({'Day': 6*[1,2,3,4,5,6,7], 'Team': np.array(3*[7*['T1']+7*['T2']]).flatten(), 'Rev': [29294,34686,35905,35969,38054,68802,62642,34861,40014,37839,
41389,44721,81578,66641, 29917,32119,32260,32774,36296,69602,61306,34423,37622,36927,
40366,45718,80544,71445, 30789,35174,32902,33488,40181,64382,59993,34031,38332,39289,
39355,41641,81689,71165]})
df.head()
Day Team Rev 0 1 T1 29294 1 2 T1 34686 2 3 T1 35905 3 4 T1 35969 4 5 T1 38054
Данная таблица содержит информацию о выручке некоторого небольшого
магазина на протяжении нескольких недель, и предполагается, что там работают две
разные смены продавцов. В данном случае первый столбец таблицы показывает день
недели, от 1 до 7. Второй столбец — это команда, или та бригада продавцов,
которая работает в соответствующий день. Всего у нас две бригады, команда первая
и вторая соответственно. И доход, то есть выручка полученная в соответствующий день.
Будем решать задачу, влияет ли то, какая бригада продавцов работает, на выручку
данного магазина.
Соответственно нам нужно сравнить между собой две группы: выручка, которую
приносила первая бригада, и выручка, которую приносила вторая бригада. Для этого
данные должны быть разделены на две группы, которые нам необходимо сравнить.
То есть первый вопрос, на который мы для себя отвечаем: нам нужен критерий
сравнения двух групп. Далее, если эти выборки подчиняются нормальному распределению, в этом случае мы можем применять параметрический критерий сравнения выборок, а именно T-критерий Стьюдента. Если же выборки не подчиняются нормальному закону, тогда мы должны использовать непараметрические
критерии.
Будем последовательны, для начала выведем гистограмму
sns.displot(df, x="Rev", hue="Team")
Как видим, общее распределение выручки бимодальное, выручки в рабочие и
выходные дни отличается у обеих бригад. Также можно с большой уверенностью
предположить, что распределение не нормальное и выручки у второй бригады выше.
Выведем еще один интересный график
g = sns.pairplot(df, hue="Team",vars=["Day", "Rev"])
На этом графике хорошо видно, что распределение бимодальное, однако если его
разделить на рабочие и выходные дни, оно очень близко к нормальному. Также хорошо видно, что в одни и те же дни недели, выручка у второй бригады выше.
Приведем необходимые тесты проверки нормальности
По всей выборке
print("Shapiro-Wilk normality test, W-statistic: %f, p-value: %f" % stats.shapiro(df.Rev))
Shapiro-Wilk normality test, W-statistic: 0.803715, p-value: 0.000005Отдельно по рабочим и выходным дням
По рабочим
print("Shapiro-Wilk normality test, W-statistic: %f,
p-value: %f" % stats.shapiro(df.Rev[df.Day<6]))
Shapiro-Wilk normality test, W-statistic: 0.982633, p-value: 0.890399По выходным
print("Shapiro-Wilk normality test, W-statistic: %f,
p-value: %f" % stats.shapiro(df.Rev[df.Day>5]))
Shapiro-Wilk normality test, W-statistic: 0.901293, p-value: 0.164834Нормальность отдельно по рабочим и выходным дням подтверждена
Таким образом у нас есть два варианта решения задачи : либо разбить выборки на
рабочие и выходные дни и по ним отдельно провести параметрические тесты, либо
провести непараметрический тест, который не требует нормальности,
по всей выборке.
Второй вариант рассмотрим позже, а сейчас сделаем сравнение разделив выборки.
Нормальность была подтверждена, проведем тесты и построим доверительные
интервалы для разности средних выручек первой и второй бригады.
Для рабочих дней :
Тест
scipy.stats.ttest_ind(df.Rev[(df.Team=='T1') & (df.Day<6)],
df.Rev[(df.Team=='T2') & (df.Day<6)], equal_var = False)
Ttest_indResult(statistic=-4.33611477628601, pvalue=0.00017431037992407167)
Интервал
cm = CompareMeans(DescrStatsW(df.Rev[(df.Team=='T1') & (df.Day<6)]),
DescrStatsW(df.Rev[(df.Team=='T2') & (df.Day<6)]))
print("95%% confidence interval: [%f, %f]" % cm.tconfint_diff(usevar='unequal'))
95% confidence interval: [-7532.550086, -2696.783247]Для выходных дней
scipy.stats.ttest_ind(df.Rev[(df.Team=='T1') & (df.Day>5)],
df.Rev[(df.Team=='T2') & (df.Day>5)], equal_var = False)
Ttest_indResult(statistic=15.08949565888958, pvalue=1.49250470421673e-06)cm = CompareMeans(DescrStatsW(df.Rev[(df.Team=='T1') & (df.Day>5)]),
DescrStatsW(df.Rev[(df.Team=='T2') & (df.Day>5)]))
print("95%% confidence interval: [%f, %f]" % cm.tconfint_diff(usevar='unequal'))
95% confidence interval: [-18226.664883, -3885.001784]Как видим, в обоих случаях весь доверительный интервал находится слева, таким
образом превосходство в выручке второй бригаде статистически значимо доказана.
Для руководство к этому можно добавить графики, демонстрирующие это более
наглядно.
sns.boxplot(x="Team", y="Rev", data=df[df.Day<6],) plt.title('Weekdays')
sns.boxplot(x="Team", y="Rev", data=df[df.Day>5]) plt.title('Weekend')
Комментариев нет:
Отправить комментарий