Customer Churn Prediction for Telecom Companies

Translated from the original Portuguese article. Ler o original em português

Cover of the article Customer Churn Prediction for Telecom Companies

Figure 1 of the article Customer Churn Prediction for Telecom Companies

Telecommunications service providers routinely analyze customer dissatisfaction, always tracking whether their customers are satisfied or not. These analyses are among the key metrics for retaining customers and acquiring new ones. Companies in this sector usually have customer service departments, and one of their main tasks is trying to win back lost customers, who can cost much more than new ones.

Because of this, companies invest in new technologies to carry out these analyses, commonly relying on the Churn rate, or simply Churn, one of the ways to measure customer attrition. Besides measuring this attrition rate, Churn analysis helps anticipate future cancellations, producing predictions that support decision-making and targeted actions for these customers. A familiar everyday example is streaming companies such as Spotify and Netflix.

Because it is such an important metric, Carlos Melo, in his Data Science na Prática course, gave us a challenge: implement a churn solution from scratch for a telecommunications company using real data. The data used in this project was originally made available on the IBM Developer learning platform. It represents a typical problem faced by telecom companies, and the full dataset can be found at this link.

The data provided by IBM doesn't come with explicit documentation, but fortunately the column names make the problem easy to understand, as we can see in the first five entries below, from a dataset of 7043 entries and 21 columns.

customerID gender SeniorCitizen Partner Dependents tenure PhoneService MultipleLines InternetService OnlineSecurity OnlineBackup DeviceProtection TechSupport StreamingTV StreamingMovies Contract PaperlessBilling PaymentMethod MonthlyCharges TotalCharges Churn
0 7590-VHVEG Female 0 Yes No 1 No No phone service DSL No Yes No No No No Month-to-month Yes Electronic check 29.85 29.85 No
1 5575-GNVDE Male 0 No No 34 Yes No DSL Yes No Yes No No No One year No Mailed check 56.95 1889.5 No
2 3668-QPYBK Male 0 No No 2 Yes No DSL Yes Yes No No No No Month-to-month Yes Mailed check 53.85 108.15 Yes
3 7795-CFOCW Male 0 No No 45 No No phone service DSL Yes No Yes Yes No No One year No Bank transfer (automatic) 42.30 1840.75 No
4 9237-HQITU Female 0 No No 2 Yes No Fiber optic No No No No No No Month-to-month Yes Electronic check 70.70 151.65 Yes

As the first step of any Data Science project, a good exploratory analysis is essential. So I ran the analyses I considered most important, starting with an assessment of the unique values.

This assessment showed that most of the columns in question have more than two categories, which means some processing is needed later on before feeding the model. Other checks were also necessary, such as identifying outliers, computing descriptive statistics, and checking for bias.

SeniorCitizen tenure MonthlyCharges TotalCharges
count 7043.000000 7043.000000 7043.000000 7043.000000
mean 0.162147 32.371149 64.761692 2281.916928
std 0.368612 24.559481 30.090047 2265.270398
min 0.000000 0.000000 18.250000 18.800000
25% 0.000000 9.000000 35.500000 402.225000
50% 0.000000 29.000000 70.350000 1397.475000
75% 0.000000 55.000000 89.850000 3786.600000
max 1.000000 72.000000 118.750000 8684.800000

However, the descriptive statistics and the outlier check didn't reveal much. So I took a closer look at the Tenure column, which represents how long each customer has used the service and can indicate "service loyalty". I assumed that the time unit of Tenure is months.

Figure 2 of the article Customer Churn Prediction for Telecom Companies

Note that tenure drops from the first month onward, rises again in the last months (70 to 72), but otherwise the data looks fairly regular. Of all the checks mentioned above, I'll highlight the presence of bias, since the others didn't show anything that could compromise the model.

The bias analysis covered the gender, Partner and Churn columns. However, only the last one showed a problematic imbalance, so it had to be balanced, as you can see below:

Figure 3 of the article Customer Churn Prediction for Telecom Companies

As for the other columns, some of their unique values could be treated as the same thing, especially in the service columns. Even though this assumption is perfectly valid, in this work I'll keep them as separate values.

At this stage I performed some transformations to prepare the data for building a good model. First came the preprocessing, using LabelEncoder and get_dummies to convert the data into binary variables, putting each class in its own column with a distinct numeric value, as shown below:

gender SeniorCitizen Partner Dependents tenure PhoneService PaperlessBilling MonthlyCharges TotalCharges Churn MultipleLines_No MultipleLines_No phone service MultipleLines_Yes InternetService_DSL InternetService_Fiber optic InternetService_No OnlineSecurity_No OnlineSecurity_No internet service OnlineSecurity_Yes OnlineBackup_No OnlineBackup_No internet service OnlineBackup_Yes DeviceProtection_No DeviceProtection_No internet service DeviceProtection_Yes TechSupport_No TechSupport_No internet service TechSupport_Yes StreamingTV_No StreamingTV_No internet service StreamingTV_Yes StreamingMovies_No StreamingMovies_No internet service StreamingMovies_Yes Contract_Month-to-month Contract_One year Contract_Two year PaymentMethod_Bank transfer (automatic) PaymentMethod_Credit card (automatic) PaymentMethod_Electronic check PaymentMethod_Mailed check
0 0 0 1 0 1 0 1 29.85 29.85 0 0 1 0 1 0 0 1 0 0 0 0 1 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0 0 1 0
1 1 0 0 0 34 1 0 56.95 1889.50 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 1 0 0 1 0 0 0 1 0 0 0 0 1
2 1 0 0 0 2 1 1 53.85 108.15 1 1 0 0 1 0 0 0 0 1 0 0 1 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0 0 0 1
3 1 0 0 0 45 0 0 42.30 1840.75 0 0 1 0 1 0 0 0 0 1 1 0 0 0 0 1 0 0 1 1 0 0 1 0 0 0 1 0 1 0 0 0
4 0 0 0 0 2 1 1 70.70 151.65 1 1 0 0 0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0 0 1 0

With that, we can generate the correlation matrix of the data.

Figure 4 of the article Customer Churn Prediction for Telecom Companies

With all of this done, we can move on to the preprocessing and define our performance metric. For error estimation I used Cross-Validation, which I've already discussed in another article, combined with the recall metric to find the best model for this problem.

To make model evaluation easier, I defined a validation function:

#Função de validação

def valid(X, y, model, quite = False):

  X = np.array(X)
  y = np.array(y)
  pipeline = make_pipeline(StandardScaler(), model)
  scores = cross_val_score(pipeline, X, y, scoring="recall")

  if quite == False:
     print("Recall: {:.3f} (+/- {:.3f})".format(scores.mean(), scores.std()))

  return scores.mean()

It returns the recall value and the score. I then built a comparison baseline to serve as a reference for evaluating the models I used.

#Criando linha base
rfc = RandomForestClassifier()
score_baseline = valid(X_treino, y_treino, rfc)

This resulted in Recall: 0.485 (+/- 0.022). The balancing technique was based on Under Sampling, and, following recommendations from the literature, the data is standardized before applying it, and only on the training set.

Since I didn't know which model would perform best, I ran cross-validation for the following models:

  • Random Forest
  • Decision Trees
  • Stochastic Gradient Descent
  • SVC
  • Logistic Regression
  • LightGBM

I then instantiated and configured them as shown below:

#Instanciar os modelos
rc = RandomForestClassifier()
ad = DecisionTreeClassifier()
gde = SGDClassifier()
svc = SVC()
rl = LogisticRegression()
xgb = XGBClassifier()
lgbm = LGBMClassifier()
#Criar lista para resultados
modelo = []
recall = []
#Avaliar desempenho
for clf in (rc, ad, gde, svc, rl, xgb, lgbm):
  modelo.append(clf.__class__.__name__)
  recall.append(valid(X_treino_rus, y_treino_rus, clf, quite = True))

Recall = pd.DataFrame(data = recall, index=modelo, columns=["Recall"])

This produced the following results:

Recall
LogisticRegression 0.803375
SVC 0.793548
XGBClassifier 0.792170
LGBMClassifier 0.769701
RandomForestClassifier 0.752854
SGDClassifier 0.707198
DecisionTreeClassifier 0.701591

As the results show, XGBoost, Logistic Regression and SVC stood out, all with their default settings. So I took the best model from the previous step (XGBoost) and tuned its parameters to get the best possible result; you can find this in the full project.

After evaluating a few XGBoost configurations, I found the best result to be:

#Modelo Final
xgb=XGBClassifier(learning_rate=0.001,n_estimators=50,max_depth=1,min_child_weight=1,gamma=0.0)xgb.fit(X_treino_rus,y_treino_rus)
XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1,
              colsample_bynode=1, colsample_bytree=1, gamma=0.0,
              learning_rate=0.001, max_delta_step=0, max_depth=1,
              min_child_weight=1, missing=None, n_estimators=50, n_jobs=1,
              nthread=None, objective='binary:logistic', random_state=0,
              reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=None,
              silent=None, subsample=1, verbosity=1)

This gave the following prediction results:

              precision    recall  f1-score   support

           0       0.94      0.57      0.71      1316
           1       0.41      0.89      0.56       445

    accuracy                           0.65      1761
   macro avg       0.67      0.73      0.64      1761
weighted avg       0.80      0.65      0.67      1761

Área sobre a curva ASC: 0.7284109149277687

And the confusion matrix:

Figure 5 of the article Customer Churn Prediction for Telecom Companies

The result is an excellent model, with a true positive rate of 0.89 and a true negative rate of 0.57, and an AUC of 0.72.

In this project, we worked on a churn prediction problem whose main goal was to build a machine learning model capable of correctly identifying as many likely churners as possible. To get there, several steps were needed, such as balancing and standardization, and several models were tested to find the best result, finally arriving at an XGBoost model with an excellent result.

Keep reading

All articles

Credit Card Fraud Detection

Credit card fraud is one of the main concerns of financial institutions. In this post, I explore a dataset of European card transactions and build a …