Skip to content

Latest commit

 

History

History
175 lines (140 loc) · 9.99 KB

File metadata and controls

175 lines (140 loc) · 9.99 KB

Feature Engineering — Churn Prediction (Live Betting)

Generated by src/feature_engineering.py · generate_features(df) appends all columns below to the input DataFrame.

Observation period: 90 days (observation_start_dateobservation_end_date)
Target: churn (1 = no live bet in the 30-day outcome window) — excluded from all features
Leakage exclusions: live_bets_count_outcome_window, observation_start_date, observation_end_date


Base feature columns

Columns from the raw dataset that are safe model inputs (BASE_FEATURE_COLUMNS).

Column Type Description
tenure_days int Days from registration to observation end
live_bets_count int Number of live bets in the observation period
avg_bet_amount float Average stake per bet (EUR); NaN when no bets
total_turnover float Total live turnover (EUR)
total_payout float Total payout (EUR)
ggr float Gross gaming revenue = turnover − payout (can be negative)
ggr_margin float GGR / turnover; NaN when turnover = 0
deposit_count int Number of deposits
total_deposit_amount float Total deposited (EUR)
deposit_to_turnover_ratio float total_deposit_amount / total_turnover; NaN when turnover = 0
days_active_in_observation int Days with ≥ 1 bet in the observation period
bet_day_rate float days_active_in_observation / 90
days_since_last_bet int Days from last bet to observation end; NaN when no bets

Extra features (ExtraFeatures enum)

66 engineered features across 9 categories. All are in ALL_FEATURE_COLUMNS alongside the base columns above.

Log1p transforms

Applied to right-skewed count and amount columns. For no-bet players avg_bet_amount is filled with 0 before transformation; days_since_last_bet is filled with 90 (max staleness).

Feature Formula
live_bets_count_log1p log1p(live_bets_count)
avg_bet_amount_log1p log1p(avg_bet_amount.fillna(0))
total_turnover_log1p log1p(total_turnover)
total_payout_log1p log1p(total_payout)
total_deposit_amount_log1p log1p(total_deposit_amount)
avg_deposit_amount_log1p log1p(total_deposit_amount / deposit_count)
deposit_count_log1p log1p(deposit_count)
tenure_days_log1p log1p(tenure_days)
days_since_last_bet_log1p log1p(days_since_last_bet.fillna(90))
days_active_in_observation_log1p log1p(days_active_in_observation)
days_to_first_bet_in_obs_log1p log1p(days_to_first_bet_in_obs)

Sqrt transforms

Lighter compression than log1p; useful as an alternative encoding for tree ensembles.

Feature Formula
live_bets_count_sqrt sqrt(live_bets_count)
total_turnover_sqrt sqrt(total_turnover)
avg_bet_amount_sqrt sqrt(avg_bet_amount.fillna(0))

GGR transforms

GGR can be negative (player winning). Signed transforms preserve direction.

Feature Formula Interpretation
ggr_abs_log1p log1p(|ggr|) Magnitude of GGR (log-scaled)
ggr_signed_log1p sign(ggr) × log1p(|ggr|) Signed log; preserves positive/negative
ggr_positive max(ggr, 0) House-winning portion only
ggr_negative_abs max(−ggr, 0) Player-winning magnitude

Recency features

Key churn signal. No-bet players are treated as maximally stale (filled with 90).

Feature Formula Interpretation
days_since_last_bet_rate days_since_last_bet / 90 Normalised staleness [0, 1]
last_bet_in_last_7_days days_since_last_bet ≤ 7 → 1 Binary: very recent activity
last_bet_in_last_14_days days_since_last_bet ≤ 14 → 1 Binary: recent activity
last_bet_in_last_30_days days_since_last_bet ≤ 30 → 1 Binary: active in last month

Frequency / rate features

Feature Formula Interpretation
bets_per_active_day live_bets_count / days_active_in_observation Betting intensity on active days
bets_per_observation_day live_bets_count / 90 Average daily bet count over full period
bets_per_tenure_day live_bets_count / tenure_days Lifetime betting rate
turnover_per_active_day total_turnover / days_active_in_observation Avg daily spend on active days
turnover_per_observation_day total_turnover / 90 Avg daily spend over full period
payout_per_observation_day total_payout / 90 Avg daily payout over full period

Per-bet metrics

Feature Formula Interpretation
payout_per_bet total_payout / live_bets_count Average return per bet
ggr_per_bet ggr / live_bets_count Average house margin per bet
ggr_per_active_day ggr / days_active_in_observation Daily value to the house
payout_rate total_payout / total_turnover Complement of ggr_margin; ≈ 1 − ggr_margin

Activity span

Derived from the gap between first_live_bet_date and last_live_bet_date within the observation period.

Feature Formula Interpretation
activity_span_days (last_live_bet_datefirst_live_bet_date).days Days between first and last bet
activity_span_rate activity_span_days / 90 Span as fraction of the period
bet_consistency_rate days_active_in_observation / (activity_span_days + 1) How densely active within their betting window
avg_days_between_bets activity_span_days / live_bets_count Average gap between consecutive bets
days_to_first_bet_in_obs (first_live_bet_dateobservation_start_date).days How far into the period they started (no-bet → 90)

Deposit features

Feature Formula Interpretation
avg_deposit_amount total_deposit_amount / deposit_count Average deposit size
deposit_frequency_rate deposit_count / 90 Deposits per day
deposit_per_bet deposit_count / live_bets_count Deposit frequency relative to betting activity
avg_deposit_to_avg_bet_ratio avg_deposit / avg_bet Whether deposits are large relative to stakes
has_deposited deposit_count > 0 → 1 Binary flag
deposit_coverage_rate total_payout / total_deposit_amount > 1 means player is net winning vs. deposits
ggr_to_deposit_ratio ggr / total_deposit_amount House take relative to player's investment
turnover_per_deposit total_turnover / total_deposit_amount Wagering multiplier (inverse of deposit_to_turnover_ratio)

Tenure & date features

Feature Formula Interpretation
active_to_tenure_ratio days_active_in_observation / tenure_days Engagement relative to account age
is_new_player tenure_days < 90 → 1 Player registered during the observation period
tenure_years tenure_days / 365 Account age in years
days_to_first_bet (first_live_bet_dateregistration_date).days Days from registration to first bet in obs period
registration_day_of_week registration_date.dayofweek 0 = Monday … 6 = Sunday
registration_month registration_date.month 1–12
registration_year registration_date.year Calendar year
last_bet_day_of_week last_live_bet_date.dayofweek −1 sentinel for no-bet players
last_bet_month last_live_bet_date.month −1 sentinel for no-bet players
first_bet_day_of_week first_live_bet_date.dayofweek −1 sentinel for no-bet players
first_bet_month first_live_bet_date.month −1 sentinel for no-bet players

Interaction features

Multiplicative cross-terms to expose non-linear relationships between volume, frequency, and recency.

Feature Formula Rationale
turnover_x_bet_day_rate total_turnover × bet_day_rate High-value + high-frequency players
ggr_margin_x_bet_day_rate ggr_margin × bet_day_rate House margin weighted by activity rate
days_since_last_bet_x_bet_day_rate days_since_last_bet × bet_day_rate Recent lapse vs. historically frequent player
tenure_x_bet_day_rate tenure_days × bet_day_rate Veteran × engagement level
avg_bet_amount_x_bet_day_rate avg_bet_amount × bet_day_rate High-stakes × frequency signal
turnover_x_ggr_margin total_turnover × ggr_margin Absolute GGR (alternative to raw ggr)
active_days_x_avg_bet_amount days_active_in_observation × avg_bet_amount Total potential value proxy

Net position features

Feature Formula Interpretation
net_loss total_deposit_amounttotal_payout Player's net cash lost (positive = lost money)
net_loss_signed_log1p sign(net_loss) × log1p(|net_loss|) Signed log; preserves winning/losing direction
net_loss_rate net_loss / total_deposit_amount Net loss as fraction of total deposits

NaN handling

Situation Affected base column Handling in extra features
No bets (live_bets_count = 0) avg_bet_amount, days_since_last_bet avg_bet_amount filled with 0; days_since_last_bet filled with 90; per-bet/per-active-day features → NaN
No deposits (deposit_count = 0) total_deposit_amount (may be 0 or NaN) Deposit-ratio features → NaN
Zero turnover ggr_margin, deposit_to_turnover_ratio Propagated as NaN
No-bet players, date fields first_live_bet_date, last_live_bet_date Bet date features → −1 sentinel; days_to_first_bet_in_obs filled with 90

Downstream models should apply appropriate imputation (e.g. median imputation or model-native NaN handling such as XGBoost's built-in missing value treatment).