Hai fatto i conti, lanciato le campagne e le vendite arrivano. Sulla carta, i margini sono ottimi. Sembra andare tutto per il verso giusto, finché a fine mese non guardi il bilancio reale e i numeri non tornano.
Cos’è successo? Semplice: hai scontrato il tuo modello teorico con la realtà.
Dopo aver costruito un primo modello matematico per stimare profitto e break-even del nostro eCommerce, è il momento di fare un passo avanti.
I modelli di base falliscono perché assumono un mondo perfetto: vendite costanti, traffico lineare e, soprattutto, l’illusione che ogni ordine generato si trasformi in un incasso definitivo.
Il vero killer silenzioso dei margini è un altro: il tasso di reso.
Particolarmente critico nel fashion, nel mercato second-hand e sui marketplace, ignorarlo significa essere sistematicamente, e pericolosamente, ottimisti. Vediamo come integrare questo parametro letale e passare da un calcolo statico a una vera previsione dinamica usando Python.
Perché i resi sono devastanti per il tuo eCommerce
Ogni volta che un cliente restituisce un pacco, si innesca una reazione a catena che brucia cassa:
-
❌ Si azzera il margine previsto.
-
❌ Si sommano i costi logistici (spedizione di andata, spedizione di ritorno e gestione magazzino).
-
❌ Rischia di abbassarsi il Lifetime Value (LTV) del cliente se l’esperienza di reso non è ottimale.
Tradotto in numeri: non stai solo smettendo di guadagnare da quella vendita, ma stai attivamente intaccando il profitto generato dalle altre vendite andate a buon fine.
L’estensione del modello matematico
Riprendiamo le variabili del nostro modello base:
- Visitatori: [math]N[/math]
- Conversion rate: [math]p[/math]
- Prezzo medio: [math]P[/math]
- Margine lordo: [math]m[/math]
1. Ricavi totali (Teorici)
Il calcolo di partenza è semplice:
[math]Ricavi\_Totali = N \cdot p \cdot P[/math]
2. Introduzione del tasso di reso
Definiamo il nostro tasso di reso come [math]r[/math].
3. Ricavi netti (Reali)
Dobbiamo decurtare i ricavi totali della percentuale di merce che torna indietro:
[math]Ricavi\_Netti = Ricavi\_Totali \cdot (1 – r)[/math]
4. Margine lordo netto
Ora applichiamo il nostro margine operativo percentuale ai soli ricavi effettivi:
[math]Margine\_Netto = Ricavi\_Netti \cdot m[/math]
5. Profitto finale
Infine, sottraiamo i costi fissi (e variabili non logistici):
[math]Profitto = Margine\_Netto – Costi[/math]
⚠️ Attenzione:
Il tasso di reso agisce come un moltiplicatore negativo nascosto.
[math]\displaystyle \begin{aligned}
&\text{Se hai un margine teorico (} m \text{) del } 30\% \text{ e un} \\
&{} \quad \text{tasso di resi (} r \text{) del } 20\%, \text{ il tuo margine} \\
&{} \quad \text{effettivo sul venduto reale non è più il } 30\%, \\
&{} \quad \text{ma scende inesorabilmente a circa il } 24\% \\
&{} \quad (0.30 \cdot 0.80).
\end{aligned}[/math]
L’evoluzione: da un modello statico a uno predittivo
Finora abbiamo ragionato su medie statiche. Ora facciamo il vero salto di qualità: invece di assumere volumi di vendita costanti, andiamo a prevederli nel tempo.
Per farlo, uniamo la logica economica aziendale al forecasting dei dati, utilizzando Prophet (una libreria open-source sviluppata da Meta per le serie storiche).
Previsione vendite con Prophet in Python
Useremo Prophet per stimare l’andamento delle vendite future, per poi applicarvi il nostro calcolatore di profitto.
Installazione
L’analisi dei dati Immaginiamo di avere un dataset realistico (vendite.csv) contenente date (ds), vendite giornaliere (y), traffico, spesa in ads, conversion rate e tasso di resi.
👉 Puoi scaricarlo qui: vendite
import pandas as pd
from prophet import Prophet
# 1. Caricamento Dataset (deve contenere obbligatoriamente le colonne 'ds' per le date e 'y' per il target)
df = pd.read_csv("vendite.csv")
# 2. Inizializzazione e addestramento del modello
model = Prophet()
model.fit(df)
# 3. Creazione del dataframe per le date future (es. prossimi 30 giorni)
future = model.make_future_dataframe(periods=30)
# 4. Generazione della previsione
forecast = model.predict(future)
# 5. Visualizziamo le ultime righe delle vendite previste (yhat)
print(forecast[['ds', 'yhat']].tail())
Interpretazione: La colonna yhat rappresenta la stima delle vendite per ogni giorno futuro. Questo è esattamente il dato (dinamico) che ci serve per alimentare il nostro modello economico.
Integrazione: Prophet incontra il conto economico
Ora chiudiamo il cerchio. Creiamo una funzione che implementi le formule viste in precedenza e applichiamola alle previsioni di Prophet.
def calcola_profitto(vendite, prezzo, margine, resi, costi_fissi):
"""Calcola il profitto netto giornaliero includendo l'impatto dei resi."""
ricavi = vendite * prezzo
ricavi_netti = ricavi * (1 - resi)
margine_netto = ricavi_netti * margine
profitto = margine_netto - costi_fissi
return profitto
# Applichiamo la funzione sui dati previsti da Prophet
forecast['profitto_previsto'] = forecast['yhat'].apply(
lambda x: calcola_profitto(
vendite=x, # le vendite previste dal modello (yhat)
prezzo=50, # Prezzo medio di 50€
margine=0.3, # Margine lordo del 30%
resi=0.2, # Tasso di reso del 20%
costi_fissi=100 # Costi fissi giornalieri (es. server, licenze, stipendi quota-parte)
)
)
print(forecast[['ds', 'yhat', 'profitto_previsto']].tail())
ds yhat profitto_previsto
205 2024-07-24 9.032993 8.395917
206 2024-07-25 8.060330 -3.276035
207 2024-07-26 8.647049 3.764589
208 2024-07-27 8.352456 0.229467
209 2024-07-28 8.177306 -1.872334
In questo modo, hai trasformato un semplice modello statistico in un cruscotto aziendale in grado di dirti non solo “quanto venderai”, ma soprattutto “quanto ti resterà in tasca”, al netto di chi deciderà di farsi rimborsare.
Punto di vista applicativo:
L’esercizio è estremamente interessante perché risolve un problema classico dei “data scientist junior”.
Molto spesso, i tutorial di Data Science si fermano al calcolo dell’accuratezza statistica (es. [math]RMSE[/math], [math]MAE[/math]) della previsione (la metrica [math]yhat[/math]). Al business, tuttavia, non interessa l’accuratezza pura del forecasting, ma l’impatto finanziario di quella previsione.
Questo esercizio mostra la vera “Data Science applicata al Business”: prende una metrica di vanità (il volume di vendite lorde o il traffico) e la processa attraverso un funnel economico realistico (che penalizza i resi) per sputare fuori l’unica metrica che conta davvero per un CEO o un eCommerce Manager: il profitto.
Peculiarità tecniche:
- Modularità: Aver incapsulato la logica in calcola_profitto permette di trasformare facilmente lo script per testare vari scenari (What-if analysis).
Cambiando i parametri nella funzione apply, puoi simulare come varierebbe il profitto futuro se riuscissi ad abbassare i resi dal 20% al 15%. - La natura di Prophet: Prophet tratta i dati considerando stagionalità (settimanali, annuali) e trend. Questo significa che i “profitti_previsti” calcolati a fine script rispetteranno le fluttuazioni fisiologiche dell’eCommerce (es. vendi di più nel weekend o sotto Natale).
Il Livello Pro: Resi dinamici e Machine Learning
Finora abbiamo ragionato su medie storiche: abbiamo ipotizzato un tasso di reso costante al 20% e un carrello medio fisso.
Nel mondo reale, però, il reso non è una costante democratica.
Dipende da una miriade di fattori:
-
La categoria merceologica (un paio di scarpe torna indietro molto più spesso di un profumo).
-
Lo storico del cliente (i “serial returners” esistono e pesano sui bilanci).
-
Gli sconti applicati (gli acquisti d’impulso super-scontati vengono spesso restituiti).
-
La stagionalità (i regali di Natale hanno tassi di reso anomali a gennaio).
Il vero salto di qualità consiste nel non “subire” il reso, ma prevederlo carrello per carrello, prima ancora che l’ordine venga spedito.
Prevedere la probabilità di reso con Scikit-Learn
Invece di usare Prophet (che lavora sulle serie temporali), qui entra in gioco la Classificazione.
Possiamo addestrare un modello di Machine Learning (come una Random Forest o una Regressione Logistica) sullo storico degli ordini.
L’obiettivo non è dirci semplicemente “Sì” o “No”, ma darci una percentuale di rischio tramite la funzione predict_proba.
Ecco un esempio di come si traduce in codice:
from sklearn.ensemble import RandomForestClassifier
# Feature: [prezzo, sconto, resi_storici, is_fashion]
X_train = df_ordini[['prezzo', 'sconto', 'resi_storici', 'is_fashion']]
y_train = df_ordini['reso_effettuato']
rf_model = RandomForestClassifier(n_estimators=100)
rf_model.fit(X_train, y_train)
# Simulazione su un nuovo ordine ad alto rischio
nuovo_ordine = [[120.0, 0.15, 3, 1]]
prob = rf_model.predict_proba(nuovo_ordine)[0][1]
print(f"Probabilità di reso per questo ordine: {prob:.1%}")
Questo simulatore è interessante perché unisce due mondi spesso distanti:
-
Macro-Forecasting (Prophet): Utile per la pianificazione finanziaria a lungo termine e la gestione del magazzino. Ti dice “dove sta andando la nave”.
-
Micro-Prediction (Random Forest): Utile per l’operatività quotidiana. Ti permette di agire sul checkout, ad esempio disabilitando la spedizione gratuita per utenti che il modello identifica come “ad altissimo rischio reso”.
Nota sull’implementazione: Il KeyError
Durante lo sviluppo, potreste imbattervi in un KeyError. Questo accade perché i dati necessari per Prophet (serie temporali) sono diversi da quelli per la Random Forest (caratteristiche dell’ordine). La sfida del Data Scientist moderno non è solo scrivere l’algoritmo, ma costruire un’infrastruttura dati capace di alimentare entrambi i modelli.
Dall’Ottica “Macro” all’Ottica “Micro”
Quando usiamo Prophet, stiamo guardando l’eCommerce con un telescopio: vediamo le grandi masse, i trend stagionali e i picchi di vendita. È un approccio aggregato. Quando usiamo la Random Forest (Machine Learning), passiamo al microscopio: guardiamo le cellule del business, ovvero il singolo ordine e il singolo cliente. Questo è l’approccio granulare.
Ecco perché la granulosità cambia completamente le regole del gioco:
1. Il Tasso di Reso non è un “Fisso Mensile”
Se ti limiti a dire “il mio tasso di reso medio è il 20%”, stai commettendo un errore di valutazione.
-
Dato aggregato: “A gennaio abbiamo avuto il 20% di resi.” (Inutile per l’azione immediata).
-
Dato granulare: “Il cliente X, che acquista da mobile, ha una probabilità del 60% di rendere questo specifico vestito di taglia M.”
Senza granularità, tratti il cliente fedele (che non rende mai) allo stesso modo del “serial returner”. I dati granulari ti permettono di fare discriminazione positiva: puoi offrire sconti maggiori a chi sai che non ti costerà un centesimo in logistica di ritorno.
2. Il Problema delle Variabili Nascoste (Features)
Un modello ML ha bisogno di “appigli” per capire la realtà. Se fornisci solo il totale vendite, il modello è cieco. I dati granulari aggiungono dimensioni:
-
Dimensioni temporali: L’ordine è stato fatto di notte? (Spesso correlato ad acquisti impulsivi e più resi).
-
Dimensioni comportamentali: Quanto tempo ha passato l’utente sulla tabella delle taglie prima di acquistare?
-
Dimensioni tecniche: Che dispositivo usa? Che metodo di pagamento?
3. Gestione del Rischio e “Unit Economics”
La granularità ti permette di calcolare la Unit Economics, ovvero il profitto reale su ogni singola unità venduta. Se un prodotto ha un alto tasso di vendita ma un tasso di reso granulare dell’80% (magari per un difetto di fabbrica o una vestibilità errata), il dato aggregato potrebbe mostrarti un “Best Seller”, mentre il dato granulare ti griderebbe che stai perdendo migliaia di euro in spedizioni inutili.
Approfondimento ML: Oltre le variabili ovvie
Per rendere il modello davvero predittivo, non possiamo limitarci al prezzo. Dobbiamo fornire alla nostra Random Forest delle feature “comportamentali” che intercettino l’intenzione del cliente.
1. Aggiungiamo 3 Feature “Non Ovvie”:
-
Time_to_Checkout: Il tempo trascorso sulla pagina del prodotto o della tabella taglie. Un tempo brevissimo potrebbe indicare un acquisto impulsivo (alto rischio reso), mentre un tempo molto lungo unito a molteplici visualizzazioni della guida taglie indica un acquisto ponderato.
-
Basket_Diversity: Quante taglie diverse dello stesso articolo sono nel carrello? Se un utente compra la stessa maglia in taglia M e L, il tasso di reso è matematicamente del 50% o del 100%.
-
Historical_Return_Rate_User: Non solo il numero di resi, ma il rapporto tra ordini fatti e resi effettuati negli ultimi 12 mesi.
2. Mini-Feature Importance
Dopo aver addestrato il modello, è fondamentale capire cosa sta guidando le decisioni. In Python, possiamo visualizzarlo così:
import matplotlib.pyplot as plt
import numpy as np
# Estraiamo l'importanza delle feature
importances = rf_model.feature_importances_
indices = np.argsort(importances)
features = ['prezzo', 'sconto', 'resi_storici', 'is_fashion', 'basket_diversity', 'time_on_page']
plt.title('Cosa influenza il reso? (Feature Importance)')
plt.barh(range(len(indices)), importances[indices], align='center')
plt.yticks(range(len(indices)), [features[i] for i in indices])
plt.xlabel('Importanza Relativa')
plt.show()

Integrazione nel Checkout: Dall’algoritmo all’azione
Un modello predittivo è inutile se resta in un notebook. La vera magia avviene quando lo colleghi al tuo front-end tramite un’API.
Esempio di workflow:
-
Il cliente aggiunge i prodotti al carrello.
-
Al momento del pagamento, il sistema invia i dati (prezzo, taglie, storico cliente) al modello ML.
-
L’algoritmo risponde in millisecondi con una probabilità di reso.
-
Logica di Business:
-
Rischio < 20%: Mostra “Spedizione Gratuita” e “Reso Facile”.
-
Rischio > 70%: Rimuovi l’opzione “Pagamento alla consegna” e offri un piccolo sconto extra se l’utente rinuncia al diritto di reso (laddove legalmente possibile) o se sceglie il ritiro in un punto di consegna fisico (che abbatte i costi logistici).
-
⚠️ I 5 Errori Killer (che distruggono il tuo margine)
Se vuoi che il tuo simulatore sia affidabile, devi evitare queste trappole comuni in cui cadono anche i manager più esperti:
1. Usare medie statiche
Pensare che il tasso di reso sia il 20% tutto l’anno è un suicidio finanziario. Durante i saldi o il Black Friday, la qualità dei clienti cambia e il tasso può raddoppiare.
Soluzione: Usa Prophet per intercettare la stagionalità del reso, non solo delle vendite.
2. Ignorare i costi logistici “invisibili”
Il reso non costa solo la spedizione. C’è il costo del magazziniere che controlla il capo, il costo del repackaging e, spesso, il costo dell’invenduto (merce che torna troppo tardi per essere rivenduta a prezzo pieno).
Soluzione: Carica nel simulatore un costo fisso per ogni operazione di reso.
3. Non distinguere le categorie merceologiche
Unire il tasso di reso dell’elettronica (basso) con quello dell’abbigliamento (alto) in un unico numero “eCommerce” porta a decisioni sbagliate.
Soluzione: Segmenta sempre i dati granulari per categoria.
4. Usare Prophet senza Cross-Validation
Fidarsi della prima previsione di Prophet senza testarla è rischioso. Prophet ha bisogno di una validazione temporale (Backtesting) per capire quanto ha sbagliato nel passato prima di dirti il futuro.
5. Addestrare il ML senza bilanciare le classi
In molti eCommerce i resi sono, fortunatamente, la minoranza (es. 10%). Se addestri un modello su questi dati, l’algoritmo imparerà a dire sempre “Nessun reso” per avere un’accuratezza del 90%.
Soluzione: Usa tecniche di oversampling (SMOTE) o pesatura delle classi per insegnare al modello a riconoscere quei rari, ma costosi, casi di reso.
Predictive Analytics
👉 Facebook Prophet: guida completa al forecasting di serie temporali
👉 Previsioni di vendita con Prophet: esercizi progressivi
👉Forecasting logistico con Python e Prophet: dal modello al deploy
👉Random Forest: guida pratica da zero al codice Python
👉Random Forest in Python: esercizi per classificazione e regressione





