In questi giorni mi hanno girato una discreta quantità di dataset in formato shapefile (quindi geografici) da analizzare.
Come i tutti questi casi accade che molti dati sono incompleti, le variabili categoriali diverse e con tanti sinonimi, valori anomali nella distribuzione (d'altronde, se qualcuno inserisce le lunghezze in metri ed altri in centimetri nello stesso campo, cosa può accadere?) e molto altro ancora.
Da buon "spippolatore" ho cominciato a guardare i dati uno alla volta, aprendoli con QGIS.
Dovendo però guardare anche le tabelle degli attributi il tutto mi ha convinto ad utilizzare python con la classica configurazione da data scientist: (geo)pandas in jupyter notebook.

Da qui tutta una serie di istruzioni

# import dei moduli necessari
import geopandas as gpd
import numpy as np
# caricamento dello shapefile del Comune di Trento con gli incidenti in un geodataframe (geopandas)
incidenti = gpd.read_file("http://webapps.comune.trento.it/cartografia/gis/dbexport?db=base&sc=vigili&ly=incidenti&fr=shp")
# informazioni sulle colonne
incidenti.info()
# descrizione delle colonne
incidenti.describe()
# descrizione delle colonne testuali
incidenti.describe(include=np.object)

con il fine di conoscere come è strutturato il dataset degli incidenti georeferenziati del Comune di Trento rilasciati in open data per arrivare ad una prima conclusione di avere una statistica del numero di incidenti per anno.

incidenti['anno'] = incidenti['anno'].fillna(0).astype({"anno":'int64'})
incidenti.groupby('anno').size().plot.bar(figsize=(15,15))
numero di incidenti per anno registrati dal Comune di Trento

e da qui poi proseguire creando mappe di intensità

from colorcet import fire
import datashader as ds
from datashader import transfer_functions as tf
agg = ds.Canvas().points(incidenti, 'x_gps', 'y_gps')
tf.set_background(tf.shade(agg, cmap=fire),"black")
intensità dei punti degli incidenti a Trento dal 2002 al 2019

o interattive

import folium
x = incidenti.to_crs({"init": "epsg:4326"}).unary_union.envelope.centroid.x
y = incidenti.to_crs({"init": "epsg:4326"}).unary_union.envelope.centroid.y
m = folium.Map([y,x], zoom_start=11, tiles="stamenterrain")
for index, row  in incidenti.to_crs({"init": "epsg:4326"}).iterrows():
  folium.CircleMarker(
    location=[row.geometry.y,row.geometry.x],
    radius=1,
    color='red',
    fill=True,
    fill_color='orange'
  ).add_to(m)
folium.LayerControl(collapsed=False).add_to(m)
m
map tiles by Stamen Design - under cc-by 3.0 - data © OpenStreetmap contributors

L'analisi per investigare i dati può portare così a continuare attraverso la chiamata di varie funzioni di pandas e geopandas.

Pandas Profiling

Il problema è però comune a chiunque voglia investigare i dati e, se il metodo .describe() già dice qualcosa di utile, sicuramente qualcuno ha già pensato ad insieme di funzioni da mettere in batteria per produrre un report.
E così, con oggi, ho scoperto pandas-profiling

Il suo uso è immediato:
una volta installato il pacchetto

pip install pandas_profiling

è sufficiente aggiungere il metodo .profile_report() ad un dataframe e magicamente compare un intera pagina HTML con un report molto completo!!!

nel mio caso sono davanti ad un geodataframe prodotto da geopandas e, pertanto lo devo "declassare" a dataframe usando pandas e rinunciando alla colonna delle geometrie

import pandas as pd
import pandas_profiling
df = pd.DataFrame(incidenti.drop(columns='geometry'))
df.profile_report(title='Incidenti a Trento',style={'full_width':True})

Si può anche decidere di salvare l'output su un file .html
qui il risultato

gif animata con il report creato da pandas_profiling
profile = df.profile_report(title='Report dataset incidenti Trento')
profile.to_file(output_file="output.html")

e nota ancora più meravigliosa è stato prodotto anche il comando per shell
pandas-profiling

Nota per gli utenti Colab Google

qualora ne fate uso da Colab di Google il comando di installazione del modulo deve essere fatto con questa sintassi:

!pip install -U pandas-profiling

Ok! Ma con una riga di python?

Vediamo ora di mettere in pratica il tutto con "una" riga di python in ambiente jupyter notebook che preveda già l'installazione dei pacchetti pandas e pandas_profiling.
L"esempio di partenza è quello del csv con le Farmacie della Provincia Autonoma di Trento.
Il cui file si recupera a questo indirizzo http://servizi.apss.tn.it/opendata/FARM001.csv

In python basta seguire queste istruzioni

import pandas
import pandas_profiling
pd.read_csv("http://servizi.apss.tn.it/opendata/FARM001.csv").profile_report()

l’istruzione

pd.read_csv("http://servizi.apss.tn.it/opendata/FARM001.csv").profile_report()

è quella che produce l’intero report

nota:
guardando attentamente il report prodotto si scopre un errore nei dati:
le farmacie sono 175 e, per ciascuna, sono associati i valori di latitudine e longitudine.
Ci si aspetterebbe di avere 175 valori univoci per ciascun valore di latitudine e longitudine, invece sono 158.
Pertanto è molto probabile che ci siano diverse farmacie le cui coordinate risultano uguali.

analizzare un dataset con una sola riga di python
analizzare un dataset con una sola riga di python
analizzare un dataset con una sola riga di python
analizzare un dataset con una sola riga di python

Versione originale su Medium