6.3.2. Churn in a TV Subscription Company#

6.3.2.1. Summary#

This is a private dataset provided by a TV cable provider [1]. The dataset consists of active customers during the first semester of 2014. The dataset as shipped contains 9,379 individual registries, each one with 46 attributes, including a churn label indicating whenever a customer is a churner. This label was created internally in the company, and can be regarded as highly accurate. In the dataset only 449 customers are churners, leading to a churn ratio of 4.79 %.

The features names are anonymized to protect the privacy of the customers.

Classes

2

Churners

449

Non-churners

8930

Samples

9379

Features

46

6.3.2.2. Using the Dataset#

The dataset can be loaded through the load_churn_tv_subscriptions function. This returns a Dataset object with the following attributes:

  • data: the feature matrix

  • target: the target vector

  • cost_matrix: a CostMatrix over the four outcome terms

  • instance_costs: a dict of per-instance cost arrays ('tp_cost', 'tn_cost', 'fp_cost', 'fn_cost')

  • feature_names: the feature names

  • target_names: the target names

  • DESCR: the full description of the dataset

The backend argument selects the dataframe library used for data and target. Pass the module itself — backend=pd for pandas or backend=pl for polars.

Unlike the other churn datasets, this one ships precomputed costs: its cost matrix is not built from symbolic business parameters, so each of the four outcome terms is supplied directly as an array in instance_costs. Those arrays are already costs, so they can be passed straight through to the plain cost arguments of the models:

import pandas as pd
from empulse.datasets import load_churn_tv_subscriptions
from empulse.models import CSLogitClassifier
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

dataset = load_churn_tv_subscriptions(backend=pd)
X, y = dataset.data, dataset.target
costs = dataset.instance_costs

pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('model', CSLogitClassifier())
])
pipeline.fit(
    X,
    y,
    model__tp_cost=costs['tp_cost'],
    model__tn_cost=costs['tn_cost'],
    model__fp_cost=costs['fp_cost'],
    model__fn_cost=costs['fn_cost'],
)

To evaluate a model instead, wrap the cost matrix in a Metric and pass the instance costs straight through:

from empulse.metrics import Metric, Cost

cost = Metric(dataset.cost_matrix, Cost())
y_score = pipeline.predict_proba(X)[:, 1]
score = cost(y, y_score, **dataset.instance_costs)

6.3.2.3. Cost Matrix#

Actual positive \(y_i = 1\)

Actual negative \(y_i = 0\)

Predicted positive \(\hat{y}_i = 1\)

tp_cost \(= \gamma_i d_i + (1 - \gamma_i) (CLV_i + c_i)\)

fp_cost \(= d_i + c_i\)

Predicted negative \(\hat{y}_i = 0\)

fn_cost \(= CLV_i\)

tn_cost \(= 0\)

with
  • \(\gamma_i\) : probability of the customer accepting the retention offer

  • \(CLV_i\) : customer lifetime value of the retained customer

  • \(d_i\) : cost of incentive offered to the customer

  • \(c_i\) : cost of contacting the customer

6.3.2.4. References#