Статьи

Определение размера образца для вашего сплит теста

Почему размеры образцов?

Когда речь идет о дизайне эксперимента, наиболее спорный вопрос , без сомнения , является то , что размер выборки. Чаще всего «Почему размер выборки такой большой?» Хорошей новостью является то, что размер выборки не должен быть таким большим, если вы готовы пойти на компромисс другими способами. В этом посте я собираюсь вникнуть в то, как оценить размер выборки для логарифмически распределенного набора данных. Затем я сосредоточусь на том, как изменение пары параметров может значительно уменьшить размеры выборки, которые нам нужны. Наконец, я покажу, как использование другого статистического распределения (для биномиального распределения) может изменить анализ.

Как мы собираемся это сделать?

Самый простой способ сделать этот анализ (если вы умеете программировать) — это:

  1. Смоделируйте переменную, которую мы будем тестировать
  2. Имитация изменения
  3. Сравните измененный дистрибутив с оригиналом много-много тысяч раз

Важно: я иду об этом в некотором роде. Я собираюсь сказать, какой эффект я хочу измерить и насколько уверенным я хочу быть, а затем я вернусь к размеру выборки, попробовав сотни тысяч тестов. Затем я ищу минимальный размер выборки, который будет соответствовать установленным мною ограничениям.

Вот более конкретные шаги:

  1. Посмотрите на реальные данные. Получите реальные данные и посмотрите, какое распределение они формируют.
  2. Создайте математическую модель данных, которую вы можете использовать для имитации смен.
  3. Запустите тысячи смоделированных статистических тестов между смоделированным управлением и смоделированным вариантом, где мы имеем известное улучшение. Посчитайте, сколько раз мы можем обнаружить данный сдвиг.
  4. Количество раз, когда мы обнаруживаем сдвиг, деленный на количество смоделированных тестов с разделением, — это вероятность обнаружить эффект данной величины с данным размером выборки. Это также известно как статистическая сила.
  5. Запустите другие симуляции, изменяющие размер выборки и изменение между имитированным контролем и симулированным вариантом.

Притворяться нормальным бревном

На нашем пути к анализу размеров выборки на предмет того, сколько тратят клиенты, нам необходимо выяснить, как сначала смоделировать свои покупки. Мой первый шаг — всегда смотреть на данные, которые я хочу смоделировать. Поскольку большинство моих примеров нормальных данных журнала получены с работы, нам придется использовать сгенерированный набор данных и делать вид, что это естественно.

В работе [2]:

%load_ext rmagic

В работе [60]:

%%R
generated_sales_data <- round(rlnorm(10000, mean=3.21, sd=.6), 2)
write.table(generated_sales_data, "raw_data/sales_data.csv", col.names=FALSE, row.names=FALSE, sep=',', quote=FALSE)

Теперь, когда у нас есть «реальные» данные о продажах, давайте забудем, что мы их сделали вообще, и поработаем в обратном направлении, чтобы посмотреть, как мы можем провести обратный инжиниринг параметров, которые я использовал для их составления. Сначала давайте рассмотрим наш дистрибутив и посмотрим, какой у нас дистрибутив.

В работе [63]:

%%R 
library('ggplot2')

sales <- read.csv('~/Documents/Notebooks/raw_data/sales_data.csv', header=F, col.names=c('purchase_amount'))

p <- ggplot(sales, aes(x=purchase_amount)) + 
    geom_histogram(binwidth=.5) + 
    xlim(0, 150) + 
    xlab("Customer amount spent (in $)") +
    ylab("# of customers") + 
    ggtitle("Amounts spent by individual consumers") + 
    theme(axis.title.y = element_text(angle = 0))
print(p)

Логнормальное распределение

Это выглядит как лог-нормальное распределение. Мы можем смоделировать случайное распределение, которое выглядит следующим образом, вычислив пару значений из приведенных выше данных. Нам нужно найти среднее значение логарифма каждой цены, а также стандартное отклонение логарифма каждой цены. Вот как это получается:

В работе [70]:

%%R 
library('ggplot2')

df <- read.csv('~/Documents/Notebooks/raw_data/sales_data.csv', header=T, col.names=c('amount_spent'))
purchase.count <- length(df$amount_spent)
purchase.log_mean <- mean(log(df$amount_spent))
purchase.log_stdev <- sd(log(df$amount_spent))

print(paste(c("Standard mean of amount spent:", round(mean(df$amount_spent),2)), sep=''))
print(paste(c("Standard deviation of amount spent:", round(sd(df$amount_spent),2)), sep=''))
print(paste(c("Log mean of amount spent:", purchase.log_mean), sep=''))
print(paste(c("Log standard deviation of amount spent:", purchase.log_stdev), sep=''))
[1] "Standard mean of amount spent:" "29.66"                         
[1] "Standard deviation of amount spent:" "19.48"                              
[1] "Log mean of amount spent:" "3.20924196153511"         
[1] "Log standard deviation of amount spent:"
[2] "0.601137563076673"       

Обратите внимание, насколько средние и среднеквадратичные отклонения отличаются от их типичных встречных частей. Когда я впервые научился это делать, я всегда надеялся, что смогу просто использовать стандартное среднее значение и стандартное отклонение, но они даже близко не подходят. Так много для того, чтобы быть ленивым! 😉

Теперь, когда у нас есть эти два параметра, мы сможем создать довольно хорошую модель наших данных.

В работе [71]:

%%R
# Create modeled data
simulated_purchases <- data.frame(amount_spent=rlnorm(purchase.count, mean=purchase.log_mean, sd=purchase.log_stdev))

# Graph it
p <- ggplot(simulated_purchases, aes(x=amount_spent)) + 
    geom_histogram(binwidth=0.5) + 
    xlim(0, 150) + 
    xlab("price") +
    ylab("# of orders") + 
    ggtitle("Simulated price frequency from one day") + 
    theme(axis.title.y = element_text(angle = 0))
print(p)

Логнормальное распределение

Глядя на реальное и смоделированное распределение того, сколько клиентов потратили, мы видим, что они довольно похожи. Еще одно небольшое отличие, которое вы можете заметить в дикой природе, заключается в том, что ваша смоделированная гистограмма не будет иметь некоторых спорадических резких всплесков, которые имеют ваши реальные данные. Ничего страшного, но просто указать на это, если вы когда-нибудь задумывались об этом. Ну что теперь?

Выбор размера эффекта для проверки

Что-то, что может существенно повлиять на размер нашей выборки, это размер эффекта, который мы решили проверить. Это то, что очень открыто для изменения, чтобы уменьшить риск. Как мы увидим в следующих примерах, большие сдвиги в наших данных требуют значительно меньшего количества выборок. Поскольку большинство тестируемых нами функций имеют низкую вероятность кардинального улучшения результатов, мы склонны придерживаться порядка 2% сдвига в наших числах.

Это основное место, где мы можем настроить наши тесты. Самое сложное в том, как мы узнаем, какой эффект нужно измерить? Мы хотели бы проверить как можно меньший эффект, верно? Конечно. В какой-то момент все заканчивается тем, что мы просто проводим тестирование для всех наших клиентов или что тестирование длится вечно. Вам нужно выбрать номер, который позволит вашей команде выполнить большинство тестов за неделю (если вы можете), и который уравновешивает риск негативного влияния на клиентов и риск ошибочно думать, что тестируемая функция неэффективна.

Позвольте мне повторить это, потому что это жизненно важно: мы должны сбалансировать вероятность того, что наш тест не будет достаточно чувствительным, с возможностью негативного влияния на наших клиентов. У нас есть возможность работать с этим, но это требует открытого диалога с конкретными ограничениями между заинтересованными сторонами.

Моделирование сплит-теста в поисках эффекта 2%

Давайте предположим, что мы решили протестировать эффект не менее 2%. Это означает, что мы хотим обнаружить, что измеренное среднее значение увеличилось на 2%.

Если вы помните, выше мы показали, что мы можем смоделировать переменную, которую мы хотим протестировать (суммы покупки), как логарифмически нормальное распределение. Давайте попробуем смоделировать небольшое улучшение нашего варианта и посмотрим, что произойдет, когда мы сравним их. Вместо того, чтобы заново извлекать константы, которые мы использовали прежде, я просто буду использовать значения среднего значения и стандартного отклонения, которые мы уже рассчитали. Во-первых, давайте попробуем сравнить два разных случайных распределения, которые должны быть эквивалентными (другими словами, нет статистически значимой разницы в средних).

В работе [72]:

%%R 
number_of_samples <- 1000
control <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)
equivalent_variation <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)

results <- t.test(equivalent_variation, control, alternative='greater')
print(results)
Welch Two Sample t-test

data:  equivalent_variation and control
t = -0.2264, df = 1997.376, p-value = 0.5895
alternative hypothesis: true difference in means is greater than 0
95 percent confidence interval:
-1.560934       Inf
sample estimates:
mean of x mean of y 
28.92833  29.11709  

Так здорово! У нас есть результаты статистических тестов, но что они значат? Найдите в выходных данных значение p. Давайте примем стандартное определение, что значение р менее 0,05 является статистически значимым. В этом случае наше p-значение> 0,05, поэтому мы заключаем, что нет никаких доказательств того, что среднее значение нашего отклонения больше, чем наш контроль.

Отлично. Это соответствует нашим ожиданиям.

Давайте пойдем немного дальше. Давайте запустим этот тест пару тысяч раз и посмотрим, как часто мы получаем результат, который соответствует нашим ожиданиям. Мы должны увидеть статистически значимую разницу только примерно в 5% случаев. Выполнение этой симуляции занимает менее секунды.

В работе [73]:

%%R
number_of_samples <- 1000

simulation_results <- mapply(function(x){
    control <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)
    equivalent_variation <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)
    results <- t.test(equivalent_variation, control, alternative='greater')
    .05 >= results[3]$p.value
}, seq(0,2000, by=1))

percent_of_time_equivalent <- length(simulation_results[simulation_results==TRUE]) / length(simulation_results)
print(paste(c("Percentage of time effect detected: ", round(percent_of_time_equivalent*100, digits=2), "%"), collapse=''))

[1] «Процент обнаруженного эффекта времени: 5,55%»

Почти в 5% случаев результаты показывают статистически значимое различие! Отлично. Это подтверждает, что t-тест пока работает очень хорошо для наших нужд.

Это соответствует теории статистического тестирования и принципам работы t.test.

У нас есть две концепции, которые могут показаться ОЧЕНЬ похожими, поэтому позвольте мне немного подробнее остановиться здесь:

  • Статистическая значимость — 95% значимости означает, что в 95% случаев эффект, который мы измеряем, НЕ будет обусловлен случайной случайностью.
  • Статистическая мощность. Процент времени, в течение которого эффект определенного размера будет обнаружен. Обратитесь сюда для получения дополнительной информации: http://en.wikipedia.org/wiki/Statistical_power
  • Размер эффекта — величина изменения, которое будет измерено. Также называется чувствительностью.

Когда вы слышите, как статистики обсуждают 95% значимость, они на самом деле говорят, что, если они провели эксперимент 100 раз, они ожидают, что один раз из двадцати они ошибочно обнаружат эффект, который происходит просто по случайной случайности. Важность этого заключается в том, что мы никогда не достигнем точки, в которой мы на 100% уверены. Мы можем приблизиться к этому, но тогда это в основном равносильно измерению влияния на все наше население клиентов. Может быть, просто развернуть функцию в этой точке. Здесь есть компромисс, так что это еще одно место, где мы можем идти на компромиссы.

Если мы говорим, что хотим измерить величину эффекта 2% при статистической мощности 95%, это означает, что мы хотим иметь возможность обнаруживать эффект по крайней мере на 2% по крайней мере в 95% случаев. Может быть, у нас все в порядке, если выбрасывать положительный эффект вдвое чаще (90% уверены). Это абсолютно правильное решение, если вы понимаете, как это влияет на наше тестирование.

Далее нам нужно посмотреть, в какой процент времени мы обнаруживаем улучшение, если мы сдвигаем вариацию, чтобы иметь небольшую разницу. Давайте сделаем тот же тест, что и выше, но давайте сместим наш вариант на 2%. Давайте назовем наш выстрел и предскажем, что должно произойти. Я не знаю точно, чего ожидать, но я знаю, что, поскольку сейчас есть изменение, мы должны обнаруживать изменение чаще, чем в 5% случаев.

В работе [74]:

%%R
number_of_samples <- 1000
effect_size <- 1.02

simulation_results <- mapply(function(x){
    control <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)
    improved_variation <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)*effect_size
    results <- t.test(improved_variation, control, alternative='greater')
    .05 >= results[3]$p.value
}, seq(0,2000, by=1))

percent_of_time_equivalent <- length(simulation_results[simulation_results==TRUE]) / length(simulation_results)
print(paste(c("Percentage of time effect detected: ", round(percent_of_time_equivalent*100, digits=2), "%"), collapse=''))

[1] «Процент обнаруженного эффекта времени: 17,79%»

Большое изменение! Итак, теперь мы видим, что эти два распределения показывают статистически значимое различие ~ 16% времени! Это означает, что если бы мы были в порядке, обнаружив только улучшение наших тестовых функций ~ 16% времени, нам потребовалось бы только 1000 образцов. Для отрицательного потенциального опыта клиента это довольно неблагоприятный риск Однако с точки зрения экспериментов это довольно ужасно. Наши тесты вряд ли будут повторяемыми. Когда мы впервые начали тестирование, мы фактически начали здесь. Было так странно получать разные результаты при каждом тесте.

В реальном сплит-тесте, когда есть изменение, мы в идеале хотим иметь возможность измерять изменение как минимум в 95% случаев. Опять же, это то, что мы можем исследовать смещение, но давайте сейчас примем это как данность. Давайте попробуем увеличить наш размер выборки и посмотрим, куда он нас приведет.

В работе [75]:

%%R
number_of_samples <- 10000
effect_size <- 1.02

simulation_results <- mapply(function(x){
    control <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)
    improved_variation <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)*effect_size
    results <- t.test(improved_variation, control, alternative='greater')
    .05 >= results[3]$p.value
}, seq(0,2000, by=1))

percent_of_time_equivalent <- length(simulation_results[simulation_results==TRUE]) / length(simulation_results)
print(paste(c("Percentage of time effect detected: ", round(percent_of_time_equivalent*100, digits=2), "%"), collapse=''))

[1] «Процентная доля обнаруженного эффекта времени: 68,72%»

Я сделал удар в темноте и попробовал 10х образцов. Это приблизило нас к обнаружению изменений в 95% случаев, но мы еще не совсем там. Обратите внимание, насколько нематематичен этот подход? Это основной способ использования симуляции для выполнения сложного анализа. Вместо того, чтобы искать формулу или инструмент в Интернете и просто доверять им, мы можем подкрепить решение проверяемым способом.

Мы находимся на ~ 67% с 10 000 образцов. Давайте утроим размер выборки и посмотрим, поможет ли это нам обнаружить эффект 2% хотя бы в 95% случаев.

В работе [76]:

%%R
number_of_samples <- 30000
effect_size <- 1.02

simulation_results <- mapply(function(x){
    control <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)
    improved_variation <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)*effect_size
    results <- t.test(improved_variation, control, alternative='greater')
    .05 >= results[3]$p.value
}, seq(0,2000, by=1))

percent_of_time_equivalent <- length(simulation_results[simulation_results==TRUE]) / length(simulation_results)
print(paste(c("Percentage of time effect detected: ", round(percent_of_time_equivalent*100, digits=2), "%"), collapse=''))

[1] «Процентная доля обнаруженного эффекта времени: 97,55%»

Отлично! 98%. Мы немного переиграли нашу цель, так что теперь я в порядке. Опять же, это несовершенный процесс.

Позже я объясню, чем именно статистический тест коэффициента конверсии отличается от суммы покупки.

Давайте продолжим наш процесс угадывания и проверки. 98% больше уверенности, чем мы хотим для этого теста. Давайте посмотрим, сможем ли мы уменьшить наш размер выборки (и риск!) Еще немного, и все еще оставаться на уровне ~ 95%.

В работе [78]:

%%R
number_of_samples <- 24000
effect_size <- 1.02

simulation_results <- mapply(function(x){
    control <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)
    improved_variation <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)*effect_size
    results <- t.test(improved_variation, control, alternative='greater')
    .05 >= results[3]$p.value
}, seq(0,2000, by=1))

percent_of_time_equivalent <- length(simulation_results[simulation_results==TRUE]) / length(simulation_results)
print(paste(c("Percentage of time effect detected: ", round(percent_of_time_equivalent*100, digits=2), "%"), collapse=''))

[1] «Процент обнаруженного временного эффекта: 95,55%»

Изменение размера выборки до 24 000 отразило нас примерно на 95%. Вуаля! Мы нашли ответ на наш вопрос:

Чтобы измерить как минимум 2% -ное влияние на сумму покупок в 95% случаев, нам нужен размер выборки около 24 000 покупок на вариант.

Теперь, очевидно, если бы мне пришлось проходить этот процесс для каждого теста, который мы сделали, у меня было бы МНОГО работы. Вместо этого я просто создал ОГРОМНЫЕ таблицы, в которых предварительно вычислил некоторые общие значения, а затем отфильтровал электронные таблицы в Excel на основе ограничений, которые у меня есть для данного теста.

Изменение ограничений для меньшего размера выборки

Ах, вот где это становится действительно интересным! Что если мы решим, что не хотим затрагивать более 7000 клиентов (или произвольное число)? Можем ли мы по-прежнему использовать эту технику? Абсолютно. Давайте пройдемся по этому, используя те же суммы покупки, которые мы уже смоделировали, и посмотрим, как все изменится. Давайте сформулируем наш вопрос более формально:

Чтобы ограничить наш тест до 7000 клиентов на вариант, каков максимальный размер эффекта и уровень достоверности, при котором мы можем проверить суммы покупки? Давайте начнем с дикой догадки и посмотрим, какую уверенность мы получаем при измерении 2% -ного сдвига в клиенте:

В работе [79]:

%%R
number_of_samples <- 7000
effect_size <- 1.02

simulation_results <- mapply(function(x){
    control <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)
    improved_variation <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)*effect_size
    results <- t.test(improved_variation, control, alternative='greater')
    .05 >= results[3]$p.value
}, seq(0,2000, by=1))

percent_of_time_equivalent <- length(simulation_results[simulation_results==TRUE]) / length(simulation_results)
print(paste(c("Percentage of time effect detected: ", round(percent_of_time_equivalent*100, digits=2), "%"), collapse=''))

[1] «Процент обнаруженного эффекта времени: 54,22%»

Мы можем измерить истинный сдвиг в 2% ~ 54-57% времени! Вроде ужасно. Итак, давайте попробуем изменить вещи. Что если мы скажем, что у нас все в порядке с обнаружением изменений 3% и более?

В работе [80]:

%%R
number_of_samples <- 7000
effect_size <- 1.03

simulation_results <- mapply(function(x){
    control <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)
    improved_variation <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)*effect_size
    results <- t.test(improved_variation, control, alternative='greater')
    .05 >= results[3]$p.value
}, seq(0,2000, by=1))

percent_of_time_equivalent <- length(simulation_results[simulation_results==TRUE]) / length(simulation_results)
print(paste(c("Percentage of time effect detected: ", round(percent_of_time_equivalent*100, digits=2), "%"), collapse=''))

[1] «Процент обнаруженного эффекта времени: 84,31%»

ХОРОШО. Теперь мы обнаружили, что мы можем правильно определить изменение средней суммы покупки на 3% в ~ 84-87% времени, влияя только на 7 000 клиентов на вариант. Давайте получим статистическую мощность как минимум до 90%. Это означает, что нам нужно увеличить размер сдвига, который мы можем измерить. Давайте попробуем 3,4%.

В работе [81]:

%%R
number_of_samples <- 7000
effect_size <- 1.034

simulation_results <- mapply(function(x){
    control <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)
    improved_variation <- rlnorm(number_of_samples, mean=purchase.log_mean, sd=purchase.log_stdev)*effect_size
    results <- t.test(improved_variation, control, alternative='greater')
    .05 >= results[3]$p.value
}, seq(0,2000, by=1))

percent_of_time_equivalent <- length(simulation_results[simulation_results==TRUE]) / length(simulation_results)
print(paste(c("Percentage of time effect detected: ", round(percent_of_time_equivalent*100, digits=2), "%"), collapse=''))

[1] «Процентная доля обнаруженного временного эффекта: 90,05%»

Да, я обманул выше. Сначала мне пришлось попробовать 3,5%, 3,1%, 3,2% и 3,3%. Наконец, я попробовал 3,4%, и это дало мне самое близкое значение. Вот почему создание таблицы было так полезно. Это позволило мне просто посмотреть на возможности для данного ограничения. Вернемся к проблеме под рукой.

Вот формальный ответ на наш вопрос: чтобы повлиять только на 7 000 клиентов на вариант, мы должны проверить изменение средней суммы наших покупок на 3,4% с уверенностью результата ~ 90%.

Тестирование метрик, таких как коэффициент конверсии

Теперь мы рассмотрели, как проверить наши гипотетические суммы покупок, которые обычно следуют так называемому логарифмически нормальному распределению. Другое распределение, которое мы видим довольно часто, — это биномиальное распределение. В частности, мы видим это в коэффициентах конверсии. Давайте рассмотрим аналогичный пример и найдем размер выборки, который нам нужен, чтобы протестировать повышение конверсии на 3%. ПРИМЕЧАНИЕ. Это действительно означает повышение коэффициента конверсии, скажем, с 8% до 8,25%. Также предположим, что мы хотим измерить этот сдвиг в 95% случаев.

Коэффициенты конверсии следуют биномиальному, а не логарифмически нормальному распределению. Это связано с тем, что каждая точка данных является либо успешной, либо неудачной. Сравните это с нашими логарифмически нормальными данными, число которых было больше нуля до бесконечности. Симуляция это немного по-другому, но так же прямо вперед.

В качестве аргумента, скажем, коэффициент конверсии, который мы хотим протестировать, обычно составляет около 8%. Мы можем создать дистрибутив следующим образом:

В работе [83]:

%%R
number_of_samples <- 1000
control_conversion_rate <- 0.08

successes <- factor(rbinom(number_of_samples, 1, control_conversion_rate) == T)
control_distribution <- data.frame(success = successes)
p <- ggplot(control_distribution, aes(x=success)) + 
    geom_histogram(binwidth=1) + 
    xlab("Purchased?") +
    ylab("# of visitors") + 
    ggtitle("Proportion Of visitors purchasing vs not") + 
    theme(axis.title.y = element_text(angle = 0))
print(p)

Биномиальное распределение

Это сильно отличается от нашего распределения суммы покупки. Вот переподготовка, чтобы вы могли сравнить:

В работе [84]:

%%R 
sales <- read.csv('~/Documents/Notebooks/raw_data/sales_data.csv', header=F, col.names=c('purchase_amount'))

p <- ggplot(sales, aes(x=purchase_amount)) + 
    geom_histogram(binwidth=.5) + 
    xlim(0, 150) + 
    xlab("Customer amount spent (in $)") +
    ylab("# of customers") + 
    ggtitle("Amounts spent by individual consumers") + 
    theme(axis.title.y = element_text(angle = 0))
print(p)

Логнормальное распределение

Эти распределения радикально отличаются. Для моделирования покупок клиентов мне понадобилось среднее значение и стандартное отклонение. Я могу получить среднее значение здесь, но я не могу получить стандартное отклонение. Что бы это даже значило в случае успеха или неудачи? В связи с этим нам необходимо использовать другой статистический тест для измерения наших результатов. Этот тест известен как биноминальный тест. Как и в случае с покупками, я собираюсь начать с моделирования теста между двумя эквивалентными дистрибутивами. Никаких изменений ни того, ни другого пока нет. В этом случае оба распределения являются «биномиальными».

В работе [85]:

%%R
number_of_samples <- 7000
control_conversion_rate <- .08

simulation_results <- mapply(function(x){
    control <- factor(rbinom(number_of_samples, 1, control_conversion_rate) == T)
    results <- binom.test(length(control[control == T]), number_of_samples, control_conversion_rate, alternative='greater')
    .05 >= results[3]$p.value
}, seq(0, 2000, by=1))

percent_of_time_equivalent <- length(simulation_results[simulation_results==TRUE]) / length(simulation_results)
print(paste(c("Percentage of time effect detected: ", round(percent_of_time_equivalent*100, digits=2), "%"), collapse=''))

[1] «Процент обнаруженного эффекта времени: 5,65%»

Опять же, мы находим существенное изменение только ~ 5% времени, что соответствует нашему 95% достоверному статистическому тесту. Теперь давайте применим размер эффекта, который мы хотим протестировать, и начнем искать точку, в которой мы достигаем 95% уверенности в нашем сплит-тесте.

В работе [86]:

%%R
number_of_samples <- 11000
effect_to_measure <- 1.03

simulation_results <- mapply(function(x){
    control <- factor(rbinom(number_of_samples, 1, control_conversion_rate*effect_to_measure) == T)
    results <- binom.test(length(control[control == T]), number_of_samples, control_conversion_rate, alternative='greater')
    .05 >= results[3]$p.value
}, seq(0, 2000, by=1))

percent_of_time_equivalent <- length(simulation_results[simulation_results==TRUE]) / length(simulation_results)
print(paste(c("Percentage of time effect detected: ", round(percent_of_time_equivalent*100, digits=2), "%"), collapse=''))

[1] «Процент обнаруженного эффекта времени: 23,74%»

Ух ты. Так что даже не близко. Давайте угадаем и проверим, пока не доберемся до 95%.

В работе [91]:

%%R
number_of_samples <- 145000
effect_to_measure <- 1.03

simulation_results <- mapply(function(x){
    control <- factor(rbinom(number_of_samples, 1, control_conversion_rate*effect_to_measure) == T)
    results <- binom.test(length(control[control == T]), number_of_samples, control_conversion_rate, alternative='greater')
    .05 >= results[3]$p.value
}, seq(0, 2000, by=1))

percent_of_time_equivalent <- length(simulation_results[simulation_results==TRUE]) / length(simulation_results)
print(paste(c("Percentage of time effect detected: ", round(percent_of_time_equivalent*100, digits=2), "%"), collapse=''))

[1] «Процентная доля обнаруженного временного эффекта: 95,35%»

После долгих предположений и проверок я, наконец, нашел размер выборки, который приближает нас к возможности надежного измерения сдвига в 3%. Это много, правда? Однако важно помнить, что, поскольку это коэффициент конверсии, нам нужно только такое количество посетителей, независимо от того, заказывают они или нет. Тем не менее, по сравнению с нашими суммами покупки, это совсем другой сценарий. Или это?

Понимаете, чтобы получить 24 000 покупок, чтобы мы могли запустить наш статистический тест, нам нужно было бы также привлечь достаточно посетителей нашего веб-сайта. Поскольку 92% из них не заказывают (поскольку наш коэффициент конверсии составляет 8%), это означает, что мы должны показать наш эксперимент гораздо большему количеству посетителей, чем мы могли ожидать.

Учитывая, что 8% наших посетителей обращаются к клиентам, и если нам нужно 24 000 клиентов, чтобы измерить 2% -ную значимость при увеличении суммы покупки, то нам потребуется около 300 000 посетителей нашего веб-сайта в нашем эксперименте для каждого варианта, чтобы гарантировать, что мы получим достаточно клиентов. Нам нужно всего лишь 145 000 посетителей в нашем эксперименте, чтобы измерить 3% значимости нашего коэффициента конверсии. По крайней мере, мы убили бы двух зайцев одним выстрелом.

Нормальный, логарифмический, биномиальный … Какой смысл?

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

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

Дело о грубой силе

Не нужно слишком углубляться в математическую теорию, мы использовали моделирование, чтобы помочь нам провести статистический анализ мощности и определить довольно точное приближение количества выборок, которое нам нужно в каждом варианте, чтобы определить 2% -ое изменение среднего значения нашего суммы покупки! Затем мы смогли жестко ограничить размер выборки нашего теста и вместо этого поиграть с изменением статистической мощности теста, а также величины эффекта, который мы измеряем.

Это несовершенный метод, но мы можем получить больше точности. Все, что нам нужно сделать, это настроить наш алгоритм для запуска более симулированных сплит-тестов. Это требует дополнительного компьютерного времени, но это НАМНОГО дешевле, чем человеческое время.

Эта статья описывает это лучше всего:

«Увеличение вычислительной мощности расширило анализ мощности во многих новых областях, и способность R запускать многократные стохастические симуляции является большой помощью. Как это ни парадоксально, математическая трудность получения формул мощности является большим уравнителем: поскольку даже статистики-исследователи обычно используют симуляции для оценки мощности, теперь можно (изучая симуляцию, которая проще, чем изучение расширенной математической статистики), работать на равных с равными передовые исследователи ». http://ms.mcmaster.ca/~bolker/emdbook/chap5A.pdf

А вот статья от компании, которая производит SAS (огромное статистическое программное обеспечение) для запуска стохастического моделирования для проведения аналогичного анализа мощности: http://blogs.sas.com/content/iml/2013/05/30/simulation -сила/

(Примечание: эта статья и высказанные мнения являются исключительно моими собственными и не отражают точку зрения моего работодателя.)