6.3.3. Bank Telemarketing Upsell Campaign#

6.3.3.1. Summary#

This dataset is related to a direct marketing campaigns (phone calls) of a Portuguese banking institution. The marketing campaigns were based on phone calls. Often, more than one contact to the same client was required, in order to access if the product (bank term deposit) would be or not subscribed.

Features recorded before the contact event are removed from the original dataset [1] to avoid data leakage. Only clients with a positive balance are considered, since clients in debt are not eligible for term deposits.

Classes

2

Subscribers

4787

Non-subscribers

33144

Samples

37931

Features

10

Other relevant information can be found in [2] and [3].

6.3.3.2. Using the Dataset#

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

  • data: the feature matrix

  • target: the target vector

  • cost_matrix: a CostMatrix with default values pre-filled

  • instance_costs: a dict of per-instance cost drivers ('balance')

  • 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.

The cost matrix is symbolic: only the customer balance varies per instance, while the interest rate, term deposit fraction and contact cost are parameters with defaults. Pass the cost matrix to the model as a Metric loss, and hand it the instance costs at fit time:

import pandas as pd
from empulse.datasets import load_upsell_bank_telemarketing
from empulse.metrics import Metric, Cost
from empulse.models import CSLogitClassifier
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, TargetEncoder

dataset = load_upsell_bank_telemarketing(backend=pd)
X, y = dataset.data, dataset.target

numeric = X.select_dtypes(include=['number']).columns
categorical = X.select_dtypes(exclude=['number']).columns

pipeline = Pipeline([
    ('preprocessor', ColumnTransformer([
        ('num', StandardScaler(), numeric),
        ('cat', TargetEncoder(), categorical),
    ])),
    ('model', CSLogitClassifier(loss=Metric(dataset.cost_matrix, Cost())))
])
pipeline.fit(X, y, model__balance=dataset.instance_costs['balance'])

6.3.3.3. Cost Matrix#

Actual positive \(y_i = 1\)

Actual negative \(y_i = 0\)

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

tp_cost \(= c\)

fp_cost \(= c\)

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

fn_cost \(= r \cdot d_i \cdot b_i\)

tn_cost \(= 0\)

with
  • \(c\) : cost of contacting the client

  • \(r\) : interest rate of the term deposit

  • \(d_i\) : fraction of the client’s balance that is deposited in the term deposit

  • \(b_i\) : client’s balance

Using default parameters, it is assumed that \(c = 1\), \(r = 0.02463333\), \(d_i = 0.25\) for all clients. The default parameters are based on [4].

These assumptions are symbolic parameters of the cost matrix, exposed under the aliases interest_rate, term_deposit_fraction and contact_cost. Override any of them by passing the alias when evaluating the metric:

import numpy as np
import pandas as pd
from empulse.datasets import load_upsell_bank_telemarketing
from empulse.metrics import Metric, Cost

dataset = load_upsell_bank_telemarketing(backend=pd)

# replace with your own model's predicted probabilities
y_score = np.random.default_rng(0).uniform(size=len(dataset.target))

cost = Metric(dataset.cost_matrix, Cost())
score = cost(
    dataset.target,
    y_score,
    interest_rate=0.05,
    term_deposit_fraction=0.30,
    contact_cost=10,
    **dataset.instance_costs,
)

6.3.3.4. Data Description#

Variable Name

Description

Type

age

Age of the client

numeric

balance

Average yearly balance

numeric

previous

Number of contacts performed before this campaign and for this client

numeric

job

Type of job (e.g., ‘admin.’, ‘blue-collar’, ‘entrepreneur’, etc.)

categorical

marital

Marital status (‘divorced’, ‘married’, ‘single’)

categorical

education

Education level (‘primary’, ‘secondary’, ‘tertiary’, ‘unknown’)

categorical

has_credit_in_default

Has credit in default? (‘yes’ = 1, ‘no’ = 0)

binary

has_housing_loan

Has housing loan? (‘yes’ = 1, ‘no’ = 0)

binary

has_personal_loan

Has personal loan? (‘yes’ = 1, ‘no’ = 0)

binary

previous_outcome

Outcome of the previous marketing campaign (‘success’, ‘failure’, ‘other’, ‘unknown’)

categorical

subscribed

Has the client subscribed a term deposit? (‘yes’ = 1, ‘no’ = 0)

binary

6.3.3.5. References#