CatalogoDati pubblici ›Riferimento
AI

I soldi del territorio — spesa, PNRR e appalti del tuo comune

La domanda civica per eccellenza — *dove vanno i soldi pubblici?* — con i dati ufficiali, comune per comune: quanto paga il tuo comune e per cosa (cassa SIOPE), che progetti sono finanziati da fondi di coesione e PNRR e a che punto sono, chi vince gli appalti. Le analisi girano NEL browser: nessun dato tuo lascia il dispositivo.

▶ Usa questa app

spesa pubblicabilanciocomunesiopeopenbdappnrrcoesioneopencupappaltianactrasparenzaaccountabilitydashboardanomalieprevisione

Cosa registra

  • Cerca il comune per nome…
  • Es. qual è la voce più pesante per abitante?
  • Es. manutenzione strade, mensa scolastica, illuminazione…

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.

  1. Usala subito. Premi «Usa questa app»: si apre subito nel tuo browser, già pronta, con i dati salvati solo sul tuo dispositivo.
  2. I tuoi dati restano tuoi. Tutto è memorizzato in locale sul dispositivo, separato per ogni app: niente account, niente server, niente da configurare.
  3. Rendila tua. Il testo qui sotto è l'app per intero: copialo, cambia campi, viste e parole, e le modifiche diventano subito interattive.
  4. 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
# 💰 I soldi del territorio

La domanda civica per eccellenza — *dove vanno i soldi pubblici?* — con i
dati ufficiali, comune per comune: quanto paga il tuo comune e per cosa
(cassa SIOPE), che progetti sono finanziati da fondi di coesione e PNRR e a
che punto sono, chi vince gli appalti. Le analisi girano NEL browser:
nessun dato tuo lascia il dispositivo.

:::::page{title="Il mio comune" icon="🏛️"}

## La spesa del tuo comune

Cerca il comune per nome e **scegli dal menu**: tutte le pagine si
aggiornano da sole.

::input[cercaComune]{value="Bologna" placeholder="Cerca il comune per nome…"}

::od-query{into="comuniTrovati" sql="SELECT codice_istat AS codice_istat, comune || ' (' || sigla || ')' AS etichetta FROM istat_confini_comuni WHERE upper(strip_accents(comune)) LIKE '%' || upper(strip_accents('{#cercaComune}')) || '%' ORDER BY length(comune), comune" limit="20"}

::select-from[comune]{from="comuniTrovati" label="etichetta" value="codice_istat"}

::od-query{into="kpiSpesa" sql="SELECT round(sum(importo)/1e6,1) AS milioni, round(sum(importo)/max(popolazione)) AS procapite, max(anno) AS anno FROM siope_spese WHERE codice_istat='{#comune}' AND mese=12 AND anno=(SELECT max(anno) FROM siope_spese WHERE mese=12 AND codice_istat='{#comune}') AND categoria_codice NOT IN ('7.01','7.02','0.00')"}

::od-query{into="kpiVar" sql="SELECT coalesce(round(100.0*(a.tot-b.tot)/nullif(b.tot,0),1)::VARCHAR || '%','n.d.') AS variazione, round(a.inv/1e6,1) AS investimenti FROM (SELECT sum(importo) AS tot, sum(importo) FILTER (WHERE categoria_codice LIKE '2.%') AS inv FROM siope_spese WHERE codice_istat='{#comune}' AND mese=12 AND anno=(SELECT max(anno) FROM siope_spese WHERE mese=12 AND codice_istat='{#comune}')) a, (SELECT sum(importo) AS tot FROM siope_spese WHERE codice_istat='{#comune}' AND mese=12 AND anno=(SELECT max(anno) FROM siope_spese WHERE mese=12 AND codice_istat='{#comune}')-1) b"}

::::grid{cols="3"}
:::cards{path="kpiSpesa" search="false"}
💶 **{milioni} mln €** pagati nel {anno} — **{procapite} €** per abitante
:::
---
:::cards{path="kpiVar" search="false"}
📈 **{variazione}** rispetto all'anno prima
:::
---
:::cards{path="kpiVar" search="false"}
🏗️ **{investimenti} mln €** di investimenti
:::
::::

::od-query{into="kpiRank" sql="SELECT posizione, totale, centile FROM (SELECT codice_istat, rank() OVER (ORDER BY pc DESC) AS posizione, count(*) OVER () AS totale, round(100*percent_rank() OVER (ORDER BY pc)) AS centile FROM (SELECT codice_istat, sum(importo)/max(popolazione) AS pc FROM siope_spese WHERE regione=(SELECT regione FROM siope_spese WHERE codice_istat='{#comune}' LIMIT 1) AND mese=12 AND anno=(SELECT anno FROM siope_spese WHERE mese=12 GROUP BY anno HAVING count(DISTINCT codice_istat)>=6500 ORDER BY anno DESC LIMIT 1) AND popolazione>0 AND categoria_codice NOT IN ('7.01','7.02','0.00') GROUP BY 1)) WHERE codice_istat='{#comune}'"}

::od-query{into="kpiDebito" sql="SELECT round((coalesce(sum(importo) FILTER (WHERE categoria_codice='1.07'),0)+coalesce(sum(importo) FILTER (WHERE categoria_codice LIKE '4.%'),0))/max(popolazione)) AS eur_ab FROM siope_spese WHERE codice_istat='{#comune}' AND mese=12 AND anno=(SELECT max(anno) FROM siope_spese WHERE mese=12 AND codice_istat='{#comune}')"}

::::grid{cols="2"}
:::cards{path="kpiRank" search="false"}
🏆 **{posizione}° su {totale}** comuni della regione per spesa per abitante — {centile}° percentile
:::
---
:::cards{path="kpiDebito" search="false"}
💳 Il debito costa **{eur_ab} € per abitante** l'anno (interessi + rimborso prestiti)
:::
::::

Sono pagamenti di **cassa** (quanto è uscito davvero, non quanto impegnato),
esclusi giri contabili e partite di giro. L'ultimo anno chiuso guida i
numeri; l'anno in corso appare nel trend come parziale.

## Per cosa spende — clicca una barra per filtrare

::od-query{into="spesaCategorie" sql="WITH pc AS (SELECT codice_istat, categoria_codice, categoria, sum(importo) AS imp, max(popolazione) AS pop FROM siope_spese WHERE regione=(SELECT regione FROM siope_spese WHERE codice_istat='{#comune}' LIMIT 1) AND mese=12 AND anno=(SELECT anno FROM siope_spese WHERE mese=12 GROUP BY anno HAVING count(DISTINCT codice_istat)>=6500 ORDER BY anno DESC LIMIT 1) AND popolazione>0 AND categoria_codice NOT IN ('7.01','7.02','0.00') GROUP BY 1,2,3), poptot AS (SELECT sum(pop) AS pop FROM (SELECT codice_istat, max(pop) AS pop FROM pc GROUP BY 1)) SELECT CASE categoria_codice WHEN '1.01' THEN 'Personale' WHEN '1.02' THEN 'Imposte' WHEN '1.03' THEN 'Beni e servizi' WHEN '1.04' THEN 'Trasferimenti' WHEN '1.07' THEN 'Interessi' WHEN '1.09' THEN 'Rimborsi entrate' WHEN '1.10' THEN 'Altre correnti' WHEN '2.02' THEN 'Investimenti' WHEN '2.03' THEN 'Contributi invest.' WHEN '2.05' THEN 'Altre c.capitale' WHEN '4.03' THEN 'Rimborso mutui' WHEN '5.01' THEN 'Anticipazioni' ELSE min(categoria) END AS voce, min(categoria) AS categoria, round(sum(imp) FILTER (WHERE codice_istat='{#comune}')/1e3) AS migliaia, round(sum(imp) FILTER (WHERE codice_istat='{#comune}')/nullif(max(pop) FILTER (WHERE codice_istat='{#comune}'),0)) AS procapite, round(sum(imp)/(SELECT pop FROM poptot)) AS media FROM pc GROUP BY categoria_codice ORDER BY migliaia DESC NULLS LAST" limit="30"}

::::dashboard{path="spesaCategorie"}
::chart-bar{data="spesaCategorie" x="voce" y="procapite,media" height="18rem"}

:::table{path="spesaCategorie" headers="Categoria,Migliaia €,€/ab (tu),€/ab (media regione)" search="false" pagesize="0"}
{categoria} | {migliaia} | {procapite} | {media}
:::
::::

Le due serie del grafico: il **tuo comune** contro la **media dei comuni
della regione**, in euro per abitante (ultimo anno a copertura piena).

## Dove si colloca il tuo comune

La distribuzione della spesa per abitante tra tutti i comuni della regione:
confronta la tua card qui sopra con la forma della campana.

::od-query{into="distribuzionePc" sql="SELECT round(sum(importo)/max(popolazione)) AS procapite FROM siope_spese WHERE regione=(SELECT regione FROM siope_spese WHERE codice_istat='{#comune}' LIMIT 1) AND mese=12 AND anno=(SELECT anno FROM siope_spese WHERE mese=12 GROUP BY anno HAVING count(DISTINCT codice_istat)>=6500 ORDER BY anno DESC LIMIT 1) AND popolazione>0 AND categoria_codice NOT IN ('7.01','7.02','0.00') GROUP BY codice_istat" limit="1600"}

::chart-histogram{data="distribuzionePc" field="procapite" bins="30"}

## Il trend: correnti e investimenti anno per anno

::od-query{into="trendSpesa" sql="SELECT anno AS anno, round(sum(importo) FILTER (WHERE categoria_codice LIKE '1.%')/1e6,2) AS correnti, round(sum(importo) FILTER (WHERE categoria_codice LIKE '2.%')/1e6,2) AS investimenti, round(sum(importo) FILTER (WHERE categoria_codice LIKE '1.%')/max(popolazione)) AS correnti_ab, round(sum(importo) FILTER (WHERE categoria_codice LIKE '2.%')/max(popolazione)) AS investimenti_ab, max(mese) AS mese FROM siope_spese WHERE codice_istat='{#comune}' GROUP BY 1 ORDER BY 1"}

::chart-line{data="trendSpesa" x="anno" y="correnti_ab,investimenti_ab" height="16rem"}

Il grafico è in **euro per abitante** (la versione onesta: un comune che
cresce di popolazione sembrerebbe spendere di più); la tabella in milioni.

:::table{path="trendSpesa" headers="Anno,Correnti (mln),Investimenti (mln),Mesi" search="false" pagesize="0" mark="mese<12:text-warning"}
{anno} | {correnti} | {investimenti} | {mese}
:::

L'anno con meno di 12 mesi (in giallo) è il cumulato parziale dell'anno in
corso.

## Chiedi ai dati

Una domanda in italiano sulle voci di spesa qui sopra (serve l'assistente AI
configurato; la risposta è calcolata sul tuo dispositivo).

::ai-query{data="spesaCategorie" into="rispostaSpesa" placeholder="Es. qual è la voce più pesante per abitante?"}

::print{label="Stampa la scheda"}

:::::

:::::page{title="Le entrate" icon="🧾"}

## Quanto incassa il tuo comune

L'altra metà del bilancio: da dove arrivano i soldi — tributi propri,
trasferimenti dallo Stato, tariffe e sanzioni.

::od-query{into="kpiEntrate" sql="SELECT round(sum(importo)/1e6,1) AS milioni, round(sum(importo)/max(popolazione)) AS procapite, max(anno) AS anno FROM siope_entrate WHERE codice_istat='{#comune}' AND mese=12 AND anno=(SELECT max(anno) FROM siope_entrate WHERE mese=12 AND codice_istat='{#comune}') AND categoria_codice NOT IN ('9.01','9.02','0.00')"}

::od-query{into="kpiAutonomia" sql="SELECT round(100.0*sum(importo) FILTER (WHERE categoria_codice LIKE '1.%' OR categoria_codice LIKE '3.%')/nullif(sum(importo) FILTER (WHERE categoria_codice LIKE '1.%' OR categoria_codice LIKE '2.%' OR categoria_codice LIKE '3.%'),0),1) AS autonomia FROM siope_entrate WHERE codice_istat='{#comune}' AND mese=12 AND anno=(SELECT max(anno) FROM siope_entrate WHERE mese=12 AND codice_istat='{#comune}')"}

::od-query{into="kpiMulte" sql="SELECT round(coalesce(sum(importo) FILTER (WHERE categoria_codice='3.02'),0)/max(popolazione)) AS eur_ab, round(coalesce(sum(importo) FILTER (WHERE categoria_codice='3.02'),0)/1e3) AS migliaia FROM siope_entrate WHERE codice_istat='{#comune}' AND mese=12 AND anno=(SELECT max(anno) FROM siope_entrate WHERE mese=12 AND codice_istat='{#comune}')"}

::::grid{cols="3"}
:::cards{path="kpiEntrate" search="false"}
🧾 **{milioni} mln €** incassati nel {anno} — **{procapite} €** per abitante
:::
---
:::cards{path="kpiAutonomia" search="false"}
🏛️ Autonomia finanziaria **{autonomia}%** (tributi e tariffe proprie sulle entrate correnti)
:::
---
:::cards{path="kpiMulte" search="false"}
🚔 Sanzioni e multe: **{eur_ab} €** per abitante ({migliaia} mila €)
:::
::::

Autonomia alta = il comune si regge sulle entrate proprie; bassa = dipende
dai trasferimenti di Stato e Regione.

## Da dove arrivano i soldi — clicca per filtrare

::od-query{into="entrateCategorie" sql="SELECT categoria AS categoria, round(sum(importo)/1e3) AS migliaia, round(sum(importo)/max(popolazione)) AS procapite FROM siope_entrate WHERE codice_istat='{#comune}' AND mese=12 AND anno=(SELECT max(anno) FROM siope_entrate WHERE mese=12 AND codice_istat='{#comune}') AND categoria_codice NOT IN ('9.01','9.02','0.00') GROUP BY 1 ORDER BY 2 DESC" limit="30"}

::::dashboard{path="entrateCategorie"}
::chart-bar{data="entrateCategorie" x="categoria" y="procapite" height="18rem"}

:::table{path="entrateCategorie" headers="Voce,Migliaia €,€ per abitante" search="false" pagesize="0"}
{categoria} | {migliaia} | {procapite}
:::
::::

## Entrate e spese: il saldo di cassa

::od-query{into="saldoCassa" sql="SELECT e.anno AS anno, e.entrate AS entrate, s.spese AS spese, round(e.entrate - s.spese,2) AS saldo FROM (SELECT anno, round(sum(importo)/1e6,2) AS entrate FROM siope_entrate WHERE codice_istat='{#comune}' AND categoria_codice NOT IN ('9.01','9.02','0.00') GROUP BY 1) e JOIN (SELECT anno, round(sum(importo)/1e6,2) AS spese FROM siope_spese WHERE codice_istat='{#comune}' AND categoria_codice NOT IN ('7.01','7.02','0.00') GROUP BY 1) s USING (anno) ORDER BY e.anno"}

::chart-line{data="saldoCassa" x="anno" y="entrate,spese" height="16rem"}

:::table{path="saldoCassa" headers="Anno,Entrate (mln),Spese (mln),Saldo (mln)" search="false" pagesize="0" mark="saldo<0:text-error font-bold"}
{anno} | {entrate} | {spese} | {saldo}
:::

Saldo negativo (in rosso) = in quell'anno è uscito più di quanto è entrato:
di per sé non è un allarme (gli investimenti si pagano anche con avanzi
accumulati), ma una serie di rossi consecutivi racconta una storia.

:::::

:::::page{title="PNRR e progetti" icon="🏗️"}

## I progetti finanziati sul territorio

Fondi di coesione, PNRR e investimenti pubblici tracciati dal CUP: cosa è
stato finanziato nel comune scelto, con che soldi e a che punto è.

::od-query{into="kpiCoesione" sql="SELECT count(*) AS progetti, round(sum(finanz_totale_pubblico)/1e6,1) AS milioni, round(100.0*sum(costo_realizzato)/nullif(sum(finanz_totale_pubblico),0),1) AS avanzamento FROM opencoesione WHERE codice_istat='{#comune}'"}

::od-query{into="kpiConclusi" sql="SELECT count(*) FILTER (WHERE stato_progetto ILIKE '%conclus%') AS conclusi, count(*) FILTER (WHERE stato_progetto NOT ILIKE '%conclus%') AS in_corso FROM opencoesione WHERE codice_istat='{#comune}'"}

::::grid{cols="3"}
:::cards{path="kpiCoesione" search="false"}
📦 **{progetti}** progetti per **{milioni} mln €** di finanziamento pubblico
:::
---
:::cards{path="kpiCoesione" search="false"}
⏱️ Avanzamento economico medio **{avanzamento}%**
:::
---
:::cards{path="kpiConclusi" search="false"}
✅ **{conclusi}** conclusi — **{in_corso}** in corso
:::
::::

## I progetti più grandi, tema per tema

::od-query{into="progettiComune" sql="SELECT titolo AS titolo, tema AS tema, ciclo AS ciclo, round(finanz_totale_pubblico/1e3) AS migliaia, round(100.0*costo_realizzato/nullif(finanz_totale_pubblico,0)) AS avanzamento, stato_progetto AS stato FROM opencoesione WHERE codice_istat='{#comune}' ORDER BY finanz_totale_pubblico DESC" limit="200"}

::::dashboard{path="progettiComune"}
::chart-pie{data="temiComune" label="tema" value="migliaia" height="16rem"}

::od-query{into="temiComune" sql="SELECT tema AS tema, round(sum(finanz_totale_pubblico)/1e3) AS migliaia FROM opencoesione WHERE codice_istat='{#comune}' GROUP BY 1 ORDER BY 2 DESC" limit="20"}

:::table{path="progettiComune" headers="Progetto,Tema,Ciclo,Migliaia €,Avanz. %,Stato" pagesize="10" mark="avanzamento<25:text-error,avanzamento>=75:text-success"}
{titolo} | {tema} | {ciclo} | {migliaia} | {avanzamento} | {stato}
:::
::::

Rosso = avanzamento economico sotto il 25%, verde = oltre il 75%. La ricerca
sopra la tabella filtra per parola ("scuola", "strada", "asilo"…).

## Gli investimenti censiti dal CUP

::od-query{into="cupComune" sql="SELECT descrizione AS descrizione, natura_intervento AS natura, round(costo_progetto/1e3) AS migliaia, anno_decisione AS anno, stato_progetto AS stato FROM opencup WHERE codice_istat='{#comune}' AND costo_progetto>0 ORDER BY costo_progetto DESC" limit="150"}

:::table{path="cupComune" headers="Intervento,Natura,Migliaia €,Anno,Stato" pagesize="10"}
{descrizione} | {natura} | {migliaia} | {anno} | {stato}
:::

:::::

:::::page{title="Appalti" icon="🔨"}

## Le gare del territorio e chi le vince

Le gare pubbliche bandite dagli enti della provincia del comune scelto, con
gli esiti e i vincitori (fonte ANAC; gli importi possono contenere errori
della fonte).

::od-query{into="gareProvincia" sql="SELECT g.cig AS cig, g.oggetto_gara AS oggetto, g.amministrazione AS amministrazione, round(g.importo_lotto/1e3) AS migliaia, g.data_pubblicazione AS data, coalesce(a.vincitore,'—') AS vincitore, coalesce(round(a.ribasso,1),0) AS ribasso FROM anac_cig g LEFT JOIN (SELECT cig, any_value(denominazione) AS vincitore, max(ribasso) AS ribasso FROM anac_aggiudicatari GROUP BY cig) a ON a.cig=g.cig WHERE substr(g.luogo_istat,1,3)=substr('{#comune}',1,3) AND g.importo_lotto>0 ORDER BY g.data_pubblicazione DESC" limit="300"}

:::table{path="gareProvincia" headers="Oggetto,Amministrazione,Migliaia €,Data,Vincitore,Ribasso %" pagesize="10" mark="ribasso>40:text-error font-bold"}
{oggetto} | {amministrazione} | {migliaia} | {data} | {vincitore} | {ribasso}
:::

I ribassi sopra il 40% (in rosso) meritano uno sguardo: possono indicare
gare mal stimate o offerte aggressive — sono **scostamenti statistici, non
accuse**.

## Cerca tra le gare (ricerca semantica)

::od-search{into="gareTrovate" table="anac_cig" placeholder="Es. manutenzione strade, mensa scolastica, illuminazione…"}

:::table{path="gareTrovate" headers="Oggetto,Amministrazione,Importo,Provincia,Esito" pagesize="10"}
{oggetto_gara} | {amministrazione} | {importo_lotto} | {provincia} | {esito}
:::

:::::

:::::page{title="Analisi ML" icon="🤖"}

## Machine learning nel browser

Le analisi di questa pagina girano **sul tuo dispositivo** (scikit-learn via
Python nel browser): la prima esecuzione scarica il motore (~60 MB, poi in
cache) e parte dal pulsante. Sono strumenti di lettura, non giudizi.

## Dove andrà la spesa: la previsione

La serie degli anni chiusi prolungata in avanti (l'R² nello stato dice
quanto il trend spiega la serie).

::od-query{into="serieSpesa" sql="SELECT anno AS anno, round(sum(importo)/1e6,2) AS spesa FROM siope_spese WHERE codice_istat='{#comune}' AND mese=12 AND categoria_codice NOT IN ('7.01','7.02','0.00') GROUP BY 1 ORDER BY 1"}

::range[anniPrev]{min="1" max="4" value="2" legend="Anni di previsione"}

::ml-forecast{data="serieSpesa" x="anno" y="spesa" horizon="#anniPrev" into="spesaPrevista"}

::chart-line{data="spesaPrevista" x="anno" y="spesa,previsione" height="16rem"}

## Spese fuori norma: le anomalie tra comuni

Il profilo pro capite del tuo comune confrontato con TUTTI i comuni della
regione (personale, beni e servizi, trasferimenti, investimenti): l'Isolation
Forest segnala i profili anomali — il tuo compreso, se lo è.

::od-query{into="profiliRegione" sql="SELECT comune AS comune, codice_istat AS codice_istat, round(sum(importo) FILTER (WHERE categoria_codice='1.01')/max(popolazione)) AS personale, round(sum(importo) FILTER (WHERE categoria_codice='1.03')/max(popolazione)) AS beni_servizi, round(sum(importo) FILTER (WHERE categoria_codice='1.04')/max(popolazione)) AS trasferimenti, round(sum(importo) FILTER (WHERE categoria_codice LIKE '2.%')/max(popolazione)) AS investimenti FROM siope_spese WHERE regione=(SELECT regione FROM siope_spese WHERE codice_istat='{#comune}' LIMIT 1) AND mese=12 AND anno=(SELECT anno FROM siope_spese WHERE mese=12 GROUP BY anno HAVING count(DISTINCT codice_istat)>=6500 ORDER BY anno DESC LIMIT 1) AND popolazione>0 GROUP BY 1,2" limit="1600"}

::ml-anomaly{data="profiliRegione" features="personale,beni_servizi,trasferimenti,investimenti" contamination="0.05" into="comuniAnomali"}

:::filter{path="comuniAnomali" field="flag" equals="1" as="table" headers="Comune,Personale €/ab,Beni e servizi,Trasferimenti,Investimenti,Punteggio" pagesize="10"}
{comune} | {personale} | {beni_servizi} | {trasferimenti} | {investimenti} | {anomalia}
:::

## Ribassi anomali negli appalti della provincia

::od-query{into="ribassiProvincia" sql="SELECT g.oggetto_gara AS oggetto, g.amministrazione AS amministrazione, round(g.importo_lotto/1e3) AS migliaia, round(a.ribasso,1) AS ribasso, a.offerte_ammesse AS offerte FROM anac_cig g JOIN anac_aggiudicatari a ON a.cig=g.cig WHERE substr(g.luogo_istat,1,3)=substr('{#comune}',1,3) AND a.ribasso IS NOT NULL AND a.ribasso BETWEEN 0.1 AND 99 AND g.importo_lotto>10000" limit="1000"}

::ml-anomaly{data="ribassiProvincia" features="migliaia,ribasso" contamination="0.04" into="gareAnomale"}

:::filter{path="gareAnomale" field="flag" equals="1" as="table" headers="Oggetto,Amministrazione,Migliaia €,Ribasso %,Offerte" pagesize="10"}
{oggetto} | {amministrazione} | {migliaia} | {ribasso} | {offerte}
:::

Combinazioni insolite di importo e ribasso rispetto al resto della
provincia: un punto di partenza per chi vuole guardarci dentro, non una
lista di colpevoli.

## Comuni che spendono come il tuo

Il k-means raggruppa i comuni della regione per profilo di spesa pro capite:
cerca il tuo nella tabella e scopri la sua famiglia.

::range[kCluster]{min="3" max="8" value="5" legend="Numero di gruppi"}

::ml-cluster{data="profiliRegione" features="personale,beni_servizi,trasferimenti,investimenti" k="#kCluster" into="famiglieComuni"}

:::table{path="famiglieComuni" headers="Comune,Gruppo,Personale,Beni e servizi,Investimenti" pagesize="10"}
{comune} | {cluster} | {personale} | {beni_servizi} | {investimenti}
:::

## Spesa e reddito: cosa si muove insieme

La correlazione tra le voci pro capite e il reddito medio dei comuni della
regione (r vicino a ±1 = legame forte; correlazione, non causa).

::od-query{into="spesaReddito" sql="SELECT s.comune AS comune, round(sum(s.importo) FILTER (WHERE s.categoria_codice='1.01')/max(s.popolazione)) AS personale, round(sum(s.importo) FILTER (WHERE s.categoria_codice='1.03')/max(s.popolazione)) AS beni_servizi, round(sum(s.importo) FILTER (WHERE s.categoria_codice LIKE '2.%')/max(s.popolazione)) AS investimenti, max(r.reddito_medio) AS reddito FROM siope_spese s JOIN mef_redditi r ON r.codice_istat=s.codice_istat AND r.anno=(SELECT max(anno) FROM mef_redditi) WHERE s.regione=(SELECT regione FROM siope_spese WHERE codice_istat='{#comune}' LIMIT 1) AND s.mese=12 AND s.anno=(SELECT anno FROM siope_spese WHERE mese=12 GROUP BY anno HAVING count(DISTINCT codice_istat)>=6500 ORDER BY anno DESC LIMIT 1) AND s.popolazione>0 GROUP BY 1" limit="1600"}

::ml-correlate{data="spesaReddito" features="personale,beni_servizi,investimenti,reddito" into="correlazioni"}

:::table{path="correlazioni" headers="Variabile A,Variabile B,r" search="false" pagesize="0" mark="r>0.5:text-success,r<-0.5:text-error"}
{a} | {b} | {r}
:::

:::::

:::::page{title="La mappa" icon="🗺️"}

## La spesa pro capite, comune per comune

La coropletica della regione del comune scelto: spesa totale per abitante
nell'ultimo anno chiuso (blu più intenso = spesa più alta).

::od-query{into="mappaSpesa" sql="SELECT c.comune AS comune, c.geojson AS geojson, round(sum(s.importo) FILTER (WHERE s.categoria_codice NOT IN ('7.01','7.02','0.00'))/max(s.popolazione)) AS procapite, round(coalesce(sum(s.importo) FILTER (WHERE s.categoria_codice LIKE '2.%'),0)/max(s.popolazione)) AS investimenti FROM siope_spese s JOIN istat_confini_comuni c ON c.codice_istat=s.codice_istat WHERE s.regione=(SELECT regione FROM siope_spese WHERE codice_istat='{#comune}' LIMIT 1) AND s.mese=12 AND s.anno=(SELECT anno FROM siope_spese WHERE mese=12 GROUP BY anno HAVING count(DISTINCT codice_istat)>=6500 ORDER BY anno DESC LIMIT 1) AND s.popolazione>0 GROUP BY 1,2" limit="1600"}

:::map{path="mappaSpesa" geojson="geojson" fill="procapite" height="28rem"}
**{comune}** — {procapite} € per abitante ({investimenti} € di investimenti)
:::

## Gli investimenti per abitante

Stessi confini, metrica diversa: la seconda mappa riusa i dati già scaricati
(i poligoni viaggiano una volta sola).

:::map{path="mappaSpesa" geojson="geojson" fill="investimenti" height="28rem"}
**{comune}** — {investimenti} € di investimenti per abitante
:::

:::::

:::::page{title="Esplora" icon="🔍"}

## Il pivot libero

Tutta la spesa del comune scelto, anno per anno e voce per voce, in una
vista esplorabile: trascina le colonne, raggruppa, cambia grafico.

::od-query{into="pivotSpesa" sql="SELECT s.anno AS anno, s.mese AS mese, s.titolo AS titolo, s.categoria AS categoria, round(s.importo) AS importo FROM siope_spese s WHERE s.codice_istat='{#comune}' ORDER BY s.anno, s.categoria" limit="1000"}

:::explore{path="pivotSpesa" view="bar" group-by="categoria" columns="importo" height="30rem"}
:::

Fonti, licenze e note metodologiche nella pagina **Fonti**.

:::::

:::::page{title="Fonti" icon="📚"}

## Da dove vengono i dati

Tutte le fonti sono **dati aperti ufficiali**, aggiornati periodicamente dal
servizio dati di Reactive e interrogati al volo: questa app non conserva
nulla sui suoi server, le analisi girano nel tuo browser.

| Dato | Fonte | Licenza | Aggiornamento |
|---|---|---|---|
| Spesa per cassa dei comuni | [OpenBDAP — MEF/Ragioneria Generale dello Stato](https://bdap-opendata.rgs.mef.gov.it/catalog/RND_SPE_SIO) (flussi SIOPE) | CC BY | mensile |
| Progetti di coesione e PNRR | [OpenCoesione](https://opencoesione.gov.it/it/opendata/) | CC BY 4.0 | bimestrale |
| Investimenti pubblici (CUP) | [OpenCUP — Presidenza del Consiglio/DIPE](https://opencup.gov.it/portale/web/opencup/open-data) | CC BY | mensile |
| Gare d'appalto e aggiudicatari | [ANAC — dati.anticorruzione.it](https://dati.anticorruzione.it/) | CC BY | mensile |
| Redditi IRPEF per comune | [MEF — Dipartimento delle Finanze](https://www1.finanze.gov.it/finanze/analisi_stat/public/index.php) | IODL/CC BY | annuale |
| Popolazione e confini comunali | [ISTAT](https://www.istat.it/) | CC BY | annuale |
| Mappa di sfondo | © OpenStreetMap contributors | ODbL | — |

## Note metodologiche

- **Cassa, non competenza**: gli importi SIOPE sono i pagamenti EFFETTUATI
  nell'anno, non gli impegni di bilancio. Un investimento deciso quest'anno
  può essere pagato nei prossimi; il confronto tra comuni resta valido
  perché la metrica è la stessa per tutti.
- **Partite di giro escluse**: dai totali sono tolti i giri contabili
  (spese 7.01/7.02, entrate 9.01/9.02) e i movimenti da regolarizzare —
  non sono spesa o entrata vera.
- **Autonomia finanziaria** = (entrate tributarie + extratributarie) /
  entrate correnti: quanto il comune si regge sulle risorse proprie.
- **Categorie**: le voci sono il livello II del piano dei conti armonizzato
  (D.Lgs 118/2011), ~25 categorie leggibili.
- **Anno parziale**: l'anno in corso (e i comuni che non hanno ancora
  trasmesso dicembre) mostra il cumulato all'ultimo mese disponibile,
  indicato nella colonna "Mesi".
- **Importi ANAC**: possono contenere errori di caricamento della fonte
  (importi palesemente sbagliati compresi); i ribassi fuori scala sono
  filtrati dalle analisi.
- **Machine learning**: previsioni, anomalie e cluster sono strumenti di
  lettura statistica calcolati sul tuo dispositivo — scostamenti, non
  giudizi.

:::::