← Назад к вопросам

Разница в подготовке данных для BI и ML

1.6 Junior🔥 171 комментариев
#Хранилища данных

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

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница в подготовке данных для BI и ML

Основные различия

Business Intelligence (BI) нужны данные для отчётов, дашбордов и аналитики. Machine Learning (ML) нужны данные для обучения моделей. Подход существенно отличается.

1. Уровень агрегации

BI: Высокий уровень агрегации

-- Дашборд для руководства
SELECT 
    DATE_TRUNC(day, order_date) as day,
    SUM(amount) as daily_revenue,
    COUNT(*) as order_count,
    AVG(amount) as avg_order_value
FROM orders
GROUP BY DATE_TRUNC(day, order_date)
ORDER BY day DESC;

Людям нужны сводки, тренды, сравнения по отделам/категориям.

ML: Детальный уровень (примеры)

# Для предсказания чёрна (churn) нужны индивидуальные признаки
import pandas as pd

df = pd.DataFrame({
    user_id: [1, 2, 3, ...],
    last_login_days_ago: [5, 1, 30, ...],  # Дни с последнего визита
    total_purchases: [10, 50, 2, ...],     # Всего покупок
    days_active: [365, 180, 30, ...],      # Дни с регистрации
    support_tickets: [0, 1, 5, ...],       # Обращения в поддержку
    churn: [0, 0, 1, ...]  # Целевая переменная
})

Моделям нужны детальные признаки каждого объекта.

2. Обработка пропусков

BI: Игнорируются или показываются отдельно

-- BI дашборд
SELECT department, COUNT(*) as employees
FROM employees
WHERE department IS NOT NULL  -- просто исключаем
GROUP BY department;

ML: Должны быть обработаны

# ML требует полных данные
from sklearn.impute import SimpleImputer

# Заполнение пропусков средним значением
imputer = SimpleImputer(strategy=mean)
X_filled = imputer.fit_transform(X)

# Или удаление строк с пропусками
df_clean = df.dropna()

3. Масштабирование признаков

BI: Не требуется

Выручка: $100 000
Средний чек: $50
Количество заказов: 2000

-- Люди видят понятные числа

ML: КРИТИЧНО

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Почему?
# Без масштабирования:
# revenue (1..1000000) доминирует над age (18..80)
# Модель учится неправильно

4. Временные ряды

BI: Просто показываем исторические значения

SELECT date, SUM(revenue) as daily_revenue
FROM sales
GROUP BY date
ORDER BY date;

-- Вывод: тренд график

ML: Создаём лаговые признаки

import pandas as pd

df = pd.DataFrame({
    date: [2024-01-01, 2024-01-02, 2024-01-03, ...],
    revenue: [1000, 1200, 1100, ...]
})

# Лаговые признаки (предыдущие дни)
df[revenue_lag_1] = df[revenue].shift(1)  # вчера
df[revenue_lag_7] = df[revenue].shift(7)  # неделю назад
df[revenue_ma7] = df[revenue].rolling(7).mean()  # средний за 7 дней

# Результат:
#         date  revenue  revenue_lag_1  revenue_lag_7  revenue_ma7
# 0  2024-01-01     1000            NaN            NaN          NaN
# 1  2024-01-02     1200           1000            NaN          NaN
# 2  2024-01-03     1100           1200            NaN          NaN

5. Категориальные переменные

BI: Используют как есть

SELECT category, SUM(revenue)
FROM products
GROUP BY category;

-- Электроника: $500K
-- Одежда: $300K
-- Книги: $100K

ML: One-Hot Encoding или Label Encoding

import pandas as pd

df = pd.DataFrame({
    category: [electronics, clothes, books, electronics, ...],
    price: [500, 50, 20, 1000, ...]
})

# One-Hot Encoding
df_encoded = pd.get_dummies(df, columns=[category], drop_first=True)

# Результат:
#   price  category_clothes  category_electronics
# 0   500              0                       1
# 1    50              1                       0
# 2    20              0                       0
# 3  1000              0                       1

6. Outliers (выбросы)

BI: Показываем как есть (важно видеть аномалии)

-- Дашборд показывает все значения
SELECT product, SUM(quantity) as sold
FROM orders
GROUP BY product
ORDER BY sold DESC;

-- iPhone Продали 500 (выброс) — это нормально видеть!

ML: Часто удаляют или трансформируют

from scipy.stats import zscore

# Удаление выбросов (> 3 standard deviations)
df_clean = df[abs(zscore(df[price])) < 3]

# Или логарифмическая трансформация
df[log_price] = np.log1p(df[price])

7. Data Leakage

BI: Не критично

Можно использовать будущие данные для исторических отчётов.

ML: КРИТИЧНО избежать!

# ПЛОХО: Data Leakage
df[is_high_value] = (df[total_purchases] > 10) & (df[churn] == 0)
# ^^ Используем целевую переменную при создании признака!

# ХОРОШО: Признаки только из прошлого
df[avg_purchase_30d_ago] = df[purchases].rolling(30).mean()
df[churn] = df[status] == inactive  # Целевая переменная отдельно

8. Таблица vs Features

BI: Витрина = готовый отчет

CREATE TABLE sales_dashboard AS
SELECT 
    date,
    department,
    SUM(amount) as revenue,
    COUNT(*) as orders
FROM sales
GROUP BY date, department;

ML: Feature Store для переиспользования

# Feature Store (например, Tecton, Feast)
feature_store = {
    user_1: {
        lifetime_value: 5000,
        days_since_purchase: 10,
        purchase_frequency: 0.5,
        churn_prob: 0.1  # Может использоваться в разных моделях
    }
}

9. Сравнение подхода

АспектBIML
АгрегацияВысокаяДетальная
ПропускиИсключаютсяЗаполняются/обрабатываются
МасштабированиеНе нужноКритично
OutliersПоказываютсяЧасто удаляются
ЛагиНе используютсяEssential для временных рядов
КатегорииКак естьOne-Hot/Label Encoding
Data LeakageНе проблемаПРОБЛЕМА!
ЦельInsightsPredictions

Примеры реальных различий

Сценарий: Прогноз неуплаты счетов (ML) vs Анализ неплатежей (BI)

BI подход (в SQL):

SELECT 
    month,
    COUNT(*) as total_invoices,
    COUNT(CASE WHEN status = unpaid THEN 1 END) as unpaid,
    ROUND(100.0 * COUNT(CASE WHEN status = unpaid THEN 1 END) / COUNT(*), 2) as percent
FROM invoices
GROUP BY month
ORDER BY month DESC;

ML подход (на Spark):

from pyspark.sql.functions import col, when, datediff, row_number
from pyspark.sql.window import Window
from pyspark.ml.feature import StandardScaler, VectorAssembler

# Для каждого счёта — вычислить признаки
df = spark.read.parquet(invoices)

window = Window.partitionBy(customer_id).orderBy(date)

features = df.select(
    invoice_id,
    amount,
    datediff(col(current_date), col(invoice_date)).alias(days_since_invoice),
    (col(amount) > 10000).cast(int).alias(high_amount),
    row_number().over(window).alias(customer_invoice_count),
    is_unpaid  # целевая переменная
)

# Масштабирование
assembler = VectorAssembler(inputCols=[amount, days_since_invoice, customer_invoice_count], outputCol=features)
scaler = StandardScaler(inputCol=features, outputCol=scaled_features)

Вывод

BI:

  • Готовят данные для анализа и отчётов
  • Работают с агрегированными данными
  • Не беспокоятся о масштабировании, пропусках, outliers

ML:

  • Готовят данные для обучения моделей
  • Работают с детальными признаками
  • Критична чистота, масштабирование, избежание data leakage

Для Data Engineer это означает: нужно подготовить две разные витрины данных, даже если источники одинаковые!