import collections
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import scipy.stats as stats
from tqdm.auto import tqdm
x = [4,5,6,6,5,6,3,3,6,4,5,3,1,4,5,1,4,3]
c = collections.Counter()
c.update(x)
for i in range(1, 7):
print(c[i])
stats.chisquare([2, 0, 4, 4, 4, 4])
Задание 1
Проанализировать набор данных и ответить на следующие вопросы:
1) Какой % наблюдений выходит за 5 и 95 перцентиль?
2) Какому закону распределения отвечают данные? На основе чего вы так решили?
Описание данных:
Вместе с ответом на задание приложить код
df = pd.read_csv('task_1.csv', sep=';')['metrics']
l, r = np.percentile(df, [5, 95])
print("Left: ", l, "right: ", r)
print("Выходит за границы {:.4f}% выборки".format(100 * ((df < l) | (df > r)).mean()))
def dist_to_buckets(data):
cnt = collections.Counter()
cnt.update(data)
res = []
for i in range(max(data) + 1):
res.append(cnt[i])
return np.array(res)
def sqweeze_buckets(*data):
arrays = [[] for _ in range(len(data))]
is_start = False
for row in zip(*data):
if all(row):
is_start= True
for i, item in enumerate(row):
arrays[i].append(item)
elif is_start:
break
arrays = [np.array(it) for it in arrays]
return tuple(arrays)
p = 0.1
b1 = dist_to_buckets(geom.rvs(p, size=100000))
b2 = dist_to_buckets(geom.rvs(p, size=100000))
len(b1), len(b2)
b1, b2 = sqweeze_buckets(b1, b2)
len(b1), len(b2)
dist = stats.expon
b1 = pd.Series(dist.rvs(p, size=1000), name='left').value_counts()
b2 = pd.Series(dist.rvs(p, size=1000), name='right').value_counts()
# common = pd.merge(b1,b2, left_index=True, right_index=True)
# b1 = common['left']
# b2 = common['right']
chisquare(b1, b2)
pd.Series(dist.rvs(p, size=1000), name='left').value_counts()
clean = df.copy()
l, r = np.percentile(df, [5, 99.9])
# clean = df[~((df < l) | (df > r))].copy()
# clean -= clean.min()
p = 0.3/clean.mean()
# p = (-1 + np.sqrt(4*clean.var() + 1))/(2*clean.var())
gen_data = geom.rvs(p, size=len(clean))
# d = (m - 2*v)**2 - 4*v*(v-m)
# d = d**0.5
# p = (2*v-m-d)/(2*v)
# r = m*(1-p)/p
# p, r
# gen_data = nbinom.rvs(p, r, size=len(clean))
# gen_data = poisson.rvs(p, size=len(clean))
assert len(gen_data) == len(clean)
bins = np.arange(1, max(max(clean), max(gen_data)))
plt.figure(figsize=(14, 7))
plt.yscale('log')
plt.hist(clean, bins=bins, alpha=0.5, label='data')
plt.hist(gen_data, bins=bins, alpha=0.5, label='gen')
plt.legend()
cnt_exp = collections.Counter()
cnt_exp.update(clean)
cnt_gen = collections.Counter()
cnt_gen.update(gen_data)
cols = [(cnt_exp[col],cnt_gen[col]) for col in bins if cnt_exp[col]]
exp_data = [col[0] for col in cols]
gen_data = [col[1] for col in cols]
chisquare(exp_data, f_exp=gen_data)
d = (m - 2*v)**2 - 4*v*(v-m)
d = d**0.5
p = (2*v-m-d)/(2*v)
r = m*(1-p)/p
p, r
Распределение похоже на экспоненциальное или смесь геометрических
Задание 2
Определить, можно сравнить две представленные выборки t критерием Стьюдента?
Если нельзя сравнивать t-критерием, то что будем использовать и почему?
Ответ аргументировать и описать
Описание данных:
Вместе с ответом на задание приложить код
df = pd.read_csv('task_2.csv', sep=';')[['variant', 'metrics']]
bins = np.arange(*np.percentile(df['metrics'], [0, 95]))
plt.figure(figsize=(14, 7))
# plt.yscale('log')
plt.hist(df[df['variant'] == 'A']['metrics'].values, bins, alpha=0.5, label='A')
plt.hist(df[df['variant'] == 'B']['metrics'].values, bins, alpha=0.5, label='B')
plt.legend();
Как можно заметить, распределение явно не нормальное, поэтому t критерий использовать нельзя.
Будем использовать бутстрап и проверять $H_0$ гипотезу о том, что выборки A и B из одного распределения.
def calc_stats(df, key_a='A', key_b='B'):
_ = df.groupby('variant')['metrics'].agg(['mean', 'median']).T
return (_[key_a] - _[key_b]).abs()
calc_stats(df)
def bootstrap_p_value(df, it=1000, key_a='A', key_b='B'):
results = []
orig = calc_stats(df, key_a, key_b)['mean']
for i in tqdm(range(it)):
df2 = df.copy()
df2['metrics'] = df['metrics'].sample(frac=1, replace=True).values
st = calc_stats(df2, key_a, key_b)
results.append(st['mean'] >= orig)
return np.mean(results)
print(f"Выборки A и B одинаковые с вероятностью {100 * bootstrap_p_value(df, 1000)}%")
Выборки одинаковые с вероятностью 2.7%, поэтому выборки отличаются с вероятностью 97.3%
Задание 3
Дано две выборки по А/Б тесту.
Две выборки несбалансированные - кол-во пользователей в каждой группе разные.
Метрика, которую меряем в эксперименте (и которую используем для определения группы, которая лучше себя показала) - кол-во просмотренных страниц на пользователя
Как вы будете принимать решение, какая группа лучше А или Б, и какие статистические методы (критерии) вы будете использовать, какие преобразования к данным применять? P.S. Вам может помочь лекция Виталия Черемисинова, где он рассказывал про А/Б тесты
Описание данных:
Вместе с ответом на задание приложить код
df = pd.read_csv('task_3.csv', sep=';')[['variant', 'metrics']]
df.groupby('variant')['metrics'].agg(['mean', 'median', 'count'])
Попробуем генерировать бутстреп выборки от наших выборок и посмотреть на доверительные интервалы для какой-то метрики.
bins = np.arange(*np.percentile(df['metrics'], [0, 95]))
plt.figure(figsize=(14, 7))
plt.yscale('log')
plt.hist(df[df['variant'] == 'A']['metrics'].values, bins, alpha=0.5, label='A')
plt.hist(df[df['variant'] == 'D']['metrics'].values, bins, alpha=0.5, label='D')
plt.legend();
print(f"Выборки A и B одинаковые с вероятностью {100 * bootstrap_p_value(df, 1000, 'A', 'D')}%")
Попробуем получить интервальные оценки с помощью bootstrap
def bootstrap_data(df):
return df.sample(frac=1, replace=True).values
def get_intervals(data, alpha):
return np.percentile(data, [100 * alpha/2, 100 * (1-alpha/2)])
for let in ['A', 'D']:
res = []
for i in tqdm(range(1000)):
res.append(bootstrap_data(df[df['variant'] == let]['metrics']).mean())
print(f"Доверительные интервалы для выборки {let} = {get_intervals(np.array(res), 0.01)}")
for let in ['A', 'D']:
res = []
for i in tqdm(range(1000)):
res.append(bootstrap_data(df[df['variant'] == let]['metrics']).mean())
print(f"Доверительные интервалы для выборки {let} = {get_intervals(np.array(res), 0.5)}")
С более грубой оценкой получаем, что доверительный интервал для выборки A больше чем для D, поэтому выберем A
Задание 4
Есть данные с большой дисперсии у метрики (диспресию метрики можно посчитать для каждой группы). Для их дальнейшего анализа дисперсию необходимо сократить. Как определить, что дисперсия действительно большая? Если она большая, как вы будете ее чистить? Ответ аргументировать и описать. P.S. Вам может помочь лекция Виталия Черемисинова, где он рассказывал про А/Б тесты
Описание данных:
Вместе с ответом на задание приложить код
df = pd.read_csv('task_4.csv', sep=';')['metrics']
plt.figure(figsize=(14, 7))
plt.hist(df, bins=100, label='metrics')
plt.yscale('log')
plt.legend();
df.var()
У выборки большой хвост, распределение похоже на экспоненциальное. Можно отбросить выбросы, произвести логарифмирование данных. Оценим дисперсию с помощью bootstrap
def bootstrap_var(df, count, col='backet', col_stat='metrics'):
res = []
for i in tqdm(range(count)):
res.append(df.sample(frac=1, replace=True).var())
return np.array(res)
l, r = np.percentile(df, [1, 99])
clean = df[~((df < l) | (df > r))].copy()
plt.figure(figsize=(14, 7))
plt.hist(backet_bootstrap(df, 1000), bins=100, label='var')
plt.legend();
plt.figure(figsize=(14, 7))
plt.hist(backet_bootstrap(clean, 1000), bins=100, label='var')
plt.legend();
plt.figure(figsize=(14, 7))
plt.hist(backet_bootstrap(np.log(clean), 1000), bins=100, label='var')
plt.legend();
Как можно заметить, выборочная дисперсия сильно уменьшилась после логарифмирования данных
Задание 5
Есть эксперимент с поисковыми подсказками. Вы хотим узнать, как изменился CTR в каждой группе эксперимента.
Данные
Задача
Данные
df = pd.read_csv('task_5.csv', sep=',')[['variant', 'view', 'action']]
df['ctr'] = df['action'] / df['view']
df.head()
df.groupby('variant')[['view', 'action', 'ctr']].agg(['sum', 'count', 'mean'])
bins = np.linspace(0, 1, 100)
plt.figure(figsize=(14, 7))
plt.yscale('log')
plt.hist(df[df['variant'] == 'A']['ctr'].values, bins, alpha=0.5, label='A')
plt.hist(df[df['variant'] == 'B']['ctr'].values, bins, alpha=0.5, label='B')
plt.legend();
np.random.seed(0)
for let in ['A', 'B']:
res = []
for i in tqdm(range(1000)):
res.append(bootstrap_data(df[df['variant'] == let]['ctr']).mean())
print(f"Доверительные интервалы для выборки {let} = {get_intervals(np.array(res), 0.01)}")
la, ra = [0.7497863, 0.77180423]
lb, rb = [0.74442776, 0.76976849]
np.random.seed(0)
res = []
for i in tqdm(range(1000)):
b = bootstrap_data(df[df['variant'] == 'A']['ctr']).mean()
res.append((la <= b <= ra) & (b > rb))
print(f"p_value для выборки A = {np.array(res).mean()}")
res = []
for i in tqdm(range(1000)):
b = bootstrap_data(df[df['variant'] == 'B']['ctr']).mean()
res.append((lb <= b <= rb) & (b < la))
print(f"p_value для выборки B = {np.array(res).mean()}")
p value для выборки A меньше, также выборка A имеет большее ожидание значения ctr, поэтому стоит выбрать ее.