Feature Engineering: Создание признаков
Условие
У вас есть датасет с транзакциями клиентов банка (дата, сумма, категория, локация).
Предложите 10 новых признаков, которые могут быть полезны для предсказания оттока клиентов.
Объясните, почему каждый признак может быть информативным.
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение
Для предсказания оттока клиентов из транзакционных данных предлагаю 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)}")