Laboratorio comuni — il machine learning sui dati d'Italia
Il machine learning sui dati pubblici, **direttamente nel tuo browser**: scegli una regione, raggruppa i suoi comuni per reddito, età e presenza straniera, trova quelli fuori norma, scopri quali indicatori si muovono insieme. I modelli (scikit-learn) girano sul tuo dispositivo: la prima esecuzione scarica la libreria (~60 MB, una tantum), poi tutto è istantaneo e reattivo.
spazio dati «laboratorio-comuni» — il collante della suite: sul tuo dispositivo, multi-utente con un invito (link o QR), cifrato end-to-end.
oppure aprila come app unica
Cosa registra
- Codice regione (2 cifre)
- Codice regione (2 cifre)
- Reddito medio € (es. 22000)
- Età media (es. 46)
- Codice regione (2 cifre)
Composta da 6 micro app
Un compito ciascuna, sugli stessi dati: ogni micro app legge e scrive lo spazio dati condiviso «laboratorio-comuni» (col tuo consenso, chiesto alla prima apertura). Tutto resta sul tuo dispositivo — e con un solo invito l'intera suite diventa multi-utente, cifrata end-to-end.
Come funziona
Questa è un'app Reactive: un semplice documento di testo che il browser trasforma in un'app funzionante — moduli, dati dal vivo, tabelle, statistiche e persino grafici, senza installare né configurare nulla.
- Usala subito. Premi «Usa questa app»: si apre subito nel tuo browser, già pronta, con i dati salvati solo sul tuo dispositivo.
- I tuoi dati restano tuoi. Tutto è memorizzato in locale sul dispositivo, separato per ogni app: niente account, niente server, niente da configurare.
- Rendila tua. Il testo qui sotto è l'app per intero: copialo, cambia campi, viste e parole, e le modifiche diventano subito interattive.
- Condividila come file. Un'app è un semplice file: salvalo, esportalo o condividilo con un link; e con la collaborazione cifrata più persone modificano gli stessi dati in tempo reale.
Il testo dell'app
# 🧪 Laboratorio comuni
Il machine learning sui dati pubblici, **direttamente nel tuo browser**: scegli
una regione, raggruppa i suoi comuni per reddito, età e presenza straniera,
trova quelli fuori norma, scopri quali indicatori si muovono insieme. I modelli
(scikit-learn) girano sul tuo dispositivo: la prima esecuzione scarica la
libreria (~60 MB, una tantum), poi tutto è istantaneo e reattivo.
:::::page{title="Gruppi" icon="🗺️"}
## I gruppi di comuni della regione
Codice regione a due cifre (es. `01` Piemonte, `03` Lombardia, `05` Veneto,
`08` Emilia-Romagna, `09` Toscana, `12` Lazio, `15` Campania, `19` Sicilia).
Poi muovi lo slider: il clustering si ricalcola e la mappa si ricolora.
::input[reg]{value="08" placeholder="Codice regione (2 cifre)"}
::od-query{into="comuni" sql="SELECT c.comune AS comune, c.geojson AS geojson, r.reddito_medio AS reddito, i.eta_media AS eta, i.perc_stranieri AS stranieri FROM istat_confini_comuni c JOIN mef_redditi r ON r.codice_istat = c.codice_istat JOIN istat_indicatori i ON i.codice_istat = c.codice_istat WHERE c.cod_reg = '{#reg}'" limit="2000"}
::range[k]{min="2" max="8" value="4" legend="Numero di gruppi"}
::ml-cluster{data="comuni" features="reddito,eta,stranieri" k="#k" into="gruppi"}
:::map{path="gruppi" geojson="geojson" fill="cluster" height="26rem"}
**{comune}** — gruppo {cluster} · reddito {reddito} € · età media {eta}
:::
Comuni simili finiscono nello stesso gruppo: il K-means lavora su reddito
medio, età media e percentuale di stranieri (standardizzati). I colori della
mappa sono i gruppi.
:::::
:::::page{title="Fuori norma" icon="🔍"}
## I comuni fuori norma
L'Isolation Forest assegna a ogni comune un punteggio di anomalia (0-1):
alto = il comune non somiglia agli altri della regione. Clicca le intestazioni
della tabella per ordinare.
::ml-anomaly{data="comuni" features="reddito,eta,stranieri" contamination="0.05" into="anomali"}
:::table{path="anomali" headers="Comune,Reddito €,Età,Stranieri %,Anomalia,Fuori norma" pagesize="10"}
{comune} | {reddito} | {eta} | {stranieri} | {anomalia} | {flag}
:::
:::::
:::::page{title="Correlazioni" icon="🔗"}
## Cosa si muove insieme
La correlazione di Pearson tra gli indicatori dei comuni della regione:
`r` vicino a **1** = crescono insieme, vicino a **-1** = opposte, vicino a
**0** = indipendenti.
::ml-correlate{data="comuni" features="reddito,eta,stranieri" into="correlazioni"}
:::table{path="correlazioni" headers="Indicatore A,Indicatore B,Correlazione r" pagesize="10" search="false"}
{a} | {b} | {r}
:::
::chart-bar{data="correlazioni" x="a" y="r" height="14rem"}
:::::
:::::page{title="Confronto" icon="📏"}
## Dove ti collochi
Il benchmarking in una riga: scegli la regione, scrivi un **reddito medio**
(per esempio quello del tuo comune) e scopri la sua posizione tra i comuni.
Posizione e percentile si aggiornano da soli quando cambi valore o regione.
::input[reg]{value="08" placeholder="Codice regione (2 cifre)"}
::od-query{into="comuniConf" sql="SELECT c.comune AS comune, r.reddito_medio AS reddito, i.eta_media AS eta FROM istat_confini_comuni c JOIN mef_redditi r ON r.codice_istat = c.codice_istat JOIN istat_indicatori i ON i.codice_istat = c.codice_istat WHERE c.cod_reg = '{#reg}'" limit="2000"}
::input[mioReddito]{value="22000" placeholder="Reddito medio € (es. 22000)"}
Su :count[nc]{path="comuniConf"} comuni della regione, questo reddito è al
**:rank[pos]{path="comuniConf" field="reddito" value="#mioReddito"}° posto**
(1° = il più alto) — **:percentile[pct]{path="comuniConf" field="reddito" value="#mioReddito"}° percentile**
(quota di comuni con un reddito più basso).
E per l'età media:
::input[miaEta]{value="46" placeholder="Età media (es. 46)"}
Un'età media di :value[ev]{ref="#miaEta"} anni è al
**:percentile[peta]{path="comuniConf" field="eta" value="#miaEta"}° percentile**
della regione.
:::::
:::::page{title="Domande" icon="💬"}
## Chiedi ai dati
Domande in linguaggio naturale sui comuni della regione: *"top 5 comuni per
reddito"*, *"quanti comuni hanno più del 15% di stranieri?"*, *"reddito medio
dei comuni con età sopra i 50"*. Il modello produce un piano di
interrogazione vincolato (non può inventare colonne) eseguito nel browser: la
risposta numerica appare sotto la domanda, le righe risultato nella tabella.
::input[reg]{value="08" placeholder="Codice regione (2 cifre)"}
::od-query{into="datiDomande" sql="SELECT c.comune AS comune, r.reddito_medio AS reddito, i.eta_media AS eta, i.perc_stranieri AS stranieri, p.popolazione AS popolazione FROM istat_confini_comuni c JOIN mef_redditi r ON r.codice_istat = c.codice_istat JOIN istat_indicatori i ON i.codice_istat = c.codice_istat JOIN istat_popolazione p ON p.codice_istat = c.codice_istat WHERE c.cod_reg = '{#reg}'" limit="2000"}
::ai-query{data="datiDomande" into="risposte"}
:::table{path="risposte" pagesize="8" search="false"}
{comune} | {reddito} | {eta} | {stranieri} | {popolazione} | {valore}
:::
Serve l'assistente AI configurato (modello nel browser od Ollama: i dati non
lasciano il dispositivo).
:::::
:::::page{title="Previsioni" icon="🎯"}
## Quanto reddito "spiegano" età e stranieri?
Una regressione impara la relazione tra gli indicatori demografici e il
reddito medio dei comuni della regione, poi lo **stima** per ognuno: dove la
stima sbaglia di più, c'è qualcosa che i soli dati demografici non spiegano.
L'**R²** nello stato dice quanta parte del reddito è spiegata dal modello.
::ml-predict{data="comuni" features="eta,stranieri" target="reddito" model="forest" into="stime"}
Ogni punto è un comune: sull'asse orizzontale il reddito **reale**, su quello
verticale la **stima** del modello. Più i punti stanno sulla diagonale, meglio
il modello spiega.
::chart-scatter{data="stime" x="reddito" y="previsione" height="20rem"}
:::table{path="stime" headers="Comune,Reddito reale €,Stima modello €" pagesize="8"}
{comune} | {reddito} | {previsione}
:::
:::::
---
I modelli girano **sul tuo dispositivo** (scikit-learn via Pyodide): i dati
scaricati dal servizio open restano nel browser e nessun tuo dato lo lascia. Le
fonti sono ufficiali: **redditi IRPEF** del MEF (CC BY 3.0), **indicatori
socio-demografici** e **confini** ISTAT (CC BY 4.0), agganciati da soli col
codice ISTAT del comune.