
Introdução
Nos últimos 10 a 15 anos, com o avanço das tecnologias digitais, as estratégias de marketing mudaram bastante. Grandes marcas e nichos menores passaram a coletar uma enorme quantidade de dados sobre transações, compras, preferências, poder de compra, comportamento, dados demográficos, avaliações e muito mais. Esses dados ajudam o marketing a entender o comportamento do cliente em diferentes etapas — da intenção de compra até a conversão e a fidelização. É aí que entra o potencial da data science.
A data science transforma big data de marketing em insights acionáveis, mesmo quando eles não são tão intuitivos à primeira vista — como padrões de consumo pouco evidentes e coocorrências. Com isso, profissionais de marketing conseguem enxergar melhor seu público-alvo, atrair e reter clientes, otimizar estratégias, aumentar a visibilidade da empresa, criar campanhas mais eficientes, abrir novos canais e, como consequência, maximizar a receita.
Um dos casos de uso mais comuns de data science no marketing é a previsão de churn de clientes. Vamos aprofundar esse tema.
Uso de data science em marketing: previsão de churn de clientes
Churn é a tendência de clientes cancelarem a assinatura de um serviço que utilizavam e, portanto, deixarem de ser clientes. A taxa de churn é o percentual de clientes que saíram em um intervalo de tempo definido. É o oposto da taxa de crescimento de clientes, que acompanha novos clientes.
A taxa de churn é um indicador importante de satisfação do cliente e da saúde do negócio como um todo. Além do churn natural, que sempre ocorre, ou do churn sazonal, típico de alguns serviços, existem fatores que podem indicar que algo na empresa não vai bem e precisa ser ajustado. Entre eles:
- ausência ou baixa qualidade do suporte ao cliente;
- experiências negativas do cliente;
- migração para um concorrente com melhores condições ou preços;
- mudança de prioridades dos clientes;
- clientes de longa data deixam de se sentir satisfeitos;
- o serviço não atendeu às expectativas;
- problemas financeiros;
- bloqueios por proteção antifraude nos pagamentos.
Uma taxa de churn alta é um problema sério para qualquer empresa pelos seguintes motivos:
- Ela está diretamente relacionada à perda de receita.
- Custa muito mais adquirir novos clientes do que reter os atuais — especialmente em mercados altamente competitivos.
- Se o churn ocorre por mau atendimento, a reputação pode ser bastante afetada por avaliações negativas em redes sociais e sites de review.
Reter clientes é fundamental para qualquer serviço baseado em assinatura. Para prever a taxa de churn e adotar medidas preventivas, é necessário coletar e analisar informações sobre o comportamento do cliente (intervalos de compra, tempo total como cliente, cancelamentos, contatos de follow-up, atividade online) e descobrir quais atributos e combinações caracterizam clientes com risco de sair. Saber com antecedência quem pode churnar em breve — principalmente clientes de alto valor ou de longa data — permite focar neles e criar estratégias eficientes para tentar mantê-los. A abordagem pode incluir uma ligação com oferta especial, desconto, upgrade de plano pelo mesmo preço ou outra experiência personalizada.
Tecnicamente, prever churn é um problema clássico de classificação em machine learning, no qual os clientes são rotulados como "sim" ou "não" quanto ao risco de churn. Vamos investigar esse caso em Python com dados reais.
Vamos modelar churn no contexto de telecom, em que clientes podem ter múltiplos serviços com uma operadora sob um contrato principal. O dataset traz atributos de atividade dos clientes (já limpos) e um rótulo de churn indicando se o cliente saiu ou não.
Vamos olhar os dados e explorar a distribuição do churn:
import pandas as pd
telcom = pd.read_csv('telco.csv')
print(f'Number of customers: {telcom.shape[0]:,}\n'
f'Churn values: {set(telcom['Churn'])}\n\n'
f'Churn distribution, %:\n{round(telcom.groupby(['Churn']).size()/telcom.shape[0]*100).convert_dtypes()}')
Number of customers: 7,032
Churn values: {0, 1}
Churn distribution, %:
Churn
0 73
1 27
dtype: float64
27% dos clientes churnaram — uma taxa relativamente alta. Diferente do caso de uso anterior, porém, este dataset não parece sofrer de um desequilíbrio severo de classes.
Agora vamos pré-processar os dados para aplicar técnicas de machine learning na previsão de churn. Isso inclui dividir em conjuntos de treino e teste, além de separar variáveis de entrada e a variável-alvo:
from sklearn.model_selection import train_test_split
target = ['Churn']
custid = ['customerID']
cols = [col for col in telcom.columns if col not in custid + target]
X = telcom[cols]
y = telcom[target]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)
O primeiro algoritmo que vamos usar para prever o rótulo de churn e estimar a acurácia é a regressão logística:
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
lr = LogisticRegression()
lr.fit(X_train, y_train)
predictions = lr.predict(X_test)
print(f'Test accuracy: {round(accuracy_score(y_test, predictions), 4)}')
Test accuracy: 0.8009
Em seguida, vamos adicionar mais uma funcionalidade ao modelo de regressão logística: rodá-lo com regularização L1 para fazer seleção de variáveis junto com o ajuste do modelo. Valores diferentes do parâmetro C (inverso da força de regularização) afetam a acurácia. Por enquanto, vamos definir C como 0,025:
lr = LogisticRegression(penalty='l1', C=0.025, solver='liblinear')
lr.fit(X_train, y_train)
predictions = lr.predict(X_test)
print(f'Test accuracy: {round(accuracy_score(y_test, predictions), 4)}')
Test accuracy: 0.7969
Agora vamos ajustar o parâmetro C da regularização L1 para encontrar o valor ideal que reduza a complexidade do modelo, mantendo bons indicadores de performance. Para isso, vamos iterar por diferentes valores de C, treinar instâncias da regressão logística e calcular as métricas.
A lista C foi criada previamente com os valores possíveis do parâmetro. O array l1_metrics foi construído com 3 colunas: a primeira com os valores de C, as demais como placeholders para a contagem de coeficientes não nulos e para a acurácia do modelo. Vamos testar:
C Non-Zero Coeffs Accuracy
0 1.0000 23.0 0.801479
1 0.5000 22.0 0.799204
2 0.2500 21.0 0.802048
3 0.1000 20.0 0.802617
4 0.0500 18.0 0.802048
5 0.0250 13.0 0.796928
6 0.0100 5.0 0.790102
7 0.0050 3.0 0.783276
8 0.0025 2.0 0.745734
Percebemos que valores menores de C reduzem o número de coeficientes diferentes de zero (ou seja, de variáveis do modelo), diminuindo a complexidade — mas também derrubam a acurácia. C igual a 0,05 parece ser o ponto ideal: reduz as variáveis para 18 e entrega acurácia ligeiramente superior à do modelo sem regularização.
Agora vamos testar outro algoritmo — a árvore de decisão:
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier()
clf.fit(X_train, y_train)
predictions = clf.predict(X_test)
print(f'Test accuracy: {round(accuracy_score(y_test, predictions), 4)}')
Test accuracy: 0.7275
Para obter um modelo mais preciso evitando overfitting, podemos ajustar a profundidade da árvore (parâmetro max_depth) e identificar o valor ideal. Tecnicamente, o processo é semelhante ao ajuste do C na regressão logística: vamos iterar por múltiplos valores de max_depth, treinar a árvore e calcular as métricas.
A lista depth_list foi criada previamente com valores possíveis para o parâmetro. O array depth_tuning tem 2 colunas: a primeira com as profundidades candidatas e a segunda como placeholder para a acurácia. Vamos aplicar essa abordagem e encontrar a profundidade ideal:
depth_list = list(range(2, 15))
depth_tuning = np.zeros((len(depth_list), 2))
depth_tuning[:, 0] = depth_list
for index in range(len(depth_list)):
clf = DecisionTreeClassifier(max_depth=depth_list[index])
clf.fit(X_train, y_train)
predictions = clf.predict(X_test)
depth_tuning[index, 1] = accuracy_score(y_test, predictions)
col_names = ['Max_Depth', 'Accuracy']
print(pd.DataFrame(depth_tuning, columns=col_names))
Max_Depth Accuracy
0 2.0 0.756542
1 3.0 0.783276
2 4.0 0.782708
3 5.0 0.791809
4 6.0 0.778157
5 7.0 0.780432
6 8.0 0.757110
7 9.0 0.762230
8 10.0 0.763936
9 11.0 0.752560
10 12.0 0.745165
11 13.0 0.732651
12 14.0 0.727531
A acurácia aumenta com mais profundidade até certo ponto e depois cai. Com max_depth = 5, a árvore atinge a melhor acurácia, então podemos considerar esse o valor ideal.
Depois de identificar os melhores parâmetros para regressão logística e árvore de decisão, vamos reconstruir os modelos e identificar/interpretar os principais fatores que elevam ou reduzem o churn.
Para a regressão logística, vamos extrair e analisar os expoentes dos coeficientes resultantes:
# Reconstruindo o melhor modelo
lr = LogisticRegression(penalty='l1', C=0.05, solver='liblinear')
lr.fit(X_train, y_train)
predictions = lr.predict(X_test)
# Combinando nomes das variáveis e coeficientes em um dataframe
feature_names = pd.DataFrame(X_train.columns, columns=['Feature'])
log_coef = pd.DataFrame(np.transpose(lr.coef_), columns=['Coefficient'])
coefficients = pd.concat([feature_names, log_coef], axis=1)
# Calculando os expoentes dos coeficientes
coefficients['Exp_Coefficient'] = np.exp(coefficients['Coefficient'])
# Removendo coeficientes iguais a zero
coefficients = coefficients[coefficients['Coefficient']!=0]
print(coefficients.sort_values(by=['Exp_Coefficient']))
Feature Coefficient Exp_Coefficient
21 tenure -0.907750 0.403431
4 PhoneService_Yes -0.820517 0.440204
17 Contract_Two year -0.595271 0.551413
8 TechSupport_Yes -0.418254 0.658195
16 Contract_One year -0.414158 0.660896
5 OnlineSecurity_Yes -0.412228 0.662173
6 OnlineBackup_Yes -0.143100 0.866667
3 Dependents_Yes -0.039299 0.961463
7 DeviceProtection_Yes -0.017465 0.982687
11 PaperlessBilling_Yes 0.071389 1.073999
1 SeniorCitizen_Yes 0.097904 1.102857
19 PaymentMethod_Electronic check 0.188533 1.207477
22 MonthlyCharges 0.901454 2.463182
Vemos que a variável com maior impacto nas chances de churn é tenure. Em geral, expoentes menores que 1 reduzem as chances; maiores que 1 aumentam.
Para a árvore de decisão, vamos extrair e plotar as regras if-else:
# Reconstruindo o melhor modelo
clf = DecisionTreeClassifier(max_depth=5)
clf.fit(X_train, y_train)
predictions = clf.predict(X_test)
from sklearn import tree
import graphviz
# Exportando um objeto graphviz da árvore treinada
exported = tree.export_graphviz(decision_tree=clf,
out_file=None,
feature_names=cols,
precision=1,
class_names=['Not churn', 'Churn'],
filled=True)
graph = graphviz.Source(exported)
display(graph)

Obtivemos uma visualização clara da árvore de decisão, que pode ser interpretada como um conjunto de regras if-else a partir do topo. Novamente, tenure do cliente aparece como a variável mais importante para o churn. É possível aumentar a profundidade da árvore para gerar mais camadas e extrair insights de outras variáveis.
Como próximos passos, vale ajustar mais parâmetros, testar outras formas de dividir treino e teste, comparar diferentes algoritmos de machine learning e analisar outros tipos de métricas para avaliar a performance do modelo.
Se você quer se aprofundar em previsão de churn e outras aplicações de data science em marketing, este curso de Machine Learning for Marketing in Python é um ótimo ponto de partida.

