Analisi dei dati: quando il cliente se ne va. Come funziona l’algoritmo Random Forest.

Analisi dei dati: quando il cliente se ne va. Come funziona l’algoritmo Random Forest.

Condividi con i tuoi amici...

A cosa può essere dovuto l’abbandono dell’azienda da parte di un cliente?

In questo articolo impieghiamo come esempio il caso dei clienti della banca. I dati sono presi dal sito Kaggle.com

Quali dati raccogliere? Quali caratteristiche (attributi) del cliente pensiamo possano influire in qualche modo sulla sua scelta di abbandonare?

Se non ne siamo sicuri è bene raccogliere tutti quei dati che nel loro complesso siano in grado di descrivere in modo esaustivo il comportamento del cliente in relazione all’azienda.

Il dataset è una tabella le cui righe identificano i clienti e le colonne i loro attributi. Una particolare colonna conterrà l’attributo target che in questo caso ci dice se il cliente ha abbandonato la banca oppure no. Questo attributo ricopre un ruolo speciale rispetto agli altri in quanto lo scopo dell’analisi è quello di stabilire in che misura ogni altro attributo influisce su questo.

L’algoritmo appropriato da impiegare è il Random Forest. Infatti, intuitivamente si può capire che per valutare il peso di ogni attributo rispetto al target la procedura più diretta è quella di creare delle sottoclassi di dati omogenee rispetto ad un certo attributo e calcolare le probabilità per ogni classe di trovare un cliente che ha abbandonato. Confrontando queste probabilità per le diverse classi emergerà l’importanza o meno che ha quell’attributo rispetto all’evento dell’abbandono. Ulteriori divisioni in sottoclassi mostreranno l’importanza degli altri attributi. Chiaramente gli algoritmi che si avvalgono degli alberi decisionali operano in modo un po’ più complesso, ma l’idea di base è quella indicata. Al termine di questo articolo approfondiremo questo aspetto.

Una volta individuati gli attributi che più condizionano l’abbandono è possibile mostrare mediante un istogramma qual è l’intervallo dei loro valori per cui ci sono più clienti che abbandonano.

Un’altra analisi utile che si può eseguire mediante l’algoritmo del Random Forest è finalizzata a determinare se tra i clienti che non hanno ancora abbandonato ce ne sono alcuni che potrebbero farlo.

Nell’esempio scelto, gli attributi importanti da considerare sono i seguenti:

CreditScore: può avere un effetto sull’abbandono dei clienti, poiché è meno probabile che un cliente con un punteggio di credito più elevato lasci la banca.
Geography: l’ubicazione di un cliente può influire sulla sua decisione di lasciare la banca?
Gender: Il genere gioca un ruolo in un cliente che lascia la banca?
Age: I clienti più anziani hanno meno probabilità di lasciare la banca rispetto a quelli più giovani.
Tenure: si riferisce al numero di anni in cui il cliente è stato cliente della banca. Normalmente, i clienti più anziani sono più fedeli e meno propensi a lasciare una banca.
Balance: Saldo. Le persone con un saldo più elevato nei loro conti hanno meno probabilità di lasciare la banca rispetto a quelle con saldi inferiori.
NumOfProducts: si riferisce al numero di prodotti che un cliente ha acquistato tramite la banca.
HasCrCard: indica se un cliente dispone o meno di una carta di credito. Le persone con una carta di credito hanno meno probabilità di lasciare la banca.
IsActiveMember: i clienti attivi hanno meno probabilità di lasciare la banca.
EstimatedSalary: come per il saldo, le persone con stipendi più bassi potrebbero avere maggiori probabilità di lasciare la banca rispetto a quelle con stipendi più alti.
Exited: se il cliente ha lasciato o meno la banca.
Complain: il cliente ha un reclamo o meno.
Satisfaction Score: punteggio fornito dal cliente per la risoluzione del reclamo.
Card type: tipo di carta tenuta dal cliente.
Point earned: i punti guadagnati dal cliente per l’utilizzo della carta di credito.

Per prima cosa è necessario codificare i dati in modo che siano tutti valori numerici. A questo scopo useremo la libreria LabelEncoder

Il codice python che usiamo per l’analisi è il seguente:

import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt



def converti_variabili_categoriche(df):
    label_encoder = LabelEncoder()
    encoded_df = df.copy()
    for col in encoded_df.columns:
        if encoded_df[col].dtype == 'object':
           encoded_df[col] = label_encoder.fit_transform(encoded_df[col])
    return encoded_df

# Carica i dati dei clienti
df_clienti = pd.read_csv("db.csv")

#cancella le prime tre colonne ininfluenti
indici_colonne_da_elimimare = df_clienti.columns[:3]
df_clienti = df_clienti.drop(indici_colonne_da_elimimare, axis=1)

# Esegue la conversione delle variabili categoriche
encoded_df = converti_variabili_categoriche(df_clienti)

df = encoded_df

# Dividere tra attributi e variabile target
X = df.drop("Exited", axis=1)
y = df["Exited"]

# Creazione del modello Random Forest
model = RandomForestClassifier()

# Addestramento del modello
model.fit(X, y)


# Ottenere le importanze degli attributi
importances = model.feature_importances_

# Mostrare le importanze degli attributi

diz={}
for i, feature_name in enumerate(X.columns):
    diz[feature_name]=importances[i]
    #print(f"{feature_name}: {importances[i]}")

diz2 = sorted(diz.items(), key=lambda x: x[1], reverse=True)

# Stampa del dizionario ordinato
for elemento in diz2:
    print(elemento)

Questo codice genera una classifica degli attributi ordinata in modo decrescente rispetto all’importanza che essi hanno in relazione all’attributo target Exited

OUTPUT

[10000 rows x 18 columns]
(‘Complain’, 0.8174900252801776)
(‘Age’, 0.06451350046328722)
(‘NumOfProducts’, 0.043053184993102525)
(‘IsActiveMember’, 0.014157313820351868)
(‘Balance’, 0.013263489393683532)
(‘CreditScore’, 0.009768857131289927)
(‘EstimatedSalary’, 0.008411927773482212)
(‘Point Earned’, 0.008308457778182893)
(‘Geography’, 0.006652118005970442)
(‘Tenure’, 0.004551831483165089)
(‘Gender’, 0.0033219006594251738)
(‘Satisfaction Score’, 0.0029191139821616347)
(‘Card Type’, 0.0025860960319596944)
(‘HasCrCard’, 0.0010021832037602411)

Il parametro feature_importances è una lista che contiene le importanze degli attributi nel modello addestrato. Queste importanze rappresentano quanto ogni attributo contribuisce alla previsione del modello.

La lunghezza di importances è uguale al numero di attributi nel dataset di addestramento.

Il valore di ogni elemento in importances varia da 0 a 1, dove un valore più alto indica che l’attributo è più importante per il modello.

Si osserva subito che la presenza di un reclamo (complain) incide molto sull’abbandono (feature_importances = 0,817…). Ciò è abbastanza comprensibile.

L’attributo che segue a distanza per importanza è l’età (Age), poi il numero di prodotti (NumOfProducts) che il cliente ha acquistato e così via.

Visualizziamo mediante un istogramma qual è l’età che massimizza l’abbandono. Per farlo aggiungiamo al precedente il seguente codice python in cui usiamo pyplot della libreria Matplotlib:


plt.hist(df[df['Exited'] == 1]['Age'], bins=10, alpha=0.7, color='red', label='Exited = True')

# Aggiunta delle etichette degli assi e della legenda
plt.xlabel('Age')
plt.ylabel('Frequenza abbandono')
plt.legend()

# Visualizzazione dell'istogramma
plt.show()

OUTPUT

Si vede come la frequenza di abbandono è massima intorno ai 40 anni. Rimane sostenuta sino ai 53/54 anni per poi diminuire.

Con dei codici analoghi possiamo visualizzare gli istogrammi associati agli altri attributi che impattano sull’abbandono. Per esempio vediamo NumOfProducts:

La frequenza di abbandono è molto alta nell’area clienti con un solo prodotto acquistato presso la banca; decresce rapidamente con il numero dei prodotti.

Probabilità di abbandono

Un’altra informazione utile è la probabilità di abbandono di un cliente.

L’algoritmo Random Forest permette di calcolarla.

Ecco il codice:

import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt



def converti_variabili_categoriche(df):
    label_encoder = LabelEncoder()
    encoded_df = df.copy()
    for col in encoded_df.columns:
        if encoded_df[col].dtype == 'object':
           encoded_df[col] = label_encoder.fit_transform(encoded_df[col])
    return encoded_df


# Carica i dati dei clienti
df_clienti = pd.read_csv("db.csv")


#cancella le prime tre colonne ininfluenti
indici_colonne_da_elimimare = df_clienti.columns[:3]
df_clienti = df_clienti.drop(indici_colonne_da_elimimare, axis=1)

# Esegue la conversione delle variabili categoriche
encoded_df = converti_variabili_categoriche(df_clienti)

df = encoded_df

# Prepara le caratteristiche dei clienti
X = df.drop("Exited", axis=1)

# Prepara gli obiettivi (valori di uscita)
y = df["Exited"]

# Calcola il rischio di abbandono utilizzando il modello di Random Forest
random_forest_model = RandomForestClassifier(class_weight={0: 1, 1: 1})
random_forest_model.fit(X, y)
rischi_abbandono = random_forest_model.predict_proba(X)[:, 1]


for i, probabilita in enumerate(rischi_abbandono):
    if probabilita > 0.3 and y[i] != 1:
       print(f"Cliente {i+1}: {probabilita}")

OUTPUT

Cliente 66: 0.34
Cliente 67: 0.32
Cliente 68: 0.38
Cliente 174: 0.38
Cliente 9978: 0.36
Cliente 9985: 0.34
Cliente 9986: 0.34

Vengono restituiti i clienti per cui è stata calcolata una probabilità maggiore del 30% (0,3) di abbandono.

Dentro l’algoritmo Random Forest

L’algoritmo Random Forest è una tecnica di apprendimento automatico che combina diversi decision tree per fare predizioni.

Immagina di avere una dataframe con N righe e M attributi (colonne) che influenzano un attributo target. L’obiettivo è valutare l’influenza di tutti gli attributi, tranne uno, sull’attributo target.

1 – Iniziamo dividendo casualmente il tuo dataset in due parti: un set di addestramento (70% dei dati) e un set di test (30% dei dati). Questa separazione è fondamentale per valutare le prestazioni del modello finale.

2 – Successivamente, creiamo un ensemble di decision tree. Per ogni albero, l’algoritmo seleziona casualmente un sottoinsieme dei dati di addestramento (con sostituzione). Questo ci consente di creare diverse istanze degli alberi.

3 – Per ogni albero nell’ensemble, viene effettuata una separazione rispetto a un attributo casuale selezionato tra gli attributi disponibili. Questa separazione è determinata misurando l’entropia dei nodi prima e dopo la divisione. L’obiettivo è massimizzare la riduzione dell’entropia nelle divisioni, in modo che i nodi risultanti siano più puri possibile rispetto all’attributo target.

La formula per calcolare l’entropia di Shannon (H) è la seguente:

H = – Σ p(x) * log2(p(x))

Dove p(x) rappresenta la probabilità di occorrenza di una classe specifica o un certo valore all’interno del nodo.

Questa formula calcola l’entropia sommando il prodotto della probabilità di ciascuna classe o valore all’interno del nodo con il logaritmo in base 2 di quella probabilità. Questo calcolo tiene conto della distribuzione delle classi all’interno del nodo e della loro probabilità di occorrenza.

L’entropia di Shannon sarà massima (cioè avrà un valore maggiore) quando tutte le classi o i valori sono equamente distribuiti, indicando un alto livello di disordine o incertezza all’interno del nodo. D’altra parte, l’entropia di Shannon sarà minima (cioè avrà un valore più basso) quando una sola classe o un singolo valore domina il nodo, indicando un alto livello di certezza o ordine.

In generale, si utilizza l’entropia di Shannon per valutare l’impurità o il disordine all’interno di un nodo e per prendere decisioni sulla divisione dei nodi in un albero di decisione in base all’attributo che massimizza la riduzione dell’entropia dopo la suddivisione.

Esempio

Supponiamo di avere un sottoinsieme di dati di addestramento come segue:

DF

RigaABCDY
101111
210011
301000
410100
511011

Vogliamo calcolare l’entropia dei nodi prima e dopo la separazione utilizzando l’attributo A. L’entropia è una misura della purezza dei nodi relativamente all’attributo target (Y).

Calcolo dell’entropia prima della separazione:

  • Numero totale di righe: 5
  • Numero di righe con Y=0: 2
  • Numero di righe con Y=1: 3

Calcoliamo la probabilità per ogni classe (Y=0 e Y=1):

  • Probabilità di Y=0: 2/5 = 0.4
  • Probabilità di Y=1: 3/5 = 0.6

Ora calcoliamo l’entropia prima della separazione (H prima):
H prima = – (0.4 * log2(0.4) + 0.6 * log2(0.6)) = 0,673

Calcolo dell’entropia dopo la separazione utilizzando l’attributo A:

  • Selezioniamo le righe con A=0 (Riga 1 e Riga 3). Il numero totale di righe selezionate è 2.

DF1

RigaABCDY
101111
301000
  • Numero di righe con Y=0: 1
  • Numero di righe con Y=1: 1
  • Probabilità di Y=0: 1/2 = 0.5
  • Probabilità di Y=1: 1/2 = 0.5
  • Entropia dopo selezione A=0 (H dopo A=0): – (0.5 * log2(0.5) + 0.5 * log2(0.5)) = 0,693
  • Selezioniamo le righe con A=1 (Riga 2, Riga 4 e Riga 5). Il numero totale di righe selezionate è 3.

DF2

RigaABCDY
210011
410100
511011
  • Numero di righe con Y=0: 1
  • Numero di righe con Y=1: 2
  • Probabilità di Y=0: 1/3 ≈ 0.333
  • Probabilità di Y=1: 2/3 ≈ 0.667
  • Entropia dopo selezione A=1 (H dopo A=1): – (0.333 * log2(0.333) + 0.667 * log2(0.667)) = 0,636

Ora calcoliamo l’entropia totale dopo la separazione (H dopo):
H dopo = (2/5) * (H dopo A=0) + (3/5) * (H dopo A=1) = (2/5) * 0,693 + (3/5) * 0,636 = 0,277 + 0,382 = 0,659

L’entropia è scesa anche se di poco da 0,673 a 0,659. C’è stato un lieve aumento d’informazione.

4 – Continuiamo questo processo di separazione fino a quando si raggiunge una condizione di terminazione. Questa condizione può essere raggiunta quando il numero massimo di nodi foglia è raggiunto o quando la profondità massima di un albero viene raggiunta.

5 – Ogni decision tree dell’ensemble viene utilizzato per fare predizioni sul set di test (o su nuovi dati non visti in precedenza). Le predizioni di ogni albero vengono ponderate in base alla loro accuratezza e utilizzate per ottenere una predizione finale.

6 – Infine, misuriamo la precisione del modello utilizzando metriche come l’accuratezza, l’errore quadratico medio o l’area sotto la curva ROC.