Рассмотрим процедуру проверки гипотез о равенстве средних на основании двух выборок. Задача состоит в том, чтобы проверить равны ли средние значения этих выборок. Напомним, что среднее значение является очень важной величиной, характеризующей выборку и процесс, ее генерирующий. Поэтому задача сравнения средних очень часто возникает в самых разных областях.
Для начала сгенерируем выборки с известными параметрами и на их примере пройдем все этапы решения задачи.
'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')
Комментариев нет:
Отправить комментарий