Spesso descriviamo i nostri clienti come se fossero acqua in un tubo: entrano dall’alto, scendono lungo il funnel e, se tutto va bene, escono dal rubinetto dell’acquisto. Se non lo fanno, diciamo che c’è una “perdita”.
Ma chi lavora con i dati sa che la realtà è molto più disordinata. Gli utenti non “scivolano” passivamente. Esitano. Cliccano, tornano indietro, rileggono la stessa pagina tre volte, mettono un prodotto nel carrello, ci ripensano e magari tornano due giorni dopo.
Se proviamo a mappare questo caos con le classiche percentuali di conversione (es. “il 2% converte”), perdiamo l’informazione più importante: il tempo.
Quanto tempo passa un utente in uno stato di indecisione prima di abbandonare? E come cambia la probabilità di vendita se quell’utente è già tornato tre volte sulla pagina prezzi?
Qui entrano in gioco le Catene di Markov. Non serve essere matematici puri per usarle: basta vederle come uno strumento narrativo più potente delle solite slide. In questo articolo prenderemo tre scenari tipici e, con poche righe di Python, li trasformeremo in modelli vivi, capaci di dirci non solo se un cliente comprerà, ma quanto tempo ci resta per convincerlo.
Il Setup: La tua cassetta degli attrezzi Python
Per rendere tutto questo operativo, non ci serve software costoso. Bastano numpy per l’algebra lineare e matplotlib per vedere cosa succede.
Il codice qui sotto è il motore di tutto l’articolo. Copialo una volta e potrai usarlo per analizzare qualsiasi processo, dall’onboarding di un’app alla fidelizzazione di un e-commerce.
import numpy as np
import matplotlib.pyplot as plt
# --- ALGEBRA E CALCOLO ---
def distribution_after_steps(P, start_index, steps):
"""Calcola dove si troveranno gli utenti dopo N step."""
p0 = np.zeros(P.shape[0])
p0[start_index] = 1.0
return p0 @ np.linalg.matrix_power(P, steps)
def mean_time_to_absorption(P, absorbing_states):
"""
Calcola il tempo medio (step) prima che un utente finisca
in uno stato assorbente (es. Churn o Conversione).
"""
n = P.shape[0]
transient = [i for i in range(n) if i not in absorbing_states]
# Estraiamo la sottomatrice Q degli stati transitori
Q = P[np.ix_(transient, transient)]
I = np.eye(len(Q))
# Calcolo della Matrice Fondamentale N = (I - Q)^-1
try:
N = np.linalg.inv(I - Q)
except np.linalg.LinAlgError:
return np.zeros(len(transient)) # Gestione errori singolarità
return N.sum(axis=1)
# --- VISUALIZZAZIONE BUSINESS-READY ---
def plot_state_diagram(P, labels, absorbing_states=None, title="Diagramma di stato"):
n = P.shape[0]
theta = np.linspace(0, 2*np.pi, n, endpoint=False)
pos = np.c_[np.cos(theta), np.sin(theta)]
plt.figure(figsize=(8, 8))
plt.scatter(pos[:,0], pos[:,1], s=1200, zorder=3, color='skyblue', edgecolors='black')
if absorbing_states is not None:
tta = mean_time_to_absorption(P, absorbing_states)
# Disegna i nodi e le etichette
t_idx = 0
for i, (x, y) in enumerate(pos):
label = labels[i]
# Se lo stato non è assorbente, mostriamo il Tempo Medio (E[T])
if absorbing_states is not None and i not in absorbing_states:
label += f"\nE[T]={tta[t_idx]:.1f}"
t_idx += 1
plt.text(x*1.15, y*1.15, label, ha='center', va='center',
bbox=dict(boxstyle='round', facecolor='white', alpha=0.9), fontsize=10, weight='bold')
# Disegna le frecce (transizioni)
for i in range(n):
for j in range(n):
if P[i, j] > 0.01: # Mostriamo solo probabilità rilevanti
dx, dy = pos[j] - pos[i]
# Curvatura per rendere leggibili frecce A->B e B->A
plt.arrow(pos[i,0], pos[i,1], dx*0.78, dy*0.78,
head_width=0.06, color='gray', alpha=0.6, length_includes_head=True)
# Posizionamento etichetta probabilità
mid_x = (pos[i,0] + pos[j,0])/2
mid_y = (pos[i,1] + pos[j,1])/2
plt.text(mid_x*0.9, mid_y*0.9, f"{P[i,j]:.2f}", fontsize=9, color='darkred')
plt.title(title, fontsize=14)
plt.axis('off')
plt.show()
Caso Studio 1: L’inerzia del Cliente (Journey con Abbandono)
Immaginiamo un funnel semplice dove l’utente entra come “Interessato”. Da lì può acquistare, ma c’è anche un rischio costante che abbandoni o, peggio, che non faccia nulla, rimanendo interessato ma inattivo.
labels = ["Interessato", "Acquisto", "Abbandono"]
P = np.array([
[0.6, 0.3, 0.1], # Interessato: 60% inerzia, 30% compra, 10% esce
[0.0, 0.7, 0.3], # Acquisto: 70% retention, 30% churn
[0.0, 0.0, 1.0] # Abbandono: Stato assorbente
])
plot_state_diagram(P, labels, absorbing_states=[2],
title="L'inerzia del Cliente Indeciso")
La lettura strategica: “Dead Man Walking”
Il grafico generato mostrerà un dato fondamentale nel nodo “Interessato”: E[T] = 5.0.
Cosa significa per il business?
Significa che partendo da “Interessato”, servono in media 5 interazioni (o step temporali) prima che l’utente esca definitivamente dal sistema (acquistando e poi andando via, o abbandonando subito).
Questo è il vostro Time-to-Action. Se la vostra strategia di email marketing o retargeting dura solo 3 giorni, ma il cliente decide biologicamente in 5, state buttando budget. Se dopo 5 step l’utente è ancora lì, statisticamente è un “morto che cammina”: la probabilità che converta crolla drasticamente.
Caso Studio 2: La scala della fedeltà (Retention Non-Lineare)
A differenza dei funnel, la retention non è un tubo in discesa. Un cliente può salire e scendere di livello.
labels = ["Fedele", "Occasionale", "Perso"]
P = np.array([
[0.80, 0.15, 0.05], # Fedele: difficile perderlo
[0.30, 0.50, 0.20], # Occasionale: instabile
[0.00, 0.00, 1.00] # Perso
])
plot_state_diagram(P, labels, absorbing_states=[2],
title="Dinamica di Fidelizzazione")
La lettura strategica: La fragilità dello stato intermedio
Osservate la riga “Occasionale”. L’utente ha il 30% di probabilità di diventare “Fedele” (upgrade) ma il 20% di perdersi.
La permanenza in questo stato è instabile. Il Markov Chain qui ci insegna che non esiste stabilità nel mezzo: un cliente occasionale va spinto in alto (Loyalty Program) o scivolerà in basso.
Il tempo medio prima del churn definitivo (che il codice calcolerà) è il Customer Lifetime atteso espresso in numero di acquisti/visite.
Caso Studio Master: Il Funnel Digitale Reale (Gara di velocità)
Uniamo tutto in un esempio completo. Nel web reale, Conversione e Uscita (Bounce/Churn) sono due stati assorbenti in competizione. È una gara.
Il codice seguente simula non solo la struttura, ma l’evoluzione temporale della “battaglia” tra successo e fallimento.
# --- DEFINIZIONE DEL FUNNEL DIGITALE ---
labels = ["Visita", "Click", "Conversione", "Uscita"]
# Matrice di Transizione
P = np.array([
[0.50, 0.30, 0.10, 0.10], # Visita: Alta inerzia (50%), pochi click (30%)
[0.00, 0.60, 0.30, 0.10], # Click: L'utente si scalda, conversione sale al 30%
[0.00, 0.00, 1.00, 0.00], # Conversione: Successo (Assorbente)
[0.00, 0.00, 0.00, 1.00] # Uscita: Fallimento (Assorbente)
])
absorbing_states = [2, 3] # Indici di Conversione e Uscita
# 1. Visualizza il grafo con i tempi medi
plot_state_diagram(P, labels, absorbing_states,
title="Funnel Digitale: Gara tra Conversione e Uscita")
# 2. Simula l'evoluzione della popolazione nel tempo
print(f"{'Step':<6} {'Visita':<10} {'Click':<10} {'CONVERSIONE':<12} {'USCITA':<10}")
print("-" * 50)
for k in [1, 3, 5, 10]:
dist = distribution_after_steps(P, start_index=0, steps=k)
print(f"{k:<6} {dist[0]:.3f} {dist[1]:.3f} {dist[2]:.3f} {dist[3]:.3f}")
Output e Analisi: “Winning Speed”
Eseguendo questo codice, vedrete come la probabilità si “travasa” dai nodi attivi ai nodi assorbenti.
Cosa osservare nei dati:
-
L’accelerazione: Noterete che all’inizio la Conversione e l’Uscita sono pari (0.10). Ma dopo 5 step, la conversione potrebbe staccare l’uscita o viceversa.
-
Time-to-Absorption (TTA): Il grafico mostrerà un E[T] diverso per “Visita” e “Click”.
-
Se E[T] su Visita è molto basso (es. 1.2), significa che il vostro sito non trattiene nessuno: o comprano subito o scappano subito (problema di UX/caricamento).
-
Se E[T] è alto ma la conversione è bassa, avete utenti “zombie” che girano a vuoto senza trovare la CTA.
-

Step Visita Click CONVERSIONE USCITA -------------------------------------------------- 1 0.500 0.300 0.100 0.100 3 0.125 0.273 0.364 0.238 5 0.031 0.140 0.525 0.304 10 0.001 0.015 0.638 0.346
Il Grafo rappresenta la Gara tra Conversione e Uscita e offre spunti strategici molto più profondi di un semplice report su Excel.
Ecco un’analisi dettagliata nodo per nodo e delle dinamiche di flusso.
1. Analisi dei Nodi e del Tempo ([math]E[T][/math])
Il Nodo “Visita” (Start Point)
[math]E[T] = 3.5[/math]: Questo è il numero più importante del grafico. Un nuovo visitatore “sopravvive” nel sistema per una media di 3.5 step (interazioni, minuti o refresh di pagina) prima di essere assorbito (convertire o uscire).
Inerzia ([math]0.50[/math] o 50%): Metà delle persone che atterrano sulla pagina non fanno nulla. Rimangono lì (loop su se stessi). Questo è tipico delle landing page: l’utente legge, scorre, ma non interagisce.
Il Bivio: Da qui, le probabilità di successo immediato (Conversione [math]0.10[/math]) e fallimento immediato (Uscita [math]0.10[/math]) sono identiche. È una situazione di stallo 1:1. Senza interazione, è un lancio di moneta.
Il Nodo “Click” (L’Engagement)
[math]E[T] = 2.5[/math]: Notate come il tempo medio si abbassa (da [math]3.5[/math] a [math]2.5[/math]). Una volta che l’utente clicca (si “ingaggia”), diventa più veloce nel prendere una decisione finale. L’incertezza diminuisce.
Inerzia Aumentata ([math]0.60[/math]): Questo è un dato interessante e controintuitivo. Una volta cliccato (es. aperto un dettaglio prodotto), la probabilità di rimanere in quello stato sale al 60%. L’utente sta valutando attivamente (“Analysis Paralysis”).
Il Ratio Vincente: Qui avviene la magia. Dal Click, la probabilità di Conversione ([math]0.30[/math]) è tripla rispetto all’Uscita ([math]0.10[/math]).
2. La Dinamica del Flusso (La Storia raccontata dai Dati)
Il grafico ci racconta due storie diverse a seconda dello stato dell’utente:
La fase “Fredda” (Visita):
Qui il gioco è rischioso. Per ogni utente che compra subito, ne perdi uno. L’obiettivo primario della UX in questa fase non deve essere per forza vendere, ma spostare l’utente nel nodo “Click”.
Perché? Perché il passaggio [math]\text{Visita} \to \text{Click} (0.30)[/math] è il ponte verso una zona statisticamente più sicura.
La fase “Calda” (Click):
Una volta arrivati qui, le probabilità sono a vostro favore (3 a 1). Tuttavia, l’alta inerzia ([math]0.60[/math]) suggerisce che l’utente ha bisogno di rassicurazioni finali. È qui che servono leve di scarsità (“ne restano solo 2”), social proof o spedizione gratuita per rompere il loop del 60% e spingere verso quel 30% di conversione.
3. Conclusioni Strategiche per il Business
Guardando questo grafo, un Growth Manager dovrebbe trarre queste conclusioni:
- Il valore del Click è predittivo: Un utente che clicca non ha solo mostrato interesse, ha cambiato classe di rischio. La sua probabilità di churn relativo (rispetto alla conversione) crolla.
- Tempo limite: Avete circa 3.5 interazioni per convincere un nuovo arrivato. Se progettate un funnel di onboarding che richiede 6 passaggi, state andando contro la statistica di sopravvivenza media dei vostri utenti ([math]E[T]=3.5[/math]).
- Collo di bottiglia: L’inerzia in “Visita” ([math]0.50[/math]) è fisiologica, ma l’inerzia in “Click” ([math]0.60[/math]) è pericolosa. Se l’utente è lì, vuole comprare ma qualcosa lo frena. Quello 0.60 va abbassato migliorando la chiarezza della pagina prodotto o del checkout.
In sintesi: il grafico mostra che la battaglia si vince portando l’utente fuori dal limbo della “Visita”. Una volta ottenuto il “Click”, il sistema tende naturalmente alla conversione, purché si agisca in fretta ([math]E[T] = 2.5[/math]).
Questa è la metrica della Winning Speed: non basta che l’utente converta, deve farlo prima che la probabilità di uscita diventi statistica certezza.
Conclusione: Oltre le Vanity Metrics
Le dashboard tradizionali ci danno una fotografia statica: “Il tasso di conversione è del 2%”.
Ma dire che il tasso è del 2% è una media che appiattisce la realtà. Un utente che è nello stato ‘Interessato’ da 5 sessioni ha una probabilità di acquisto totalmente diversa da uno appena atterrato sul sito.
Le Catene di Markov smascherano questa media. Ci permettono di vedere il funnel non come un imbuto rigido, ma come un ecosistema dinamico dove il tempo è una variabile cruciale.
Per un Growth Engineer, questo modello serve a identificare i colli di bottiglia temporali. Per un CMO, il Time-to-Absorption è un indicatore anticipatore della salute del brand: se il tempo prima dell’abbandono si accorcia, state bruciando la vostra audience.
Siamo partiti da matrici e vettori, ma siamo arrivati a quantificare la pazienza dei clienti. E nel marketing, il tempo è letteralmente denaro.
Articoli di approfondimento
👉 Cosa sono le catene di Markov e come usarle: esempi reali e calcoli
👉 Catene di Markov: 6 esercizi svolti e commentati per il business
👉 Modello meteorologico con catene di Markov e Random Forest
👉 Ottimizzazione call center con CTMC: nascita–morte e performance







