Data Storytelling: 6 Esercizi Pratici per Trasformare i Dati in Decisioni (con Codice Python)

Cerca:

Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors

I dati, da soli, sono muti. Un foglio di calcolo pieno di numeri non ispira, non convince e raramente guida una decisione strategica. È solo quando iniziamo a interrogarli, a collegarli e a dare loro una forma che iniziano a parlare. Questa è l’essenza del Data Storytelling: non una tecnica arcana, ma l’abilità di tradurre l’analisi in una narrazione chiara e persuasiva.

In questo percorso non troverai formule magiche, ma un allenamento pratico. Attraverso 6 esercizi di difficoltà crescente, passeremo dalla semplice visualizzazione di un dato alla costruzione di una storia complessa che mette in relazione più fattori. Useremo strumenti comuni come Python, ma il vero focus sarà sul “perché”: perché quel grafico funziona? Quale domanda sta rispondendo? E, soprattutto, quale azione può ispirare?

Gli esercizi spaziano dalla preparazione dei dati all’interpretazione narrativa avanzata.

6 esercizi progressivi su Data Storytelling, con dataset generati ad hoc, soluzioni dettagliate e visualizzazioni

Esercizio 1 (Facile): Esplorazione Base e Grafico a Barre

Testo:

Hai un dataset che raccoglie le vendite mensili (in migliaia di euro) di 3 prodotti (A, B, C) nel 2023:

Mese Prodotto A Prodotto B Prodotto C
Gen 12 8 15
Feb 14 10 18
Mar 16 9 20

Obiettivo:

  • Calcola le vendite totali per ogni prodotto.
  • Crea un grafico a barre verticali per confrontarli.
  • Scrivi una breve osservazione (1-2 righe) su come comunicheresti il risultato a un manager.

Soluzione:

Calcolo:

  • Prodotto A: [math]12 + 14 + 16 = 42[/math]
  • Prodotto B: [math]8 + 10 + 9 = 27[/math]
  • Prodotto C: [math]15 + 18 + 20 = 53[/math]

Grafico:

import matplotlib.pyplot as plt
prodotti = ['A', 'B', 'C']
vendite = [42, 27, 53]
plt.bar(prodotti, vendite, color=['red', 'blue', 'green'])
plt.title('Vendite Totali per Prodotto (Gen-Mar 2023)')
plt.ylabel('Vendite (in migliaia di €)')
plt.show()

Osservazione:

“Il prodotto C domina le vendite (+26% rispetto ad A). B è il meno performante: suggerirei un’analisi delle sue criticità di mercato.”

Il Potere della Chiarezza (Grafico a Barre)

Questo esercizio è l’essenza della comunicazione diretta. Il grafico a barre è universalmente compreso e non lascia spazio a interpretazioni ambigue. La sua forza risiede nella sua onestà visiva: mostra confronti netti. L’aggiunta dell’osservazione per il manager trasforma un semplice grafico in uno strumento decisionale, spostando il focus dal “cosa” (il prodotto C vende di più) al “e quindi?” (dobbiamo analizzare il prodotto B).

È perfetto per dashboard di vendita, report trimestrali o qualsiasi situazione in cui si debba confrontare la performance di categorie discrete (prodotti, regioni, canali di marketing). È il primo passo per rispondere alla domanda: “Chi sta vincendo e chi sta perdendo?”.

Esercizio 2 (Intermedio): Serie Temporale con Trend

Testo:

  • Usa i dati dell’Esercizio 1.
  • Crea un grafico a linee delle vendite mensili dei 3 prodotti.
  • Calcola il tasso di crescita medio mensile per il Prodotto A.
  • Aggiungi annotazioni al grafico per evidenziare il picco di vendite.

Soluzione:

Grafico a linee:

mesi = ['Gen', 'Feb', 'Mar']
plt.plot(mesi, [12, 14, 16], marker='o', label='A')
plt.plot(mesi, [8, 10, 9], marker='s', label='B')
plt.plot(mesi, [15, 18, 20], marker='d', label='C')
plt.annotate('Picco: 20k €', xy=('Mar', 20), xytext=('Feb', 19), arrowprops=dict(facecolor='black'))
plt.legend()

 

Tasso di crescita A:

  • Feb vs Gen: [math]\frac{(14-12)}{12} = +16.7\%[/math]
  • Mar vs Feb: [math]\frac{(16-14)}{14} = +14.3\%[/math]
  • Media: [math]\frac{(16.7 + 14.3)}{2} = 15.5\%[/math] mensile.

Teoria:

Serie temporali mostrano trend e stagionalità. Le annotazioni guidano l’attenzione su eventi rilevanti.

Raccontare il Cambiamento (Serie Temporale)

Se il grafico a barre è una fotografia, la serie temporale è un film. Mostra l’evoluzione, il ritmo, la direzione. Calcolare il tasso di crescita (come per il prodotto A) aggiunge una metrica quantitativa al trend visivo. L’annotazione del picco è una tecnica narrativa cruciale: guida l’occhio dello spettatore esattamente dove vogliamo, per evidenziare un successo, un’anomalia o un evento chiave (es. il lancio di una campagna).

Forse potrebbe interessarti anche:  Dalla Curva di Rogers alle Catene di Markov: Come Modellare e Prevedere il Customer Journey con Python

Indispensabile per analizzare metriche che evolvono nel tempo: traffico di un sito web, andamento dei ricavi, numero di iscritti a una newsletter. Permette di identificare stagionalità, impatto di eventi esterni e traiettorie di crescita o declino.

Esercizio 3 (Intermedio-Avanzato): Heatmap per Correlazioni

Testo:

Hai dati giornalieri (5 giorni) per 3 metriche di un sito e-commerce:

Giorno Sessioni Frequenza Rimbalzo (%) Vendite (€)
1 1500 65 3200
2 1700 60 3500
3 1200 70 2800
4 2000 55 4000
5 1800 58 3800
  • Calcola la matrice di correlazione.
  • Crea una heatmap.
  • Interpreta la relazione tra “Frequenza Rimbalzo” e “Vendite”.

Soluzione:

Correlazione (Pearson):

Sessioni Frequenza Rimbalzo Vendite
Sessioni 1.00 -0.92 0.98
Freq. Rimbalzo -0.92 1.00 -0.96
Vendite 0.98 -0.96 1.00

Heatmap:

# ESERCIZIO 3: HEATMAP PER CORRELAZIONI
# Obiettivo: Analizzare le relazioni tra metriche di un e-commerce

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# -----------------------------------------
# PASSO 1: Creazione del dataset
# -----------------------------------------
data = {
    "Giorno": [1, 2, 3, 4, 5],
    "Sessioni": [1500, 1700, 1200, 2000, 1800],
    "Frequenza_Rimbalzo": [65, 60, 70, 55, 58],  # in %
    "Vendite": [3200, 3500, 2800, 4000, 3800]    # in €
}

df = pd.DataFrame(data)
print("Dataset originale:")
print(df)

# -----------------------------------------
# PASSO 2: Calcolo matrice di correlazione
# -----------------------------------------
# Selezione colonne numeriche (escludendo 'Giorno')
numeric_cols = df.select_dtypes(include=[np.number]).columns
corr_matrix = df[numeric_cols].corr(method='pearson')  # metodo default

print("\nMatrice di correlazione (Pearson):")
print(corr_matrix)

# -----------------------------------------
# PASSO 3: Creazione heatmap
# -----------------------------------------
plt.figure(figsize=(8, 6))
heatmap = sns.heatmap(
    corr_matrix,
    annot=True,          # Mostra valori nelle celle
    cmap='coolwarm',     # Mappa di colori divergente
    vmin=-1,            # Valore minimo per la scala
    vmax=1,             # Valore massimo per la scala
    linewidths=0.5,      # Spessore linee tra celle
    annot_kws={"size": 12}  # Dimensione testo annotazioni
)

plt.title("Mappa di Correlazione tra Metriche E-commerce", pad=20, fontsize=14)
plt.xticks(rotation=45)  # Ruota etichette asse X per migliore leggibilità

# -----------------------------------------
# PASSO 4: Interpretazione guidata
# -----------------------------------------
# Evidenziazione celle critiche
heatmap.add_patch(plt.Rectangle((2, 0), 1, 3, fill=False, edgecolor='gold', lw=3, clip_on=False))  # Riquadro Vendite
heatmap.add_patch(plt.Rectangle((0, 2), 3, 1, fill=False, edgecolor='gold', lw=3, clip_on=False))  # Riquadro Frequenza Rimbalzo

plt.tight_layout()
plt.show()

# -----------------------------------------
# RISULTATO E COMMENTI
# -----------------------------------------
"""
INTERPRETAZIONE:
1) Vendite-Sessioni (0.98): 
   - Fortissima correlazione positiva. Più sessioni = più vendite.
   
2) Vendite-Frequenza_Rimbalzo (-0.96):
   - Fortissima correlazione NEGATIVA. Meno utenti abbandonano il sito (freq. rimbalzo ↓), più le vendite ↑.
   - Strategia: Ottimizzare l'engagement per ridurre la frequenza di rimbalzo.

3) Sessioni-Frequenza_Rimbalzo (-0.92):
   - Al crescere delle sessioni, la frequenza di rimbalzo tende a diminuire.
   - Possibile spiegazione: traffico di qualità più elevata.
"""

# Extra: Esportazione dati per report (opzionale)
df.to_csv("ecommerce_correlation_data.csv", index=False)

 

Dataset originale:
   Giorno  Sessioni  Frequenza_Rimbalzo  Vendite
0       1      1500                  65     3200
1       2      1700                  60     3500
2       3      1200                  70     2800
3       4      2000                  55     4000
4       5      1800                  58     3800

Matrice di correlazione (Pearson):
                      Giorno  Sessioni  Frequenza_Rimbalzo   Vendite
Giorno              1.000000  0.466628           -0.505634  0.562926
Sessioni            0.466628  1.000000           -0.996204  0.992336
Frequenza_Rimbalzo -0.505634 -0.996204            1.000000 -0.994018
Vendite             0.562926  0.992336           -0.994018  1.000000

Interpretazione:

La frequenza di rimbalzo è negativamente correlata con le vendite (-0.96): meno utenti abbandonano il sito, più aumentano le vendite. La relazione è quasi perfetta.

Svelare le Relazioni Nascoste (Heatmap)

Questo esercizio introduce il pensiero multivariato. La heatmap è uno strumento denso di informazioni che, a colpo d’occhio, rivela la struttura delle interdipendenze in un sistema. La correlazione negativa tra Frequenza di Rimbalzo e Vendite è un insight potentissimo. Non dice solo cosa succede, ma suggerisce una leva strategica: per aumentare le vendite, dobbiamo migliorare l’esperienza utente e ridurre i rimbalzi.

Fondamentale nell’e-commerce, nell’analisi di prodotto (quali feature sono correlate con la retention?) e nel marketing (quali canali portano clienti con il valore più alto?). È uno strumento diagnostico per capire quali leve muovono le metriche chiave.

Forse potrebbe interessarti anche:  Binomiale vs Ipergeometrica: L'Errore che Costa Caro

Esercizio 4 (Avanzato): Istogramma e Distribuzione

Testo:

  • Simula 1000 valori di età di clienti con distribuzione normale ([math]\mu=35[/math], [math]\sigma=10[/math]).
  • Crea un istogramma con 15 bin.
  • Aggiungi una linea per la densità.
  • Calcola la percentuale di clienti tra 25 e 45 anni.

Soluzione:

Istogramma + KDE:

import numpy as np
eta = np.random.normal(35, 10, 1000)
sns.histplot(eta, bins=15, kde=True, stat='density')

 

Calcolo percentuale:

  • Z-score per 25: [math]\frac{(25-35)}{10} = -1[/math]
  • Z-score per 45: [math]\frac{(45-35)}{10} = +1[/math]
  • Da tabelle normali: [math]P(-1 < Z < 1) \approx 68.3\%[/math].

Teoria:

Gli istogrammi mostrano la forma della distribuzione. La regola 68-95-99.7% è utile per dati normali.

Capire il “Chi” (Istogramma e Distribuzione)

Qui passiamo dall’analisi di performance all’analisi del pubblico. L’istogramma ci mostra la forma della nostra base clienti. La distribuzione normale è un modello, ma nella realtà potremmo scoprire distribuzioni bimodali (due gruppi di clienti distinti) o asimmetriche (molti giovani e pochi anziani). Sapere che il 68% dei clienti ha tra 25 e 45 anni non è un dato statistico, è un’informazione di business: il nostro marketing, il nostro prodotto e la nostra comunicazione devono parlare a quel nucleo.

Essenziale per la segmentazione della clientela (analisi demografiche), per il controllo qualità (distribuzione degli errori di produzione) o per la finanza (distribuzione dei rendimenti di un portafoglio).

Esercizio 5 (Difficile): Grafico a Scatter con Regressione

Testo:

Un dataset raccoglie ore di studio e voti d’esame per 20 studenti:

ore_studio = np.random.randint(10, 50, 20)
voti = 0.5 * ore_studio + 30 + np.random.normal(0, 5, 20)
  • Crea uno scatter plot con linea di regressione.
  • Calcola l'[math]R^2[/math] e interpretalo.
  • Prevedi il voto per uno studente che studia 35 ore.

Soluzione:

Scatter plot:

sns.regplot(x=ore_studio, y=voti, ci=None)

 

[math]R^2[/math]:

Se [math]R^2 \approx 0.75[/math], significa che il 75% della variabilità dei voti è spiegato dalle ore di studio.

Previsione:

Equazione: [math]\displaystyle \begin{aligned} \text{Voto} &= 0.5 \times (35) + 30 \\ &\approx 47.5 \end{aligned}[/math]

Quantificare l’Impatto e Prevedere (Regressione)

Questo esercizio è il ponte tra l’analisi descrittiva e quella predittiva. Lo scatter plot mostra la relazione, ma la linea di regressione e l’R² la formalizzano. Un R² di 0.75 ci dice che il nostro modello (le ore di studio) è un buon predittore, ma non perfetto: c’è un 25% di variabilità spiegato da altri fattori (talento, qualità dello studio, etc.). La previsione per 35 ore rende tangibile il valore del modello.

Cruciale in ambiti dove si vuole dimostrare il ROI: “Quante vendite aggiuntive genera ogni euro speso in pubblicità?”. Utile anche nel forecasting operativo, per prevedere le vendite future in base a variabili note.

Esercizio 6 (Molto Difficile): Storytelling Multivariato

Testo:

Hai dati su 4 città (2022):

Città Popolazione (mln) PIL (mld €) Emissioni CO2 (kt)
Milano 1.4 200 5000
Roma 2.8 150 7000
Torino 0.9 80 4000
Napoli 3.1 90 6000
  • Crea un bubble chart con:
    • Asse X: Popolazione
    • Asse Y: PIL
    • Dimensione bolle: CO2
  • Aggiungi annotazioni per la città con il miglior rapporto PIL/CO2.
  • Scrivi un mini-report (max 100 parole) per un policymaker.

Soluzione:

Bubble chart:

# ESERCIZIO 6: STORYTELLING MULTIVARIATO - BUBBLE CHART
# Obiettivo: Analizzare relazioni tra popolazione, PIL ed emissioni CO2 in 4 città italiane

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# -----------------------------------------
# PASSO 1: Creazione dataset
# -----------------------------------------
data = {
    "Città": ["Milano", "Roma", "Torino", "Napoli"],
    "Popolazione": [1.4, 2.8, 0.9, 3.1],  # in milioni
    "PIL": [200, 150, 80, 90],            # in miliardi di €
    "CO2": [5000, 7000, 4000, 6000]       # in ktonnellate
}

df = pd.DataFrame(data)

# Calcolo metriche aggiuntive
df["PIL_per_CO2"] = df["PIL"] / (df["CO2"] / 1000)  # mld € per kt CO2
df["PIL_pro_capite"] = df["PIL"] / df["Popolazione"]  # mld € per milione abitanti

print("Dataset arricchito:")
print(df[["Città", "Popolazione", "PIL", "CO2", "PIL_per_CO2", "PIL_pro_capite"]])

# -----------------------------------------
# PASSO 2: Bubble Chart avanzato
# -----------------------------------------
plt.figure(figsize=(10, 7))

# Creazione scatter plot con bolle dimensionate
scatter = plt.scatter(
    x=df["Popolazione"],
    y=df["PIL"],
    s=df["CO2"]*0.8,  # Scala le dimensioni delle bolle
    c=df["PIL_per_CO2"],  # Colore basato su efficienza ambientale
    cmap="viridis",
    alpha=0.7,
    edgecolors="black",
    linewidths=1
)

# Aggiunta etichette città con offset personalizzato
for i, row in df.iterrows():
    offset_x = 0.05 if row["Città"] != "Torino" else -0.1  # Evita sovrapposizioni
    plt.annotate(
        row["Città"],
        (row["Popolazione"] + offset_x, row["PIL"]),
        fontsize=10,
        ha='center'
    )

# Evidenziazione città con miglior rapporto PIL/CO2
best_city = df.loc[df["PIL_per_CO2"].idxmax()]
plt.annotate(
    f"Migliore efficienza\n{best_city['Città']}: {best_city['PIL_per_CO2']:.1f} mld€/kt CO2",
    xy=(best_city["Popolazione"], best_city["PIL"]),
    xytext=(1.8, 180),
    arrowprops=dict(facecolor='red', shrink=0.05),
    bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5)
)

# Personalizzazione grafico
plt.title("Relazione tra Popolazione, PIL ed Emissioni CO2 (2022)", pad=20, fontweight='bold')
plt.xlabel("Popolazione (milioni)", labelpad=10)
plt.ylabel("PIL (miliardi di €)", labelpad=10)

# Aggiunta legende
cbar = plt.colorbar(scatter)
cbar.set_label("Efficienza Ambientale (mld €/kt CO2)", rotation=270, labelpad=20)

# Legenda dimensioni bolle (simulata)
for co2 in [4000, 6000, 7000]:
    plt.scatter([], [], s=co2*0.8, c='gray', alpha=0.5, edgecolors='black', label=f'{co2} kt CO2')
plt.legend(title="Emissioni CO2", loc="upper right", bbox_to_anchor=(1.3, 1))

plt.grid(alpha=0.3)
plt.tight_layout()

# -----------------------------------------
# PASSO 3: Generazione report automatico
# -----------------------------------------
report = f"""
ANALISI POLITICHE URBANE - 2022
---------------------------------
1) EFFICIENZA AMBIENTALE:
   - Miglior performance: {best_city['Città']} ({best_city['PIL_per_CO2']:.1f} mld€/kt CO2)
   - Napoli ha il PIL/CO2 più basso ({df.loc[3, 'PIL_per_CO2']:.1f})

2) PIL PRO-CAPITE:
   - Milano leader: {df.loc[0, 'PIL_pro_capite']:.1f} mld€/mln abitanti
   - Differenza Nord-Sud: +{(df.loc[0, 'PIL_pro_capite']/df.loc[3, 'PIL_pro_capite']-1)*100:.0f}% vs Napoli

3) PRIORITÀ:
   - Investire in tecnologie verdi a Napoli/Roma
   - Replicare best practice di {best_city['Città']} sulle emissioni
"""

print("\nREPORT PER POLICYMAKER:")
print(report)

# Salvataggio grafico e report
plt.savefig('bubble_chart_politiche_urbane.png', dpi=300, bbox_inches='tight')
with open('report_politiche_urbane.txt', 'w') as f:
    f.write(report)

plt.show()

 

Dataset arricchito:
    Città  Popolazione  PIL   CO2  PIL_per_CO2  PIL_pro_capite
0  Milano          1.4  200  5000    40.000000      142.857143
1    Roma          2.8  150  7000    21.428571       53.571429
2  Torino          0.9   80  4000    20.000000       88.888889
3  Napoli          3.1   90  6000    15.000000       29.032258

REPORT PER POLICYMAKER:

ANALISI POLITICHE URBANE - 2022
---------------------------------
1) EFFICIENZA AMBIENTALE:
   - Miglior performance: Milano (40.0 mld€/kt CO2)
   - Napoli ha il PIL/CO2 più basso (15.0)

2) PIL PRO-CAPITE:
   - Milano leader: 142.9 mld€/mln abitanti
   - Differenza Nord-Sud: +392% vs Napoli

3) PRIORITÀ:
   - Investire in tecnologie verdi a Napoli/Roma
   - Replicare best practice di Milano sulle emissioni

Miglior rapporto:

Milano [math]\frac{200}{5} = 40[/math] mld €/kt CO2.

Forse potrebbe interessarti anche:  Cos'è il Paradosso di Braess? Simulazione con Python, NetworkX ed esempi applicati al marketing

Report:

“Milano ha l’economia più green (40 mld/kt CO2), nonostante Roma e Napoli abbiano più abitanti. Torino, pur piccola, ha emissioni proporzionalmente alte. Prioritizzare politiche verdi al Sud.”

La Narrazione Strategica (Bubble Chart)

Questo è il culmine del data storytelling. Il bubble chart è un capolavoro di densità informativa: racconta una storia a 3 o 4 dimensioni (Popolazione, PIL, CO2, e l’efficienza codificata nel colore) su un grafico 2D. L’insight qui è sofisticato: non si tratta di chi è “migliore” in assoluto, ma di chi è più efficiente. Milano, pur non essendo la più grande o la più ricca in termini assoluti, emerge come leader in termini di rapporto PIL/CO2. L’annotazione e il report per il policymaker trasformano un’analisi complessa in una raccomandazione chiara e attuabile.

Perfetto per analisi competitive, portfolio analysis o, come nell’esempio, per confronti strategici tra diverse unità di business, paesi o città. Permette di identificare gli “outlier virtuosi” da cui imparare e le aree critiche su cui intervenire.

Strategie generali:

  • Grafici semplici (barre, linee) per messaggi diretti.
  • Heatmap per correlazioni complesse.
  • Bubble chart per relazioni multivariate.
  • Annotazioni per guidare l’attenzione.
  • Storytelling chiaro: problema → dati → soluzione.
Pubblicità