Введение
В современном мире больших данных и высокопроизводительных вычислений оптимизация времени выполнения алгоритмов играет ключевую роль. Одним из эффективных методов ускорения вычислений является бакетизация данных.
В данной статье мы рассмотрим, как бакетизация может существенно ускорить вычисления, и представим график зависимости отношения времени на расчеты без бакетизации к времени на расчеты с бакетизацией.
Что такое бакетизация?
Бакетизация — это процесс разделения общей выборки случайным образом на несколько подгрупп (buckets), которые затем анализируются отдельно. Этот метод широко используется в статистических исследованиях, особенно в A/B тестировании.
Процесс бакетизации
Определяем каждому из N изначальных независимых наблюдений случайное число от 1 до M (M≤N), назовем это бакетом.
Агрегируем по этим бакетам, считаем сумму метрики в каждом бакете, считаем количество наблюдений в каждом бакете, и теперь у нас независимое наблюдение – бакет.
Основные преимущества бакетизации включают:
Снижение объема данных: Группировка данных уменьшает количество обрабатываемых элементов, что снижает нагрузку на память и процессор.
Ускорение вычислений: Выполнение операций на уровне групп (бакетов) требует меньше времени по сравнению с выполнением тех же операций на уровне индивидуальных данных.
Упрощение алгоритмов: Некоторые алгоритмы могут быть значительно упрощены при работе с агрегированными данными, что также способствует ускорению вычислений.
Методология
Для исследования эффективности бакетизации данных и её влияния на время выполнения вычислений мы провели симуляцию, в которой сравнили два подхода: индивидуальный (без бакетизации) и агрегированный (с бакетизацией). Вычисления производились в 1 поток.
Описание эксперимента
-
Генерация данных:
Для каждого набора данных мы сгенерировали нормальное распределение с параметрами μ = 10 и σ = 1. Количество данных варьировалось от 10^4 до 10^7 элементов.
-
Бакетизация:
Данные были распределены по 1000 бакетам.
Для каждого бакета рассчитывались суммы и количество элементов.
-
Симуляции:
Для обоих подходов (индивидуальный и агрегированный) мы провели t-тесты на различия между контрольной и экспериментальной группами.
В каждой симуляции использовалось 100 повторений для получения стабильных результатов.
-
Измерение времени:
Время выполнения симуляций замерялось для каждого подхода, и затем рассчитывалось отношение времени выполнения индивидуального подхода к времени выполнения агрегированного подхода.
Результаты
Результаты эксперимента были визуализированы на графике зависимости отношения времени на расчеты без бакетизации к времени на расчеты с бакетизацией от количества наблюдений.
Анализ результатов
На графике видно, что отношение растет линейно с увеличением количества наблюдений. Это подтверждает гипотезу о том, что бакетизация позволяет значительно ускорить вычисления.
Получившиеся коэффициенты линейной регрессии:
Наклон (Slope): 1.69e-05
Сдвиг (Intercept): -5.38
Коэффициент детерминации (R-squared): 0.976
Эти результаты свидетельствуют о сильной линейной зависимости и высокой эффективности бакетизации для ускорения вычислений при увеличении объема данных.
Код:
-
Импорт библиотек
import numpy as np from scipy.stats import ttest_ind import time from tqdm import tqdm import plotly.graph_objects as go import scipy.stats as stats import warnings warnings.filterwarnings("ignore")
-
Функция
run_simulation
:Выполняет t-тест для двух групп данных и возвращает p-значение.
def run_simulation(control_group, treatment_group): """ Выполняет t-тест для двух групп данных и возвращает p-значение. Args: control_group (array-like): Данные контрольной группы. treatment_group (array-like): Данные экспериментальной группы. Returns: float: p-значение t-теста. """ _, p_value = ttest_ind(control_group, treatment_group) return p_value
-
Функция
run
:Генерирует данные.
Выполняет бакетизацию данных.
Проводит симуляции для обоих подходов (ind и agg).
Замеряет время выполнения симуляций и возвращает результаты в виде словаря.
def run(n_obs, n_buckets, n_simulations): """ Выполняет симуляции для сравнения времени выполнения расчетов с бакетизацией и без неё. Args: n_obs (int): Количество пользователей (размер данных). n_buckets (int): Количество бакетов для бакетизации. n_simulations (int): Количество повторений симуляций. Returns: dict: Словарь с временем выполнения для каждого подхода. """ # Генерация данных data_ind = np.random.normal(10, 1, n_obs) # Бакетизация данных bucket_indices_control = np.random.randint(0, n_buckets, data_ind.size) bucket_sums_control = np.bincount(bucket_indices_control, weights=data_ind, minlength=n_buckets) bucket_counts_control = np.bincount(bucket_indices_control, minlength=n_buckets) data_agg = bucket_sums_control / bucket_counts_control # Параметры для симуляций params = [ ('ind', data_ind[:n_obs//2], data_ind[n_obs//2:]), ('agg', data_agg[:n_buckets//2], data_agg[n_buckets//2:]) ] times = {} for param in params: start_time = time.time() for _ in range(n_simulations): run_simulation(param[1], param[2]) elapsed_time = time.time() - start_time sim_type = 'Individual Data' if param[0] == 'ind' else 'Aggregated Data' times[sim_type] = elapsed_time return times
-
Основной блок кода:
Определяет параметры эксперимента (размеры данных, количество бакетов и количество симуляций).
Запускает симуляции для каждого размера данных и сохраняет результаты.
Строит график зависимости отношения времени на расчеты без бакетизации к времени на расчеты с бакетизацией.
Добавляет линию тренда и аннотацию с результатами регрессии.
Настраивает макет графика и отображает его.
# Параметры эксперимента ns_obs = np.linspace(10**4, 10**7, 100).astype(int) n_buckets = 1000 n_simulations = 100 results = {} for n_obs in tqdm(ns_obs, desc='Processing user counts'): results[n_obs] = run(n_obs, n_buckets, n_simulations) # Визуализация результатов с линией тренда ind_times = np.asarray([results[n]['Individual Data'] for n in ns_obs]) agg_times = np.asarray([results[n]['Aggregated Data'] for n in ns_obs]) ratio = ind_times / agg_times # Вычисление коэффициентов регрессии slope, intercept, r_value, p_value, std_err = stats.linregress(ns_obs, ratio) trendline = intercept + slope * ns_obs # Создание графика fig = go.Figure() fig.add_trace(go.Scatter(x=ns_obs, y=ratio, mode='markers+lines', name='Data Points')) # Добавление линии тренда fig.add_trace(go.Scatter(x=ns_obs, y=trendline, mode='lines', name='Trend Line', line=dict(color='red', dash='dash'))) # Форматирование текста аннотации с использованием экспоненциальной нотации stats_text = f"Slope: {slope:.2e}<br>Intercept: {intercept:.2f}<br>R-squared: {r_value**2:.3f}" # Добавление аннотации с результатами регрессии fig.add_annotation(x=ns_obs[-1], y=trendline[-1], text=stats_text, showarrow=True, font=dict(size=12), arrowhead=1, arrowsize=1, arrowwidth=2, arrowcolor='red', ax=20, ay=-30, bgcolor='white', bordercolor='black') # Настройка макета fig.update_layout( title='Comparison of Computation Times', xaxis_title='Number of Users', yaxis_title='Individual approach time / Aggregated approach time', plot_bgcolor='white', paper_bgcolor='white', font_color='black', width=1200, height=800, showlegend=True ) fig.show()
Вывод
Проведенное исследование показало, что бакетизация данных позволяет значительно ускорить вычисления. Отношение времени выполнения операций без бакетизации к времени выполнения с бакетизацией увеличивается линейно с ростом объема данных, что подтверждает высокую эффективность метода.
Применение бакетизации особенно полезно в задачах, связанных с обработкой больших объемов данных, где время выполнения вычислений играет критически важную роль. В условиях растущих требований к производительности этот метод может стать ключевым инструментом для оптимизации вычислений и повышения эффективности обработки данных.
Aquahawk
Первый раз слышу чтобы кто-то упирался в a/b тестах в скорость. Разве что если весь код на чистом питоне написал это может тормозить. Положите данные в clickhouse и используйте его как вычислительную платформу, получите ускорение в 100-1000 раз.
Guest11 Автор
Добрый день!
Согласен, использование таких систем, как ClickHouse, может существенно ускорить вычисления в сравнении с некоторыми аналогами.
В данной статье я хотел показать преимущества бакетизации на уровне алгоритмов, которые могут быть полезны в различных контекстах, в том числе в тех, где использование высокопроизводительных баз данных, таких как ClickHouse, невозможно или не предусмотрено. Это может быть, например, где есть определенные ограничения по русурсам.