воскресенье, 6 февраля 2022 г.

Статистика с Python в маркетинге : двухвыборочные критерии Стьюдента о равенстве средних

 Рассмотрим процедуру проверки гипотез о равенстве средних на основании двух выборок. Задача состоит в том, чтобы проверить равны ли средние значения этих выборок. Напомним, что среднее значение является очень важной величиной, характеризующей выборку и процесс, ее генерирующий. Поэтому задача сравнения средних очень часто возникает в самых разных областях.


Для начала сгенерируем выборки с известными параметрами и на их примере пройдем все этапы решения задачи.

df = pd.DataFrame({'rvs1': stats.norm.rvs(loc=5, scale=10, size=500),
                   'rvs2': stats.norm.rvs(loc=5, scale=10, size=500),
                   'rvs3': stats.norm.rvs(loc=8, scale=10, size=500)})
df.head()


rvs1rvs2rvs3
014.48796423.36396718.002069
1-0.218182-1.49242817.301648
216.216604-11.3246978.825380
3-5.00041115.58942211.170270
4-5.78629813.07436213.508006

Выведем гистограмму

df.plot.hist()


Как мы и ожидали, третья выборка смещена вправо, т.к. ее среднее значение больше чем у первой и второй.
Еще один способ продемонстрировать различия - диаграмма boxplot.

df.boxplot(return_type='axes')


Для того чтобы использовать двухвыборочный критерий Стьюдента, сначала нужно убедиться, что распределения выборок существенно не отличаются от нормального. Для этого давайте построим Q-Q plot для каждого из выборок.

pylab.figure(figsize=(20,8))
pylab.subplot(2,3,1)
stats.probplot(df.rvs1, dist="norm", plot=pylab)
pylab.subplot(2,3,2)
stats.probplot(df.rvs2, dist="norm", plot=pylab)
pylab.subplot(2,3,3)
stats.probplot(df.rvs3, dist="norm", plot=pylab)



Видим, что  наши точки не сильно отличаются от прямой, можно сказать точно лежат
на ней. Это подтверждает, что данные распределены с некоторым распределением, которое сильно от нормального не отличается.

Однако для того чтобы проверить это более строго, воспользуемся критерием
 Шапиро-Уилка. В данном случае нулевая гипотеза будет соответствовать тому, что наши выборки  распределены нормально, соответственно, альтернатива — распределена по-другому, не нормально. Видим, что pvalue получается большими, намного больше 0,05. Поэтому мы не можем отвергнуть нулевую гипотезу о нормальности выборок.

print("Shapiro-Wilk normality test, W-statistic: %f, p-value: %f" % stats.shapiro(df.rvs1))
print("Shapiro-Wilk normality test, W-statistic: %f, p-value: %f" % stats.shapiro(df.rvs2))
print("Shapiro-Wilk normality test, W-statistic: %f, p-value: %f" % stats.shapiro(df.rvs3))

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 FrontIn-Aisle
0224192
1189236
2248164
3285154
4273189
Для начала посмотрим на данные, отобразим гистограммы распределения продаж. 
Видим, что  гистограммы разные, видно, что, минимальное значение продаж в проходе меньше, чем в специальной зоне и  максимальное значение больше в  специальной,
 однако это все равно не дает нам возможности формально сказать, что продажи 
в специальной зоне лучше.

Также выведем диаграмму boxplot.

df.boxplot(return_type='axes')



Проверим нормальность распределения с помощью графиков и тестов

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.748618

print("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()


EXPERTAB
0C.C.2426
1S.E.2727
2E.G.1922
3B.L.2427
4C.M.2225


Построим один интересный график, который достаточно убедительно продемонстрирует

отличие оценок у двух брендов.  По осям 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()

DayTeamRev
01T129294
12T134686
23T135905
34T135969
45T138054


Данная таблица содержит информацию о выручке некоторого небольшого
 магазина на протяжении нескольких недель, и предполагается, что там работают две
 разные смены продавцов. В данном случае первый столбец таблицы показывает день
 недели, от 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')


















































Комментариев нет:

Отправить комментарий