Loading...

Telco Churn

This example uses the Telco Churn dataset to predict which Telco user is likely to churn; that is, customers that will likely stop using Telco. You can download the Jupyter Notebook of the study here.

  • Churn: customers that left within the last month.

  • Services: services of each customer (phone, multiple lines, internet, online security, online backup, device protection, tech support, and streaming TV and movies).

  • Customer account information: how long they’ve been a customer, contract, payment method, paperless billing, monthly charges, and total charges.

  • Customer demographics: gender, age range, and if they have partners and dependents.

We will follow the data science cycle (Data Exploration - Data Preparation - Data Modeling - Model Evaluation - Model Deployment) to solve this problem.

Initialization

This example uses the following version of VerticaPy:

import verticapy as vp

vp.__version__
Out[2]: '1.1.0'

Connect to Vertica. This example uses an existing connection called VerticaDSN . For details on how to create a connection, see the Connection tutorial. You can skip the below cell if you already have an established connection.

vp.connect("VerticaDSN")

Let’s create a Virtual DataFrame of the dataset. The dataset is available here.

churn = vp.read_csv("customers.csv")

Let’s take a look at the first few entries in the dataset.

churn.head(10)
Abc
customerID
Varchar(20)
100%
...
123
TotalCharges
Numeric(9,3)
99%
010
Churn
Boolean
100%
10002-ORFBO...593.3
❌
20003-MKNFE...542.4
❌
30004-TLHLJ...280.85
✅
40017-DINOC...2460.55
❌
50020-JDNXP...1993.2
❌
60022-TCJCI...2791.5
✅
70023-XUOPT...1215.6
✅
80040-HALCW...1090.6
❌
90042-JVWOJ...471.85
❌
100064-SUDOG...224.5
❌

Data Exploration and Preparation

Let’s examine our data.

churn.describe(method = "categorical", unique = True)
...
top_percent
unique
"customerID"...0.0147043.0
"gender"...50.4762.0
"SeniorCitizen"...83.7852.0
"Partner"...51.6972.0
"Dependents"...70.0412.0
"tenure"...8.70473.0
"PhoneService"...90.3172.0
"MultipleLines"...48.1333.0
"InternetService"...43.9593.0
"OnlineSecurity"...49.6663.0
"OnlineBackup"...43.8453.0
"DeviceProtection"...43.9443.0
"TechSupport"...49.3113.0
"StreamingTV"...39.8983.0
"StreamingMovies"...39.5433.0
"Contract"...55.0193.0
"PaperlessBilling"...59.2222.0
"PaymentMethod"...33.5794.0
"MonthlyCharges"...0.8661585.0
"TotalCharges"...0.1566530.0
"Churn"...73.4632.0

Several variables are categorical, and since they all have low cardinalities, we can compute their dummies. We can also convert all booleans to numeric.

for column in [
    "DeviceProtection",
    "MultipleLines",
    "PaperlessBilling",
    "Churn",
    "TechSupport",
    "Partner",
    "StreamingTV",
    "OnlineBackup",
    "Dependents",
    "OnlineSecurity",
    "PhoneService",
    "StreamingMovies",
]:
    churn[column].decode("Yes", 1, 0)
churn.one_hot_encode().drop(
    [
        "customerID",
        "gender",
        "Contract",
        "PaymentMethod",
        "InternetService",
    ],
)
123
SeniorCitizen
Int
100%
...
123
Partner
Integer
100%
123
PaymentMethod_Electronic_check
Bool
100%
10...10
20...00
30...01
40...00
50...10
61...00
70...11
80...10
90...00
100...10
110...00
120...01
130...10
141...01
150...00
160...10
171...10
180...11
190...10
200...01

Let’s compute the correlations between the different variables and the response column.

churn.corr(focus = "Churn")

Many features have a strong correlation with the Churn variable. For example, the customers that have a Month to Month contract are more likely to churn. Having this type of contract gives customers a lot of flexibility and allows them to leave at any time. New customers are also likely to churn.

# No lock-in = Churn
churn.barh(["Contract_Month-to-month", "tenure"], method = "avg", of = "Churn", height = 500)

The following scatter plot shows that providing better tariff plans can prevent churning. Indeed, customers having high total charges are more likely to churn even if they’ve been with the company for a long time.

churn.scatter(["TotalCharges", "tenure"], by = "Churn")

Let’s move on to machine learning.


Machine Learning

LogisticRegression is a very powerful algorithm and we can use it to detect churns. Let’s split our vDataFrame into training and testing set to evaluate our model.

train, test = churn.train_test_split(
    test_size = 0.2,
    random_state = 0,
)

Let’s train and evaluate our model.

from verticapy.machine_learning.vertica import LogisticRegression

model = LogisticRegression(
    penalty = "L2",
    tol = 1e-6,
    max_iter = 1000,
    solver = "BFGS",
)
model.fit(
    train,
    churn.get_columns(exclude_columns = ["churn"]),
    "churn",
    test,
)
model.classification_report()
value
auc0.8419292283921654
prc_auc0.6496481202189804
accuracy0.8022678951098512
log_loss0.178354728672812
precision0.6447368421052632
recall0.5340599455040872
f1_score0.5842026825633383
mcc0.45947139005227444
informedness0.43061166964201814
markedness0.49026529738981606
csi0.4126315789473684

The model is excellent! Let’s run some machine learning on the entire dataset and compute the importance of each feature.

model.drop()
model.fit(
    churn,
    churn.get_columns(exclude_columns = ["churn"]),
    "churn",
)
model.features_importance()

Based on our model, most churning customers are at least one of the following:

  • Paying higher bills

  • New Telco customers

  • Have a monthly contract

Notice that customers have a Fiber Optic option are also likely to churn. Let’s check if this relationship is causal by computing some aggregations.

import verticapy.sql.functions as fun

# Is Fiber optic a Bad Option? - VerticaPy
churn.groupby(
    ["InternetService_Fiber_optic"],
    [
        fun.avg(churn["tenure"])._as("tenure"),
        fun.avg(churn["totalcharges"])._as("totalcharges"),
        fun.avg(churn["contract_month-to-month"])._as("contract_month_to_month"),
        fun.avg(churn["monthlycharges"])._as("monthlycharges"),
    ]
)
123
InternetService_Fiber_optic
Integer
100%
...
123
contract_month_to_month
Float(22)
100%
123
monthlycharges
Float(22)
100%
10...0.44261464403344343.7882442361287
21...0.6873385012919991.5001291989664

It seems like the Fiber Optic option in and of itself doesn’t lead to churning, but customers that have this option tend to churn because their contract puts them into one of the three categories we listed before: they’re paying more.

To retain these customers, we’ll need to make some changes to what types of contracts we offer.

We’ll use a lift chart to help us identify which of our customers are likely to churn.

model.lift_chart()

By targeting less than 30% of the entire distribution, our predictions will be more than three times more accurate than the other 70%.

Conclusion

We’ve solved our problem in a Pandas-like way, all without ever loading data into memory!