Narrazione basata sui dati — Deliverable 3.3

BRISWA 2.0 • Work Package 3

Negativo Neutro Positivo

Narrazione basata sui dati: un Policy Brief e una Guida Didattica — Questo documento ha una doppia finalità. Da un lato è redatto come un policy brief, utilizzando dati reali su razzismo e inclusione per evidenziare scarti tra ottimismo, fiducia ed esperienze vissute. Dall’altro lato costituisce una guida didattica, mostrando passo dopo passo come creare grafici e infografiche in Python e trasformarli in strumenti narrativi. La combinazione consente non solo di comprendere i risultati, ma anche di apprendere i metodi — così da applicare le stesse tecniche in altri contesti di decisione sociale.

Oltre alla funzione didattica, i dati suggeriscono un ottimismo diffuso verso istituzioni e programmi — probabilmente legato alla giovane età della maggior parte dei rispondenti — insieme alla persistenza di esperienze discriminatorie e a un supporto percepito non sempre sufficiente.

GraficoPythonDati

Percezioni istituzionali (con riquadro età)

Barre orizzontali divergenti (negativo / neutro / positivo) per: Supporto all’integrazione, Programmi che affrontano il razzismo, Efficacia sull’inclusione. A destra, un riquadro mostra la distribuzione per età (campione a prevalenza studentesca, moda ≈ 20–21).

Barre divergenti e riquadro età
Clicca l’immagine per ingrandire Scarica PDF
import matplotlib.pyplot as plt
from matplotlib.patches import Patch
import numpy as np

# --------------------------
# 1) Dati su fiducia/efficacia
# --------------------------
support_counts   = {1: 2, 2: 4, 3: 29, 4: 33, 5: 69}
prog_counts_raw  = {'No': 17, 'No, Not sure': 1, 'Not sure': 32, 'Yes': 86, 'Yes, No': 1}
effective_counts = {1: 1, 2: 4, 3: 26, 4: 51, 5: 55}

def likert_to_buckets(counts):
    total = sum(counts.values())
    neg = counts.get(1, 0) + counts.get(2, 0)
    neu = counts.get(3, 0)
    pos = counts.get(4, 0) + counts.get(5, 0)
    return neg, neu, pos, total

def yns_to_buckets(counts):
    neg = counts.get('No', 0)
    pos = counts.get('Yes', 0)
    neu = counts.get('Not sure', 0) + counts.get('Yes, No', 0) + counts.get('No, Not sure', 0)
    total = neg + neu + pos
    return neg, neu, pos, total

def to_pct(neg, neu, pos, total):
    f = 100.0 / total if total else 0
    return neg*f, neu*f, pos*f

sN, sU, sP = to_pct(*likert_to_buckets(support_counts))
pN, pU, pP = to_pct(*yns_to_buckets(prog_counts_raw))
eN, eU, eP = to_pct(*likert_to_buckets(effective_counts))

labels = [
    "Support for integration",
    "Programmes address racism",
    "Effectiveness on inclusion"
]
rows = [(sN, sU, sP), (pN, pU, pP), (eN, eU, eP)]

# Colori coerenti
NEG_COLOR = "#d73027"
NEU_COLOR = "#f7f7f7"
POS_COLOR = "#2b8cbe"

# --------------------------
# 2) Distribuzione età
# --------------------------
ages = [
    24,20,22,22,18,21,19,25,20,20,21,19,20,18,24,22,19,23,22,21,
    21,21,20,20,19,18,19,21,20,22,21,20,20,20,20,20,20,19,20,20,
    20,20,20,23,26,25,20,21,22,20,23,19,20,22,20,19,20,20,19,20,
    19,20,21,20,20,21,20,18,21,19,20,19,20,20,23,22,21,22,20,20,
    20,19,20,20,20,19,20,22,20,20,20,20,19,20,20,21,20,22,20,20,
    19,20,22,21,21,23,21,20,20,20,20,22,19,19,21,22,24,19,19,22,
    28,20,20,21,20,19,20,22,18,20,22,19,20
]

ages_arr = np.array(ages, dtype=int)
bins = np.arange(ages_arr.min()-0.5, ages_arr.max()+1.5, 1)  # bin di 1 anno
counts, bin_edges = np.histogram(ages_arr, bins=bins)
centers = (bin_edges[:-1] + bin_edges[1:]) / 2.0

# Media mobile semplice
kernel = np.array([1, 2, 1], dtype=float)
kernel = kernel / kernel.sum()
smooth = np.convolve(counts, kernel, mode='same').astype(float)
if smooth.max() > 0:
    smooth = smooth / smooth.max()  # normalizza 0–1

# Moda dell'età
unique, cts = np.unique(ages_arr, return_counts=True)
mode_age = int(unique[np.argmax(cts)])

# --------------------------
# Grafico principale + inset
# --------------------------
fig, ax = plt.subplots(figsize=(12, 6))

# Barre orizzontali divergenti
ypos = list(range(len(rows)))[::-1]
for y, (neg, neu, pos) in zip(ypos, rows):
    ax.barh(y, -neg, left=0, align='center', color=NEG_COLOR, edgecolor='none')
    ax.barh(y, neu, left=0, align='center', color=NEU_COLOR, edgecolor='none')
    ax.barh(y, pos, left=neu, align='center', color=POS_COLOR, edgecolor='none')

ax.set_yticks(ypos)
ax.set_yticklabels(labels)
ax.set_xlim(-100, 100)
ax.set_xlabel("Share of respondents (%)")
ax.axvline(0, linewidth=1, color="#999999")
ax.set_title("Institutional perceptions")

# Annotazioni percentuali
def annotate(x_left, width, y, text):
    if abs(width) < 4: return
    ax.text(x_left + width/2, y, text, va='center', ha='center', fontsize=9)

for y, (neg, neu, pos) in zip(ypos, rows):
    annotate(-neg, neg, y, f"{neg:.0f}%")
    annotate(0, neu, y, f"{neu:.0f}%")
    annotate(neu, pos, y, f"{pos:.0f}%")

# Legenda
handles = [
    Patch(facecolor=NEG_COLOR, edgecolor='none', label="Negative (1–2 or No)"),
    Patch(facecolor=NEU_COLOR, edgecolor='none', label="Neutral (3 or Not sure / mixed)"),
    Patch(facecolor=POS_COLOR, edgecolor='none', label="Positive (4–5 or Yes)")
]
ax.legend(handles=handles, loc="upper center", ncol=3, bbox_to_anchor=(0.5, 1.12), frameon=False)

# Inset distribuzione età
mini = ax.inset_axes([0.15, 0.15, 0.22, 0.55])
mini.set_facecolor("white")
mini.plot(centers, smooth, color=POS_COLOR, lw=2)
mini.set_xticks([mode_age]); mini.set_xticklabels([str(mode_age)])
mini.set_yticks([]); mini.set_ylabel("age distribution", rotation=90)
mini.spines["top"].set_visible(False); mini.spines["right"].set_visible(False)
mini.spines["bottom"].set_alpha(0.4); mini.spines["left"].set_alpha(0.4)
mini.set_title("Age distribution", pad=6, fontsize=10)

plt.tight_layout()
plt.show()
Supporto all’integrazione (Likert 1–5)
ValoreConteggio
12
24
329
433
569
Programmi che affrontano il razzismo (Sì/No/Non so)
RispostaConteggio
Yes86
No17
Not sure32
Yes, No1
No, Not sure1
Efficacia sull’inclusione (Likert 1–5)
ValoreConteggio
11
24
326
451
555
GraficoPython

Hai vissuto episodi di discriminazione razziale?

Donut chart: quota di rispondenti che hanno dichiarato discriminazione (al centro la % di “Sì”). Il riquadro a lato del grafico riassume i contesti tra chi ha risposto “Sì”.

Donut: esperienza di discriminazione
Clicca l’immagine per ingrandire Scarica PDF
import matplotlib.pyplot as plt

# Dati sondaggio
discr_experience = {"No": 109, "Yes": 26, "Yes, No": 2}
n_total = sum(discr_experience.values())
n_yes = 26 + 2   # Yes + Yes, No

# Contesti tra i 28 "Yes"
context_info = [
    "Of those who responded Yes:",
    "57.1% experienced discrimination in a public space",
    "46.5% experienced discrimination in an educational setting",
    "39.3% experienced discrimination in Social media",
    "7.1% experienced discrimination in a football setting"
]

# Donut
fig, ax = plt.subplots(figsize=(8, 6))
sizes = list(discr_experience.values())
labels = list(discr_experience.keys())
colors = ["#1f77b4", "#d62728", "#bdbdbd"]

wedges, _ = ax.pie(sizes, startangle=90, colors=colors)

# foro centrale
centre_circle = plt.Circle((0, 0), 0.60, fc="white")
ax.add_artist(centre_circle)

# testo centrale
pct_yes = round(100.0 * n_yes / n_total, 1)
ax.text(0, 0.07, f"{pct_yes}%", ha="center", va="center", fontsize=12, weight="bold")
ax.text(0, -0.06, "reported ‘Yes’", ha="center", va="center", fontsize=11)

# riquadro contesti
legend_text = "\n".join(context_info)
ax.text(1.2, 0, legend_text,
        ha="left", va="center",
        fontsize=10,
        bbox=dict(boxstyle="round,pad=0.5", fc="white", ec="0.5"))

ax.set_title("Experienced racial discrimination?")
ax.set_aspect('equal')
plt.tight_layout()
plt.show()
GraficoPython

Sport: strumento di integrazione & luogo di discriminazione

In alto a sinistra: tipologie di programma (sport evidenziato). In alto a destra: convinzione Sì/No che lo sport contrasti il razzismo (Sì evidenziato). In basso: contesti tra chi ha segnalato discriminazione (calcio evidenziato al 7,1%).

Sport e contesti di discriminazione
Clicca l’immagine per ingrandire Scarica PDF
import matplotlib.pyplot as plt
from matplotlib.patches import Patch
import math

# Dati
program_groups = {
    "Sport as tool for integration": 1,
    "Community forums & dialogue": 1,
    "Support for vulnerable groups": 1,
    "Awareness-raising": 1,
    "Education & training": 1
}
sport_counts = {"Yes": 126, "No": 11}
contexts_pct = {
    "Public space": 57.1,
    "Educational setting": 46.5,
    "Social media": 39.3,
    "Football setting": 7.1
}

# Palette
HIGHLIGHT = "#2b8cbe"
PALE      = "#cdd9e5"
NO_TONE   = "#c7c7c7"
PIE_PALE  = "#e6e6e6"
PIE_HI    = "#d73027"

# Layout 2x2
fig = plt.figure(figsize=(12.5, 8), constrained_layout=True)
gs  = fig.add_gridspec(2, 2, height_ratios=[1, 1.1])

# --- Top-left: tipologie di programma (sport evidenziato) ---
ax0 = fig.add_subplot(gs[0, 0])
labels_pg = list(program_groups.keys())
values_pg = list(program_groups.values())
colors_pg = [HIGHLIGHT if "Sport" in lab else PALE for lab in labels_pg]
ax0.barh(labels_pg, values_pg, color=colors_pg)
ax0.set_xlim(0, 1.2)
ax0.set_xticks([])
ax0.set_title("Anti-racism program types", pad=6)
for i, lab in enumerate(labels_pg):
    ax0.text(0.05, i, lab, va="center", ha="left", fontsize=10, color="black")

# --- Top-right: credenza Sì/No sull'efficacia dello sport ---
ax1 = fig.add_subplot(gs[0, 1])
yn_labels = list(sport_counts.keys())
yn_vals   = list(sport_counts.values())
yn_colors = [HIGHLIGHT if lab == "Yes" else NO_TONE for lab in yn_labels]
bars = ax1.bar(yn_labels, yn_vals, color=yn_colors)
ax1.set_title("Do students see sport as an effective anti-racism tool?", pad=6)
ax1.set_ylabel("Number of respondents")
for b in bars:
    ax1.text(b.get_x() + b.get_width()/2, b.get_height() + 0.8,
             f"{int(b.get_height())}", ha="center", va="bottom", fontsize=10)

# --- Bottom: contesti (evidenzia 'Football') ---
ax2 = fig.add_subplot(gs[1, :])
labels_ctx = list(contexts_pct.keys())
values_ctx = list(contexts_pct.values())
colors_ctx = [PIE_HI if "Football" in lab else PIE_PALE for lab in labels_ctx]
wedges, _ = ax2.pie(values_ctx, colors=colors_ctx, startangle=90)
ax2.set_title("Where discrimination occurs (among those who reported it)", pad=8)

legend_handles = [Patch(facecolor=PIE_HI,  label="Football setting"),
                  Patch(facecolor=PIE_PALE, label="Other contexts")]
ax2.legend(handles=legend_handles, loc="center left", bbox_to_anchor=(0.02, 0.5), frameon=False)

# Etichetta 7.1% sullo spicchio 'Football'
fb_idx = labels_ctx.index("Football setting")
fb_wedge = wedges[fb_idx]
angle = (fb_wedge.theta2 + fb_wedge.theta1) / 2
r = 1.1
x = r * math.cos(math.radians(angle))
y = r * math.sin(math.radians(angle))
ax2.text(x, y, "7.1%", ha="center", va="center", fontsize=11, weight="bold")

plt.show()
GraficoPython

Indicatori demografici

Doppio donut: anello interno = donne tra i nuovi nazionali (57%); anello esterno = donne migranti in povertà severa (16,7%).

Doppio donut: indicatori demografici
Clicca l’immagine per ingrandire Scarica PDF
import matplotlib.pyplot as plt
from matplotlib.patches import Patch

women_share = 57.0
severe_poverty = 16.7

START = 90
OUTER_R, OUTER_W = 1.3, 0.28
INNER_R, INNER_W = 0.85, 0.28

BLUE_HI, BLUE_REST = "#1f77b4", "#dbe9f6"
RED_HI, RED_REST = "#d62728", "#f8c9c9"

fig, ax = plt.subplots(figsize=(7.2, 7.2))

ax.pie([severe_poverty, 100-severe_poverty],
       radius=OUTER_R, startangle=START,
       colors=[RED_HI, RED_REST],
       wedgeprops=dict(width=OUTER_W, edgecolor="white"))

ax.pie([women_share, 100-women_share],
       radius=INNER_R, startangle=START,
       colors=[BLUE_HI, BLUE_REST],
       wedgeprops=dict(width=INNER_W, edgecolor="white"))

ax.set(aspect="equal"); ax.axis("off")
ax.set_title("Demographic indicators", pad=10, fontsize=14, weight="bold")
handles = [Patch(facecolor=RED_HI, label=f"Migrant women in severe poverty (Spain): {severe_poverty:.1f}%"),
           Patch(facecolor=BLUE_HI, label=f"Women among new nationals (Spain): {women_share:.0f}%")]
ax.legend(handles=handles, loc="center left", bbox_to_anchor=(1.05, 0.0), frameon=False)
plt.tight_layout(); plt.show()

Implicazioni

Le istituzioni dovrebbero rafforzare meccanismi che aumentano la fiducia e garantiscono supporto quando si verifica discriminazione. I programmi sportivi hanno un forte valore simbolico, ma vanno dotati di meccanismi concreti che riducano attivamente razzismo e discriminazioni. Infine, la realtà demografica deve guidare la progettazione e l’implementazione delle politiche fin dall’inizio.

Torna in alto