Geocodifica per Data Scientist

'Geocoding for Data Scientists'

Questo articolo introduce la geocodifica come parte di un flusso di lavoro di data science. Copre la geocodifica manuale e quella basata su API con un esempio divertente e coinvolgente.

Quando i data scientist hanno bisogno di conoscere tutto ciò che c’è da sapere sulla “dove” dei loro dati, spesso si rivolgono ai Sistemi Informativi Geografici (GIS). GIS è un insieme complicato di tecnologie e programmi che servono a una vasta gamma di scopi, ma l’Università di Washington fornisce una definizione abbastanza completa, dicendo che “un sistema informativo geografico è un complesso di cose o oggetti associati o connessi, il cui scopo è comunicare conoscenza sulle caratteristiche sulla superficie della terra” (Lawler et al). GIS abbraccia una vasta gamma di tecniche per l’elaborazione dei dati spaziali, dall’acquisizione alla visualizzazione, molte delle quali sono strumenti preziosi anche se non sei uno specialista GIS. Questo articolo fornisce una panoramica completa della geocodifica con dimostrazioni in Python di diverse applicazioni pratiche. In particolare, si determinerà la posizione esatta di una pizzeria a New York, New York utilizzando il suo indirizzo e si collegherà ai dati sui parchi nelle vicinanze. Mentre le dimostrazioni utilizzano il codice Python, i concetti di base possono essere applicati a molti ambienti di programmazione per integrare la geocodifica nel tuo flusso di lavoro. Questi strumenti forniscono la base per trasformare i dati in dati spaziali e aprono la porta per un’analisi geografica più complessa.

Cos’è la geocodifica?

La geocodifica è più comunemente definita come la trasformazione dei dati dell’indirizzo in coordinate di mappatura. Di solito, questo comporta il rilevamento di un nome di strada in un indirizzo, l’abbinamento della strada ai confini del suo omologo nel mondo reale in un database, quindi l’effettuazione di una stima di dove posizionare l’indirizzo sulla strada utilizzando il numero civico. Come esempio, passiamo attraverso il processo di una semplice geocodifica manuale per l’indirizzo di una pizzeria a New York su Broadway: 2709 Broadway, New York, NY 10025. Il primo compito è trovare shapefile appropriati per il sistema stradale della posizione del tuo indirizzo. Si noti che in questo caso la città e lo stato dell’indirizzo sono “New York, NY”. Fortunatamente, la città di New York pubblica dettagliate informazioni stradali sulla pagina NYC Open Data (CSCL PUB). In secondo luogo, esaminare il nome della strada “Broadway”. Ora sai che l’indirizzo può trovarsi su qualsiasi strada chiamata “Broadway” nella città di New York, quindi puoi eseguire il seguente codice Python per interrogare la NYC Open Data SODA API per tutte le strade chiamate “Broadway”.

import geopandas as gpd
import requests
from io import BytesIO

# Richiedi i dati dalla SODA API
req = requests.get(
    "https://data.cityofnewyork.us/resource/gdww-crzy.geojson?stname_lab=BROADWAY"
)
# Converti in uno stream di byte
reqstrm = BytesIO(req.content)
# Leggi lo stream come un GeoDataFrame
ny_streets = gpd.read_file(reqstrm)

Ci sono oltre 700 risultati di questa query, ma ciò non significa che devi controllare 700 strade per trovare la tua pizza. Visualizzando i dati, si può vedere che ci sono 3 strade principali di Broadway e alcune più piccole.

Il motivo di ciò è che ogni strada è divisa in sezioni che corrispondono approssimativamente a un isolato, consentendo un’osservazione più granulare dei dati. Il passo successivo del processo consiste nel determinare esattamente su quale di queste sezioni si trova l’indirizzo utilizzando il codice postale e il numero civico. Ogni segmento stradale nel dataset contiene intervalli di indirizzi per gli indirizzi degli edifici su entrambi i lati della strada. Allo stesso modo, ogni segmento contiene il codice postale per entrambi i lati della strada. Per individuare il segmento corretto, il seguente codice applica filtri per trovare il segmento stradale il cui codice postale corrisponde al codice postale dell’indirizzo e il cui intervallo di indirizzi contiene il numero civico dell’indirizzo.

# Indirizzo da geocodificare
indirizzo = "2709 Broadway, New York, NY 10025"
codice_postale = indirizzo.split(" ")[-1]
num_civico = indirizzo.split(" ")[0]

# Trova i segmenti stradali i cui intervalli di indirizzi del lato sinistro contengono il numero civico
potenziali = ny_streets.loc[ny_streets["l_low_hn"] < num_civico]
potenziali = potenziali.loc[potenziali["l_high_hn"] > num_civico]
# Trova i segmenti stradali il cui codice postale corrisponde all'indirizzo
potenziali = potenziali.loc[potenziali["l_zip"] == codice_postale]

Ciò restringe l’elenco al segmento stradale visualizzato di seguito.

Il compito finale consiste nel determinare dove si trova l’indirizzo su questa linea. Ciò viene fatto posizionando il numero civico all’interno dell’intervallo dell’indirizzo per il segmento, normalizzando per determinare quanto lungo la linea dovrebbe essere l’indirizzo, e applicando quella costante alle coordinate dei punti finali della linea per ottenere le coordinate dell’indirizzo. Il codice seguente illustra questo processo.

import numpy as np
from shapely.geometry import Point

# Calcola quanto lungo la strada posizionare il punto
denom = (
    potentials["l_high_hn"].astype(float) - potentials["l_low_hn"].astype(float)
).values[0]
normalized_street_num = (
    float(street_num) - potentials["l_low_hn"].astype(float).values[0]
) / denom

# Definisci un punto su quella strada
# Sposta la linea per iniziare a (0,0)
pizza = np.array(potentials["geometry"].values[0].coords[1]) - np.array(
    potentials["geometry"].values[0].coords[0]
)
# Moltiplica per il numero di strada normalizzato per ottenere le coordinate sulla linea
pizza = pizza * normalized_street_num
# Aggiungi il segmento di inizio per posizionare la linea sulla mappa
pizza = pizza + np.array(potentials["geometry"].values[0].coords[0])
# Converti in un array di geometria per geopandas
pizza = gpd.GeoDataFrame(
    {"address": [address], "geometry": [Point(pizza[0], pizza[1])]},
    crs=ny_streets.crs,
    geometry="geometry",
)

Aver completato la geocodifica dell’indirizzo, ora è possibile visualizzare la posizione di questa pizzeria su una mappa per capire la sua posizione. Poiché il codice sopra ha esaminato le informazioni relative al lato sinistro di un segmento stradale, la posizione effettiva sarà leggermente a sinistra del punto tracciato in un edificio sul lato sinistro della strada. Finalmente sai dove puoi trovare una pizza.

Questo processo copre ciò che viene comunemente definito geocodifica, ma non è l’unico modo in cui il termine viene utilizzato. Potresti anche vedere la geocodifica riferirsi al processo di trasferimento dei nomi dei punti di riferimento alle coordinate, dei codici postali alle coordinate o delle coordinate ai vettori GIS. Potresti persino sentire la geocodifica inversa (che verrà trattata in seguito) riferirsi come geocodifica. Una definizione più indulgente per la geocodifica che comprende questi sarebbe “il trasferimento tra descrizioni approssimative in linguaggio naturale di posizioni e coordinate geografiche”. Quindi, ogni volta che hai bisogno di passare tra questi due tipi di dati, considera la geocodifica come soluzione.

Come alternativa a ripetere questo processo ogni volta che devi geocodificare gli indirizzi, una varietà di endpoint API, come il Geocodificatore del Bureau del Censimento degli Stati Uniti e la Google Geocoding API, forniscono un servizio di geocodifica accurato gratuitamente. Alcune opzioni a pagamento, come ArcGIS di Esri, Geocodio e Smarty, offrono persino un’accuratezza sul tetto per gli indirizzi selezionati, il che significa che la coordinata restituita cade esattamente sul tetto dell’edificio anziché sulla strada vicina. Le seguenti sezioni illustrano come utilizzare questi servizi per adattare la geocodifica alla tua pipeline dati utilizzando come esempio il Geocodificatore del Bureau del Censimento degli Stati Uniti.

Come Geocodificare

Per ottenere la massima precisione possibile durante la geocodifica, dovresti sempre iniziare assicurandoti che i tuoi indirizzi siano formattati per adattarsi agli standard del servizio scelto. Ciò varierà leggermente tra ogni servizio, ma un formato comune è il formato USPS di “NUMERO CIVICO VIA, CITTA’, STATO, CAP” dove STATO è un codice di abbreviazione, NUMERO CIVICO è il numero civico e tutte le menzioni di numeri di suite, numeri di edifici e caselle postali sono rimossi.

Una volta formattato l’indirizzo, è necessario inviarlo all’API per la geocodifica. Nel caso del Geocodificatore del Bureau del Censimento degli Stati Uniti, è possibile inviare manualmente l’indirizzo tramite la scheda di elaborazione dell’indirizzo One Line o utilizzare la REST API fornita per inviare l’indirizzo in modo programmatico. Il Geocodificatore del Bureau del Censimento degli Stati Uniti consente anche di geocodificare interi file utilizzando il geocodificatore batch e specificare la fonte dati utilizzando il parametro di riferimento. Per geocodificare la pizzeria di prima, è possibile utilizzare questo link per passare l’indirizzo alla REST API, che può essere fatto in Python con il seguente codice.

# Invia l'indirizzo al Geocodificatore del Bureau del Censimento degli Stati Uniti REST API per l'elaborazione
response = requests.get(
    "https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?address=2709+Broadway%2C+New+York%2C+NY+10025&benchmark=Public_AR_Current&format=json"
).json()

I dati restituiti sono un file JSON, che può essere facilmente decodificato in un dizionario Python. Esso contiene un campo “tigerLineId” che può essere utilizzato per abbinare il shapefile della strada più vicina, un campo “side” che può essere usato per determinare su quale lato di quella strada si trovi l’indirizzo, e i campi “fromAddress” e “toAddress” che contengono l’intervallo di indirizzi per il segmento di strada. Più importante, esso contiene un campo “coordinates” che può essere utilizzato per individuare l’indirizzo su una mappa. Il seguente codice estrae le coordinate dal file JSON e lo elabora in un GeoDataFrame per prepararlo per l’analisi spaziale.

# Estrai le coordinate dal file JSON
coords = response["result"]["addressMatches"][0]["coordinates"]
# Converti le coordinate in un punto Shapely
coords = Point(coords["x"], coords["y"])
# Estrai l'indirizzo corrispondente
matched_address = response["result"]["addressMatches"][0]["matchedAddress"]
# Crea un GeoDataFrame contenente i risultati
pizza_point = gpd.GeoDataFrame(
    {"indirizzo": [matched_address], "geometria": coords},
    crs=ny_streets.crs,
    geometry="geometry",
)

Visualizzando questo punto si nota che è leggermente fuori strada a sinistra del punto che è stato geocodificato manualmente.

Come Fare il Reverse Geocoding

Il reverse geocoding è il processo di prendere le coordinate geografiche e corrisponderle a descrizioni in linguaggio naturale di una regione geografica. Quando applicato correttamente, è una delle tecniche più potenti per allegare dati esterni nella cassetta degli attrezzi del data science. Il primo passo del reverse geocoding è determinare le tue geografie di destinazione. Questa è la regione che conterrà i tuoi dati di coordinate. Alcuni esempi comuni sono i tratti di censimento, i codici ZIP e le città. Il secondo passo è determinare quale, se ce ne sono, di quelle regioni il punto si trova. Quando si utilizzano le regioni comuni, è possibile utilizzare il Geocoder del Censimento degli Stati Uniti per il reverse geocoding apportando piccole modifiche alla richiesta REST API. Una richiesta per determinare quali geografie del censimento contengono la pizzeria di prima è collegata qui . Il risultato di questa query può essere elaborato utilizzando gli stessi metodi di prima. Tuttavia, definendo creativamente la regione per adattarla a una necessità di analisi e geocodificando manualmente in essa, si aprono molte possibilità.

Per fare il reverse geocoding manuale, devi determinare la posizione e la forma di una regione, quindi determinare se il punto si trova all’interno di quella regione. Determinare se un punto si trova all’interno di un poligono è in realtà un problema piuttosto difficile, ma l’algoritmo di lancio del raggio , in cui un raggio che parte dal punto e si sposta infinitamente in una direzione interseca il confine della regione un numero dispari di volte se si trova all’interno della regione e un numero pari di volte altrimenti (Shimrat), può essere utilizzato per risolverlo nella maggior parte dei casi. Per i matematici, questo è in realtà un’applicazione diretta del teorema della curva di Jordan (Hosch). Come nota, se si utilizzano dati provenienti da tutto il mondo, l’algoritmo di lancio del raggio può fallire poiché il raggio alla fine si avvolge attorno alla superficie della Terra e diventa un cerchio. In questo caso, dovrai invece trovare il numero di avvolgimenti (Weisstein) per la regione e il punto. Il punto si trova all’interno della regione se il numero di avvolgimenti non è zero. Fortunatamente, la libreria geopandas di Python fornisce la funzionalità necessaria sia per definire l’interior di una regione poligonale che per testare se un punto si trova all’interno di essa senza tutta la matematica complessa.

Mentre la geocodifica manuale può essere troppo complessa per molte applicazioni, il reverse geocoding manuale può essere un’aggiunta pratica alle tue competenze poiché ti consente di abbinare facilmente i tuoi punti a regioni altamente personalizzate. Ad esempio, supponiamo che tu voglia portare la tua fetta di pizza in un parco e fare un picnic. Potresti voler sapere se la pizzeria si trova a breve distanza da un parco. La città di New York fornisce shapefile per i loro parchi come parte del dataset Parks Properties (NYC Parks Open Data Team), e possono anche essere accessibili tramite la loro API SODA utilizzando il seguente codice.

# Estrarre i shapefile dei parchi di NYC
parks = gpd.read_file(
    BytesIO(
        requests.get(
            "https://data.cityofnewyork.us/resource/enfh-gkve.geojson?$limit=5000"
        ).content
    )
)
# Limitare ai parchi con area verde per un picnic
parks = parks.loc[
    parks["typecategory"].isin(
        [
            "Garden",
            "Nature Area",
            "Community Park",
            "Neighborhood Park",
            "Flagship Park",
        ]
    )
]

Questi parchi possono essere aggiunti alla visualizzazione per vedere quali parchi si trovano vicino alla pizzeria.

Ci sono chiaramente alcune opzioni nelle vicinanze, ma determinare la distanza utilizzando i shapefile e il punto può essere difficile e computazionalmente costoso. Invece, può essere applicato il reverse geocoding. Il primo passo, come menzionato in precedenza, è determinare la regione a cui si vuole attaccare il punto. In questo caso, la regione è “una distanza di 1/2 miglio da un parco a New York City”. Il secondo passo è il calcolo se il punto si trova all’interno di una regione, che può essere fatto matematicamente utilizzando i metodi precedentemente menzionati o applicando la funzione “contains” in geopandas. Il codice seguente viene utilizzato per aggiungere un buffer di 1/2 miglio ai confini dei parchi prima di testare per vedere quali regioni bufferizzate dei parchi contengono ora il punto.

# Proietta le coordinate da latitudine e longitudine in metri per i calcoli di distanza
buffered_parks = parks.to_crs(epsg=2263)
pizza_point = pizza_point.to_crs(epsg=2263)
# Aggiunge un buffer alle regioni estendendo il confine di 1/2 miglio = 2640 piedi
buffered_parks = buffered_parks.buffer(2640)
# Trova tutti i parchi le cui regioni bufferizzate contengono la pizzeria
pizza_parks = parks.loc[buffered_parks.contains(pizza_point["geometry"].values[0])]

Questo buffer rivela i parchi nelle vicinanze, che sono evidenziati in blu nell’immagine seguente

Dopo il successful reverse geocoding, hai appreso che ci sono 8 parchi entro mezzo miglio dalla pizzeria in cui potresti fare un picnic. Goditi quella fetta. Pizza Slice di j4p4n

Fonti

  1. Lawler, Josh e Schiess, Peter. ESRM 250: Introduzione ai sistemi informativi geografici nelle risorse forestali. Definizioni di GIS, 12 febbraio 2009, Università di Washington, Seattle. Lezione di classe. https://courses.washington.edu/gis250/lessons/introduction_gis/definitions.html
  2. CSCL PUB. New York OpenData. https://data.cityofnewyork.us/City-Government/road/svwp-sbcd
  3. U.S. Census Bureau Geocoder Documentation. Agosto 2022. https://geocoding.geo.census.gov/geocoder/Geocoding_Services_API.pdf
  4. Shimrat, M., “Algorithm 112: Position of point relative to polygon” 1962, Communications of the ACM Volume 5 Issue 8, Aug.
    1. https://dl.acm.org/doi/10.1145/368637.368653
  5. Hosch, William L.. “Teorema della curva di Jordan”. Enciclopedia Britannica, 13 aprile 2018, https://www.britannica.com/science/Jordan-curve-theorem
  6. Weisstein, Eric W. “Contour Winding Number.” Da MathWorld – Una risorsa web di Wolfram. https://mathworld.wolfram.com/ContourWindingNumber.html
  7. NYC Parks Open Data Team. Parks Properties. 14 aprile 2023. https://nycopendata.socrata.com/Recreation/Parks-Properties/enfh-gkve
  8. j4p4n, “Pizza Slice.” Da OpenClipArt. https://openclipart.org/detail/331718/pizza-slice

Evan Miller è un Data Science Fellow presso Tech Impact, dove utilizza i dati per supportare le organizzazioni non profit e governative con una missione di bene sociale. In precedenza, Evan ha utilizzato il machine learning per addestrare i veicoli autonomi presso la Central Michigan University.