Привет, Хабр!

Знаете, что самое раздражающее в A/B тестах? Это ожидание результатов.

А что, если можно ускорить процесс и получить нужные данные быстрее? Сегодня расскажу, как разогнать A/B тесты, чтобы не терять время зря и быстрее получать результаты

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

Параллельные A/B тесты: когда времени мало, а гипотез много

Параллельные тесты предполагают проведение нескольких A/B тестов одновременно. Однако, тут важен правильный подход, чтобы избежать перекрёстного влияния тестов друг на друга, что может исказить результаты.

Основные стратегии проведения параллельных A/B тестов:

  1. Сегментация аудитории: один из самых простых способов избежать конфликтов между параллельными тестами — сегментация аудитории. Например, если есть 100 000 пользователей, можно разделить их на группы по 25 000 и запустить отдельный тест на каждую группу. Так тесты не будут пересекаться и влиять друг на друга.

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

  3. Ограничение числа одновременных тестов: да, можно запустить много тестов одновременно, но помните о том, что чем больше тестов вы запускаете, тем выше вероятность перекрёстного влияния. Рекомендуется не запускать более 2–3 тестов на одном и том же сегменте аудитории.

Пример на Питоне для сегментации:

import random

# допустим, есть 100,000 пользователей
users = list(range(1, 100001))

# разделим их на четыре группы по 25,000
group_a = random.sample(users, 25000)
group_b = random.sample(list(set(users) - set(group_a)), 25000)
group_c = random.sample(list(set(users) - set(group_a) - set(group_b)), 25000)
group_d = list(set(users) - set(group_a) - set(group_b) - set(group_c))

# теперь можно запускать тесты на каждой группе

Многовариантные тесты

Многовариантные тесты позволяют проверять несколько гипотез в одном эксперименте. В отличие от A/B тестирования, где сравниваются две версии страницы, в многовариантном тесте можно сравнивать сотни комбинаций различных элементов.

Основные принципы многовариантного тестирования:

  1. Разбиение на факторы и уровни: каждая тестируемая гипотеза разбивается на факторы, а каждый фактор — на уровни. Например, можно тестировать три заголовка и два варианта кнопки на странице. Это создаст шесть комбинаций для тестирования.

  2. Подсчет всех возможных комбинаций: важно понимать, сколько комбинаций будет тестироваться, т.к количество пользователей, необходимых для достижения статистической значимости, будет увеличиваться с ростом числа комбинаций. Например, если есть три заголовка, два изображения и две кнопки, будет 3×2x2 = 12 комбинаций.

  3. Использование инструментов: многовариантное тестирование требует некоторых платформ или библиотек, к пример Optimizely или Google Optimize, которые могут автоматически генерировать и управлять этими комбинациями.

Пример настройки многовариантного теста на JavaScript:

function runMultivariateTest() {
    const headlineOptions = ['Заголовок 1', 'Заголовок 2', 'Заголовок 3'];
    const imageOptions = ['image1.jpg', 'image2.jpg'];
    const buttonOptions = ['Кнопка A', 'Кнопка B'];

    const headline = headlineOptions[Math.floor(Math.random() * headlineOptions.length)];
    const image = imageOptions[Math.floor(Math.random() * imageOptions.length)];
    const button = buttonOptions[Math.floor(Math.random() * buttonOptions.length)];

    document.getElementById('headline').innerText = headline;
    document.getElementById('mainImage').src = image;
    document.getElementById('ctaButton').innerText = button;
}

runMultivariateTest();

Анализ данных в реальном времени и динамическая корректировка

Для анализа данных в режиме реального времени существует множество инструментов, некоторые из них:

  1. Google Optimize:

    • Google Optimize позволяет не только создавать и запускать A/B тесты, но и получать результаты в режиме реального времени. Платформа интегрирована с Google Analytics.

  2. Optimizely:

    • Optimizely предоставляет функционал не только для A/B тестирования, но и для многовариантных и персонализированных тестов.

  3. VWO:

    • VWO имеет функционал для мониторинга тестов в реальном времени.

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

Реализация анализа данных в реальном времени с помощью Google Analytics API

Допустим, нужно отслеживать, как изменяется коэффициент конверсии в реальном времени и принимать решения на основе этих данных:

from googleapiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials
import pandas as pd

# настройка доступа к Google Analytics API
SCOPES = ['https://www.googleapis.com/auth/analytics.readonly']
KEY_FILE_LOCATION = 'path_to_your_key_file.json'
VIEW_ID = 'your_view_id'

def initialize_analyticsreporting():
    credentials = ServiceAccountCredentials.from_json_keyfile_name(
        KEY_FILE_LOCATION, SCOPES)
    analytics = build('analyticsreporting', 'v4', credentials=credentials)
    return analytics

def get_realtime_data(analytics):
    return analytics.data().realtime().get(
        ids='ga:' + VIEW_ID,
        metrics='rt:activeUsers'
    ).execute()

# пример получения данных
analytics = initialize_analyticsreporting()
realtime_data = get_realtime_data(analytics)

active_users = realtime_data['rows'][0][0]
print(f"Количество активных пользователей на сайте: {active_users}")

Адаптивное тестирование

Адаптивное тестирование — это методика A/B тестирования, позволяющая динамически изменять условия эксперимента на основе текущих данных.

В основе адаптивного тестирования лежат алгоритмы, которые анализируют результаты тестов в режиме реального времени и принимают решения о том, как оптимально перераспределить трафик. Этот подход основан на методах ML и статистических моделях, таких как Bayesian Bandits или Thompson Sampling, которые позволяют оценивать эффективность каждого варианта и динамически настраивать тест.

Предположим, нужно провести A/B/C тест, где есть три варианта: A, B и C. Классический подход подразумевает равномерное распределение трафика между всеми тремя вариантами до конца теста. В адаптивном тестировании, как только один из вариантов начинает значительно опережать другие по ключевым метрикам, алгоритм может перераспределить больше трафика на этот вариант, чтобы быстрее достичь статистической значимости.

Пример для реализации адаптивного тестирования с использованием Thompson Sampling:

import numpy as np

# предположим, у нас есть три варианта A, B и C
conversions = np.array([10, 20, 30])  # Количество конверсий для каждого варианта
views = np.array([100, 100, 100])     # Количество просмотров для каждого варианта

# функция для расчета вероятности победы варианта с использованием Thompson Sampling
def thompson_sampling(conversions, views, num_samples=10000):
    sampled_means = np.random.beta(conversions + 1, views - conversions + 1, (num_samples, len(conversions)))
    return np.argmax(sampled_means, axis=1)

# генерация 10000 выборок и подсчет, какой вариант чаще выигрывает
samples = thompson_sampling(conversions, views)
winning_option = np.bincount(samples).argmax()

print(f"Наиболее вероятный вариант-победитель: Вариант {chr(65 + winning_option)}")

Несмотря на свои преимущества, адаптивное тестирование имеет и свои риски. Например, если слишком рано перераспределить трафик на один из вариантов, можно пропустить скрытые тенденции в других вариантах.

Снижение вариативности и контроль внешних факторов

Результаты A/B теста могут быть искажены множеством внешних факторов. Если не контролировать эти переменные, то есть риск получить неверные данные, которые могут привести к ошибочным решениям.

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

Методы снижения вариативности

Сегментация трафика — один из лучших способов контроля внешних факторов. Это процесс разделения аудитории на группы, которые обладают сходными характеристиками. Основные методы сегментации включают:

  1. Географическая сегментация

  2. Сегментация по устройствам

  3. Сегментация по источнику трафика

Пример реализации на Python:

import pandas as pd

# пример данных
data = {
    'user_id': [1, 2, 3, 4, 5, 6],
    'location': ['US', 'US', 'UK', 'UK', 'RU', 'RU'],
    'device': ['mobile', 'desktop', 'mobile', 'desktop', 'mobile', 'desktop'],
    'source': ['organic', 'paid', 'organic', 'paid', 'social', 'organic']
}

df = pd.DataFrame(data)

# сегментация по локации
us_users = df[df['location'] == 'US']
uk_users = df[df['location'] == 'UK']
in_users = df[df['location'] == 'RU']

# вывод данных для каждой группы
print("US Users:\n", us_users)
print("UK Users:\n", uk_users)
print("IN Users:\n", in_users)

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

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

Пример использования временных факторов:

df['day_of_week'] = pd.to_datetime(df['timestamp']).dt.day_name()

# сегментация данных по дням недели
weekend_data = df[df['day_of_week'].isin(['Saturday', 'Sunday'])]
weekday_data = df[~df['day_of_week'].isin(['Saturday', 'Sunday'])]

print("Weekend Data:\n", weekend_data)
print("Weekday Data:\n", weekday_data)

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

Пример блокировки переменных на Python:

import numpy as np

# предположим, есть группа пользователей с различным опытом на сайте
df['experience'] = np.random.choice(['new', 'returning'], size=len(df))

# блокируем пользователей с одинаковым опытом и рандомизируем их между A/B группами
df['group'] = df.groupby('experience')['user_id'].apply(lambda x: np.random.choice(['A', 'B'], size=len(x)))

print(df)

Чтобы результаты тестов были максимально достоверными, важно правильно настроить группы контроля и тестирования. Группа контроля — это группа, на которую не влияют тестируемые изменения, и она служит базовым уровнем для сравнения. Группа тестирования получает новый опыт.

Основные принципы настройки групп:

  1. Пользователи должны быть равномерно распределены между группами, чтобы каждая группа была представительной.

  2. Группы должны быть схожи по основным характеристикам: демография, поведенческие паттерны и источники трафика.


Теперь у вас есть несколько способов ускорить свои A/B тесты и при этом не потерять ни капли точности!

И помните: ждать, конечно, можно и долго, но кто первый получил результаты — тот и в дамках. Удачи в ваших тестах и высоких конверсий!

Больше актуальных навыков по аналитике и анализу вы можете получить в рамках практических онлайн-курсов от экспертов отрасли.

Комментарии (1)


  1. Andrey_Solomatin
    03.09.2024 14:07
    +1

    group_a = random.sample(users, 25000)
    group_b = random.sample(list(set(users) - set(group_a)), 25000)
    group_c = random.sample(list(set(users) - set(group_a) - set(group_b)), 25000)group_d = list(set(users) - set(group_a) - set(group_b) - set(group_c))

    От количества временных коллекций которые создаются в этом коде становится грусно.