Skip to content

Instantly share code, notes, and snippets.

@aborruso
Created January 15, 2026 11:08
Show Gist options
  • Select an option

  • Save aborruso/2bbff6a3cc113f3678f3a09e11896c9c to your computer and use it in GitHub Desktop.

Select an option

Save aborruso/2bbff6a3cc113f3678f3a09e11896c9c to your computer and use it in GitHub Desktop.

Data Explorer Analysis Report

Rilevazioni Ambientali Inquinamento Atmosferico - Stazione Villa Dante (Messina)

Analisi effettuata: 15 Gennaio 2026 Dataset: Comune di Messina - Rilevazioni ambientali stazione Villa Dante Periodo: 23/03/2015 - 27/02/2017 Fonte: https://dati.gov.it/view-dataset/dataset?id=37eade1f-20f2-490c-838f-4cb099d4aedd


Resource Overview

  • Total resources: 1 (CSV file)
  • Format: CSV con virgola come delimitatore
  • Encoding: ASCII
  • File size: 1.3 MB
  • Total rows: 16,992 (+ 1 header)
  • Frequenza campionamento: 1 rilevazione/ora

Individual Resource Analysis

Resource: messina_villa_dante.csv

Structure:

  • Rows: 16,992
  • Columns: 9
  • Key columns: Data Ora (temporal), Campionamenti (ID sequenziale), inquinanti (metrics), coordinate geografiche

Data Types:

  • 1 BIGINT: Campionamenti
  • 1 VARCHAR: Data Ora
  • 5 DOUBLE: Inquinanti (Benzene, Toluene, O-Xilene, CO, PM10)
  • 2 DOUBLE: Latitudine, Longitudine

Inquinanti Monitorati:

  • Benzene (media oraria, μg/m³)
  • Toluene (media oraria, μg/m³)
  • O-Xilene (media oraria, μg/m³)
  • CO - Monossido di Carbonio (media oraria, mg/m³)
  • PM10 - Particolato (media oraria, μg/m³)

Posizione Stazione:

  • Latitudine: 38.175292° N (costante)
  • Longitudine: 15.545746° E (costante)
  • Località: Villa Dante, Messina

Statistical Summary:

Colonna Min Max Media Mediana StdDev NULL %
Campionamenti 1 16,975 8,488 8,488 4,900 0.10%
Benzene 0.19 1,371.0 1.26 0.80 18.7 33.22%
Toluene 0.12 14,052.0 5.30 1.83 144.9 33.10%
O-Xilene 0.13 82,771.0 12.78 0.69 880.0 33.14%
CO 0.00004 125.0 0.24 0.20 1.28 12.76%
PM10 0.0 19,117.0 53.9 5.0 901.2 36.51%

Sample Data (5 random rows):

Campionamenti  Data Ora          Benzene  Toluene  O-Xilene  CO       PM10      Lat        Lon
15762          07/01/2017 18:01  0.699    0.626    0.734     0.170    NULL      38.175292  15.545746
10594          06/06/2016 10:00  NULL     NULL     NULL      NULL     NULL      38.175292  15.545746
13810          18/10/2016 10:01  NULL     NULL     NULL      0.024    31.68     38.175292  15.545746
360            07/04/2015 00:00  0.661    0.378    0.304     0.127    3.79      38.175292  15.545746
906            29/04/2015 18:00  NULL     NULL     NULL      NULL     NULL      38.175292  15.545746

Data Category: Time-series environmental data con rilevazioni orarie multi-parametro


Data Quality Assessment

🔴 PROBLEMI CRITICI

1. Valori Outlier Estremi (Severità: CRITICA)

Valori fisicamente impossibili o errori strumentali/di trascrizione:

Inquinante Valori > 100 μg/m³ Max Rilevato Note
Benzene 4 1,371.0 Limite UE: 5 μg/m³ anno. Valori >100 impossibili
Toluene 10 14,052.0 Valori normali 1-10 μg/m³. 14,000 impossibile
O-Xilene 7 82,771.0 Valori normali <5 μg/m³. 82,000 assurdo
PM10 30 19,117.0 Limite UE: 50 μg/m³ giorno. 19,000 impossibile

Statistiche outlier (metodo IQR, Benzene):

  • Bounds: [-0.26, 1.94] μg/m³
  • Outlier: 538 valori (4.74% dei dati validi)
  • Upper outlier: valori fino a 1,371 μg/m³

Esempio record con outlier estremi (riga 17):

23/03/2015 17:00: Benzene=76.46, Toluene=43,983, O-Xilene=NULL

Raccomandazione:

-- Identificare e rimuovere outlier estremi (> 3 deviazioni standard)
WITH stats AS (
    SELECT
        AVG("Benzene media oraria") + 3 * STDDEV("Benzene media oraria") AS upper_limit
    FROM 'tmp/messina_villa_dante.csv'
    WHERE "Benzene media oraria" IS NOT NULL
)
SELECT *
FROM 'tmp/messina_villa_dante.csv' t, stats s
WHERE t."Benzene media oraria" > s.upper_limit;

-- Applicare stesso filtro a Toluene, O-Xilene, PM10

2. Notazione Scientifica Inconsistente (Severità: ALTA)

Il file CSV usa notazione scientifica in modo inconsistente:

  • 1.38E+01 invece di 13.8
  • 4.32E+00 invece di 4.32

Impatto:

  • Difficoltà parsing in alcuni tool
  • Confusione visiva nella lettura manuale
  • Potenziale perdita precisione

Raccomandazione:

# Convertire notazione scientifica in decimale
sed -E 's/([0-9]+\.[0-9]+)E\+0([0-9])/\1 * 10^\2/g' tmp/messina_villa_dante.csv | \
  awk -F, '{for(i=1;i<=NF;i++) if($i ~ /\*/) {cmd="echo \"scale=6; "$i"\" | bc"; cmd | getline $i; close(cmd)} print}' OFS=,

3. Record Corrotti con Date Invalid (Severità: ALTA)

Presenza di righe con date malformate:

Data Ora  Campionamenti  Benzene  Toluene  ...
8         NULL           NULL     NULL     ...
9         NULL           NULL     NULL     ...
10        NULL           NULL     NULL     ...

Impatto: ~10-20 record con solo un numero invece di data DD/MM/YYYY HH:MM

Raccomandazione:

-- Rimuovere righe con date corrotte
SELECT *
FROM 'tmp/messina_villa_dante.csv'
WHERE LENGTH("Data Ora") >= 16;  -- Formato corretto: "DD/MM/YYYY HH:MM"

4. Field Naming Issues (Severità: MEDIA)

Tutti i nomi colonne contengono spazi:

Original Name Issue Recommended Name
Data Ora Spazi data_ora
Benzene media oraria Spazi, ridondante benzene_ug_m3
Toluene media oraria Spazi, ridondante toluene_ug_m3
O-Xilene media oraria Spazi, ridondante o_xilene_ug_m3
CO media oraria Spazi, ridondante co_mg_m3
PM10 media oraria Spazi, ridondante pm10_ug_m3

Proposta rename con unità di misura esplicite:

SELECT
    "Campionamenti" AS campionamento_id,
    "Data Ora" AS data_ora,
    "Benzene media oraria" AS benzene_ug_m3,
    "Toluene media oraria" AS toluene_ug_m3,
    "O-Xilene media oraria" AS o_xilene_ug_m3,
    "CO media oraria" AS co_mg_m3,
    "PM10 media oraria" AS pm10_ug_m3,
    "Latitudine" AS latitudine,
    "Longitudine" AS longitudine
FROM 'tmp/messina_villa_dante.csv'
WHERE LENGTH("Data Ora") >= 16  -- Escludi record corrotti
  AND "Benzene media oraria" < 50  -- Soglia massima ragionevole
  AND "Toluene media oraria" < 100
  AND "O-Xilene media oraria" < 100
  AND "PM10 media oraria" < 500;

🟡 PROBLEMI MEDI

5. Dati Mancanti Massivi (Severità: MEDIA)

Alta percentuale di valori NULL:

Colonna NULL Count NULL % Periodo Critico
PM10 6,204 36.51% Primi mesi 2015
Benzene 5,645 33.22% Primi mesi 2015
O-Xilene 5,631 33.14% Primi mesi 2015
Toluene 5,625 33.10% Primi mesi 2015
CO 2,168 12.76% Sparse

Pattern: Prime 16 righe (23/03/2015) hanno tutti NULL per inquinanti organici, probabilmente fase di calibrazione strumenti.

Impatto:

  • Analisi serie temporale incompleta
  • Impossibile calcolare medie annuali affidabili primi mesi
  • Ridotta potenza statistica

Raccomandazione:

  • Documentare periodi di mancato funzionamento strumenti
  • Considerare rimozione primi mesi se non rappresentativi
  • Usare interpolazione solo per gap brevi (<6 ore)

6. Formato Data Non Standard (Severità: MEDIA)

  • Formato attuale: DD/MM/YYYY HH:MM (es. 23/03/2015 01:00)
  • Standard ISO 8601: YYYY-MM-DD HH:MM:SS

Conversione raccomandata:

SELECT
    STRPTIME("Data Ora", '%d/%m/%Y %H:%M') AS data_ora_iso
FROM 'tmp/messina_villa_dante.csv';

🟢 ASPETTI POSITIVI

7. Completezza Geografica

  • Coordinate GPS complete e consistenti
  • Nessun NULL in Latitudine/Longitudine (eccetto 17 record con Campionamenti NULL)
  • Precisione: 6 decimali (~11 cm accuracy)

8. Sequenza Temporale

  • Nessun timestamp duplicato
  • Serie temporale continua (con gap documentati da NULL)
  • ID campionamento sequenziale

9. Encoding Corretto

  • File in ASCII puro
  • Nessun problema encoding caratteri speciali
  • CSV ben formattato (comma-delimited)

Statistiche Riassuntive Corrette (post-cleaning)

Query suggerita per dataset pulito:

WITH cleaned AS (
    SELECT *
    FROM 'tmp/messina_villa_dante.csv'
    WHERE LENGTH("Data Ora") >= 16
      AND "Benzene media oraria" < 50
      AND "Toluene media oraria" < 100
      AND "O-Xilene media oraria" < 100
      AND "PM10 media oraria" < 500
)
SELECT
    COUNT(*) as righe_valide,
    COUNT("Benzene media oraria") as benzene_non_null,
    ROUND(AVG("Benzene media oraria"), 3) as benzene_media,
    ROUND(AVG("PM10 media oraria"), 2) as pm10_media,
    MIN("Data Ora") as primo_campionamento,
    MAX("Data Ora") as ultimo_campionamento
FROM cleaned;

Risultati attesi dopo pulizia:

  • Righe valide: ~16,950 (99.75%)
  • Outlier rimossi: ~40-50
  • Record corrotti rimossi: ~10-20

Valutazione Quantitativa

Dimensione Punteggio Note
Completezza 4/10 33% NULL, primi mesi mancanti
Accuratezza 3/10 Outlier estremi, valori impossibili
Consistenza 6/10 Formato date OK ma notazione scientifica
Tempestività 5/10 Dati 2015-2017, aggiornati 2017
Accessibilità 7/10 CSV pubblico, encoding corretto
Interoperabilità 5/10 Spazi in header, unità non esplicite
Documentazione 4/10 Manca metadato unità di misura esplicite

Punteggio Complessivo: 4.9/10 ⭐⭐

Giudizio: Dataset con gravi problemi di qualità che richiedono pulizia sostanziale prima dell'uso analitico.


Recommended Next Steps

Priority 1: Data Cleanup (CRITICO)

# 1. Creare versione pulita del dataset
duckdb -c "
COPY (
    SELECT
        \"Campionamenti\" AS campionamento_id,
        STRPTIME(\"Data Ora\", '%d/%m/%Y %H:%M') AS data_ora,
        CASE
            WHEN \"Benzene media oraria\" > 50 THEN NULL
            ELSE \"Benzene media oraria\"
        END AS benzene_ug_m3,
        CASE
            WHEN \"Toluene media oraria\" > 100 THEN NULL
            ELSE \"Toluene media oraria\"
        END AS toluene_ug_m3,
        CASE
            WHEN \"O-Xilene media oraria\" > 100 THEN NULL
            ELSE \"O-Xilene media oraria\"
        END AS o_xilene_ug_m3,
        CASE
            WHEN \"CO media oraria\" > 50 THEN NULL
            ELSE \"CO media oraria\"
        END AS co_mg_m3,
        CASE
            WHEN \"PM10 media oraria\" > 500 THEN NULL
            ELSE \"PM10 media oraria\"
        END AS pm10_ug_m3,
        \"Latitudine\" AS latitudine,
        \"Longitudine\" AS longitudine
    FROM read_csv('tmp/messina_villa_dante.csv')
    WHERE LENGTH(\"Data Ora\") >= 16
      AND \"Campionamenti\" IS NOT NULL
) TO 'tmp/messina_villa_dante_clean.csv' (HEADER, DELIMITER ',');
"

# 2. Convertire in Parquet per performance migliori
duckdb -c "
COPY (SELECT * FROM 'tmp/messina_villa_dante_clean.csv')
TO 'tmp/messina_villa_dante_clean.parquet';
"

Priority 2: Richiesta Dati Corretti al Fornitore

Contattare opendata@comune.messina.it per:

  1. Verifica outlier estremi: File valori >100 μg/m³ per Benzene, Toluene, O-Xilene
  2. File unità di misura: Richiedere TXT con dizionario completo (già pubblicato ma verificare corrispondenza)
  3. Metadati calibrazione: Date manutenzione/calibrazione strumenti
  4. Dati aggiornati: Serie 2017-2025 se disponibile
  5. Correzione notazione: File senza notazione scientifica o con formato consistente

Priority 3: Analisi Ambientale (post-cleaning)

Dopo pulizia, analisi suggerite:

-- Superamenti limite Benzene (5 μg/m³ media annua UE)
SELECT
    YEAR(data_ora) as anno,
    AVG(benzene_ug_m3) as benzene_media_annua,
    MAX(benzene_ug_m3) as benzene_max
FROM 'tmp/messina_villa_dante_clean.parquet'
WHERE benzene_ug_m3 IS NOT NULL
GROUP BY YEAR(data_ora);

-- Superamenti limite PM10 (50 μg/m³ media giornaliera)
SELECT
    DATE_TRUNC('day', data_ora) as giorno,
    AVG(pm10_ug_m3) as pm10_media_giornaliera
FROM 'tmp/messina_villa_dante_clean.parquet'
WHERE pm10_ug_m3 IS NOT NULL
GROUP BY DATE_TRUNC('day', data_ora)
HAVING AVG(pm10_ug_m3) > 50
ORDER BY pm10_media_giornaliera DESC;

-- Pattern orario inquinamento (traffico)
SELECT
    HOUR(data_ora) as ora,
    AVG(benzene_ug_m3) as benzene_avg,
    AVG(co_mg_m3) as co_avg,
    COUNT(*) as campioni
FROM 'tmp/messina_villa_dante_clean.parquet'
GROUP BY HOUR(data_ora)
ORDER BY ora;

Conclusioni

Il dataset Rilevazioni ambientali stazione Villa Dante presenta gravi criticità qualitative che ne limitano fortemente l'utilizzabilità scientifica e normativa:

Problemi Bloccanti

  1. ❌ Valori outlier fisicamente impossibili (Benzene 1,371 μg/m³, O-Xilene 82,771 μg/m³)
  2. ❌ 33% dati mancanti per inquinanti organici
  3. ❌ Record corrotti con date invalid

Utilizzo Raccomandato

  • ⚠️ NON utilizzabile per confronto normativo UE senza validazione outlier
  • ⚠️ Utilizzabile con cautela per analisi pattern temporali (post-cleaning)
  • Utilizzabile per geolocalizzazione stazione (coordinate valide)

Azioni Richieste

  1. Contattare fornitore per validazione/correzione outlier
  2. Applicare cleaning rigoroso (rimozione outlier >3σ)
  3. Documentare gap temporali e periodi calibrazione
  4. Richiedere dati aggiornati 2017-2025

Stima Effort Cleanup

  • Automatico (SQL cleaning): 1 ora
  • Validazione manuale outlier: 4-8 ore
  • Contatto fornitore + correzioni: 2-4 settimane

Report generato: 15/01/2026 Tool utilizzati: DuckDB CLI, normalizer, jq Metodologia: Data Explorer Skill (quality-checks.md)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment