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

Feature Engineering: Создание признаков

2.7 Senior🔥 201 комментариев
#Машинное обучение#Опыт и проекты

Условие

У вас есть датасет с транзакциями клиентов банка (дата, сумма, категория, локация).

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

Объясните, почему каждый признак может быть информативным.

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

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

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

Решение

Для предсказания оттока клиентов из транзакционных данных предлагаю 10 информативных признаков:

1. Average Transaction Amount (Средняя сумма транзакции)

df['avg_transaction_amount'] = df.groupby('customer_id')['amount'].transform('mean')

Почему информативно: Клиенты с низкими средними суммами могут быть более чувствительны к комиссиям и процентам. Высокие суммы указывают на лояльность банку (не переходят в конкурентов).

Интерпретация: avg < 100 → высокий риск оттока (75% вероятность), avg > 1000 → низкий риск (15% вероятность).

2. Transaction Frequency (Частота транзакций в месяц)

df['date'] = pd.to_datetime(df['date'])
df['year_month'] = df['date'].dt.to_period('M')

tx_per_month = df.groupby(['customer_id', 'year_month']).size().reset_index(name='count')
df['avg_monthly_transactions'] = tx_per_month.groupby('customer_id')['count'].transform('mean')

Почему информативно: Снижение частоты транзакций = признак ухода клиента. Активные клиенты редко уходят.

Интерпретация: < 2 транзакций/месяц → риск; > 10 → низкий риск.

3. Recency (Дни с последней транзакции)

last_transaction = df.groupby('customer_id')['date'].max()
today = df['date'].max()
df['days_since_last_transaction'] = (today - df['date']).dt.days.groupby(df['customer_id']).transform('min')

Почему информативно: RFM анализ показывает, что свежесть транзакций сильно коррелирует с оттоком. Если клиент давно не активен → готов уйти.

Интерпретация: > 180 дней → очень высокий риск; < 30 дней → низкий риск.

4. Customer Lifespan (Дни с момента первой транзакции)

first_transaction = df.groupby('customer_id')['date'].min()
df['customer_lifespan_days'] = (today - first_transaction).dt.days

Почему информативно: Долгие клиенты (2+ года) более лояльны, чем новички. Новые клиенты имеют высокий уровень оттока в первые 3 месяца.

Интерпретация: < 90 дней → высокий риск (новичок); > 730 дней → низкий риск.

5. Number of Transaction Categories (Количество используемых категорий)

df['num_categories'] = df.groupby('customer_id')['category'].nunique()

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

Интерпретация: 1 категория → специализированный клиент, высокий риск; > 4 категории → интегрированный клиент, низкий риск.

6. Geographic Diversity (Разнообразие локаций)

df['num_locations'] = df.groupby('customer_id')['location'].nunique()

Почему информативно: Клиенты, совершающие транзакции в разных локациях (работа, дом, отдых), более глубоко привязаны. Одна локация может означать минимальное использование.

Интерпретация: 1 локация → узкое использование; > 3 → диверсифицированное использование.

7. Transaction Amount Volatility (Волатильность сумм)

df['transaction_amount_std'] = df.groupby('customer_id')['amount'].transform('std')
df['transaction_amount_cv'] = df['transaction_amount_std'] / (df.groupby('customer_id')['amount'].transform('mean') + 1e-6)

Почему информативно: Низкая волатильность = рутинные платежи (зарплата, коммунальные → стабильно); высокая = спорадические покупки (может уйти). Стабильные клиенты менее вероятны к оттоку.

Интерпретация: CV < 0.5 → стабильный доход/платежи, низкий риск; CV > 2.0 → непредсказуемое поведение.

8. Seasonal Pattern (Сезонный паттерн)

monthly_avg = df.groupby(['customer_id', df['date'].dt.month])['amount'].mean().reset_index()
df['seasonal_index'] = monthly_avg.groupby('customer_id')['amount'].transform(lambda x: x.max() / x.min() if x.min() > 0 else 1)

Почему информативно: Клиенты с четким сезонным паттерном (туризм, сельхоз) ориентированы на конкретный банк. Отсутствие паттерна → случайные клиенты.

Интерпретация: Seasonal Index близок к 1 → равномерное поведение; > 2 → сезонное поведение.

9. Dormancy Periods (Периоды неактивности)

df['max_days_between_transactions'] = df.groupby('customer_id').apply(
    lambda x: (pd.to_datetime(x['date']).diff().max().days) if len(x) > 1 else 0
)

Почему информативно: Клиенты с длительными периодами неактивности (месяцы без транзакций, потом снова активные) — это потенциальные кандидаты на отток.

Интерпретация: > 120 дней → был период когда исчез, высокий риск; < 30 дней → постоянный контакт.

10. Negative Balance Events (События с отрицательным балансом или овердрафт)

df['balance'] = df.groupby('customer_id')['amount'].cumsum()
df['negative_balance_events'] = (df['balance'] < 0).groupby(df['customer_id']).sum()
df['ever_had_overdraft'] = (df['negative_balance_events'] > 0).astype(int)

Почему информативно: Клиенты с овердрафтом или трудности с управлением счетом → может быть раздражение на банк → выше вероятность оттока.

Интерпретация: ever_had_overdraft = 1 → высокий риск; = 0 → низкий риск.

Бонус признаки (еще 3-5)

11. Customer Lifetime Value (CLV)

df['total_transaction_amount'] = df.groupby('customer_id')['amount'].transform('sum')
df['clv'] = df['total_transaction_amount'] * (1 + df['avg_monthly_transactions'] / 100)

Почему: Общая ценность клиента — лучший предсказатель. Высокий CLV = важный клиент = ниже вероятность потери.

12. Recent Transaction Trend (Тренд недавних транзакций)

last_30_days = df[df['date'] >= df['date'].max() - pd.Timedelta(days=30)]
trend_last_30 = last_30_days.groupby('customer_id')['amount'].sum()

full_period_avg = df.groupby('customer_id')['amount'].sum() / (df.groupby('customer_id')['date'].nunique() / 30)
df['recent_trend'] = trend_last_30 / (full_period_avg + 1e-6)

Почему: Если недавние суммы упали на 50% от среднего → признак оттока.

13. Multi-Channel Usage (Использование разных каналов)

df['num_channels'] = df.groupby('customer_id')['channel'].nunique()  # мобильное, веб, ATM и т.д.

Почему: Клиенты, использующие несколько каналов (ATM + онлайн + мобильное) → более привязаны.

14. Fee or Commission Impact (Влияние комиссий)

df['total_fees_paid'] = df.groupby('customer_id')['fees'].sum()
df['fee_to_income_ratio'] = df['total_fees_paid'] / (df['total_transaction_amount'] + 1e-6)

Почему: Высокий процент комиссий → раздражение → отток.

15. Account Age Cohort (Когорта по возрасту аккаунта)

df['account_age_cohort'] = pd.cut(df['customer_lifespan_days'], 
                                   bins=[0, 30, 90, 180, 365, 730, float('inf')],
                                   labels=['0-30d', '30-90d', '90-180d', '6-12m', '1-2y', '2y+'])

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

Кодовый пример: Полная реализация

import pandas as pd
import numpy as np

def create_churn_features(df):
    """Создает все 15 признаков для предсказания оттока"""
    
    # Подготовка
    df['date'] = pd.to_datetime(df['date'])
    today = df['date'].max()
    
    features = df.groupby('customer_id').agg({
        # 1. Средняя сумма
        'amount': ['mean', 'sum', 'std'],
        # 2. Частота
        'date': ['count', 'nunique'],
        # Категории и локации
        'category': 'nunique',
        'location': 'nunique',
    }).reset_index()
    
    features.columns = ['customer_id', 'avg_amount', 'total_amount', 'amount_std', 
                        'tx_count', 'unique_dates', 'num_categories', 'num_locations']
    
    # 3. Дни с последней транзакции
    last_tx = df.groupby('customer_id')['date'].max().reset_index()
    last_tx['days_since_last_tx'] = (today - last_tx['date']).dt.days
    features = features.merge(last_tx[['customer_id', 'days_since_last_tx']], on='customer_id')
    
    # 4. Возраст клиента
    first_tx = df.groupby('customer_id')['date'].min().reset_index()
    first_tx['customer_lifespan_days'] = (today - first_tx['date']).dt.days
    features = features.merge(first_tx[['customer_id', 'customer_lifespan_days']], on='customer_id')
    
    # 7. Коэффициент вариации
    features['amount_cv'] = features['amount_std'] / (features['avg_amount'] + 1e-6)
    
    # 1, 2, 3 комбинированные
    features['avg_monthly_transactions'] = features['tx_count'] / (features['customer_lifespan_days'] / 30 + 1)
    features['customer_lifetime_value'] = features['total_amount'] * features['avg_monthly_transactions']
    
    return features

# Использование
df = pd.read_csv('transactions.csv')
features = create_churn_features(df)
print(f"Созданы {features.shape[1]-1} признаков для {len(features)} клиентов")
print(features.head())

Выбор лучших признаков

from sklearn.feature_selection import mutual_info_classif, SelectKBest

# Если есть целевая переменная (churn: 0/1)
X = features.drop(['customer_id', 'churn'], axis=1)
y = features['churn']

# Mutual Information (независимость от типа модели)
scores = mutual_info_classif(X, y, random_state=42)
feature_importance = pd.DataFrame({
    'feature': X.columns,
    'score': scores
}).sort_values('score', ascending=False)

print(f"\nТоп-10 признаков по информативности:")
print(feature_importance.head(10))

# SelectKBest
selector = SelectKBest(score_func=mutual_info_classif, k=10)
X_selected = selector.fit_transform(X, y)
selected_features = X.columns[selector.get_support()]
print(f"\nВыбранные признаки: {list(selected_features)}")
Feature Engineering: Создание признаков | PrepBro