Alla ricerca della soluzione più rapida per l'analisi Point in Polygon di 200 milioni di punti [chiuso]


35

Ho un CSV contenente 200 milioni di osservazioni con il seguente formato:

id,x1,y1,x2,y2,day,color
1,"-105.4652334","39.2586939","-105.4321296","39.2236632","Monday","Black"
2,"-105.3224523","39.1323299","-105.4439944","39.3352235","Tuesday","Green"
3,"-104.4233452","39.0234355","-105.4643990","39.1223435","Wednesday","Blue"

Per ogni set di coordinate (x1 / y1 e x2 / y2), desidero assegnare il tratto di censimento o blocco censimento degli Stati Uniti in cui rientra (ho scaricato il file di forma TIGER del tratto di censimento qui: ftp://ftp2.census.gov/ geo / tiger / TIGER2011 / TRACT / tl_2011_08_tract.zip ). Quindi, devo fare un'operazione point-in-poligono due volte per ogni osservazione. È importante che le partite siano molto precise.

Qual è il modo più veloce per farlo, incluso il tempo per imparare il software? Ho accesso a un computer con 48 GB di memoria, nel caso ciò possa costituire un vincolo rilevante.

Diversi thread raccomandano di utilizzare PostGIS o Spatialite (Spatialite sembra più facile da usare, ma è efficiente come PostGIS?). Se queste sono le migliori opzioni, è indispensabile popolare un indice spaziale (RTree?)? In tal caso, come si fa (ad es. Utilizzando il Census Tract Shapefile)? Sarei estremamente grato per qualsiasi consiglio che includa un codice di esempio (o un puntatore a un codice di esempio).

Il mio primo tentativo (prima di trovare questo sito) consisteva nell'usare ArcGIS per eseguire un join spaziale (solo x1 / y1) del sottocampione dei dati (100.000 punti) su US Census Block. Ci sono volute più di 5 ore prima che io uccidessi il processo. Spero in una soluzione che possa essere implementata sull'intero set di dati in meno di 40 ore di tempo di elaborazione.

Mi scuso per aver posto una domanda che è stata posta in precedenza: ho letto le risposte e mi chiedo come implementare le raccomandazioni. Non ho mai usato SQL, Python, C e ho usato ArcGIS solo una volta prima: sono un principiante assoluto.


3
40 ore equivalgono a quasi 2800 operazioni point-in-poligono al secondo. Semplicemente non sembra possibile nella mia mente. Non ho idea di quale software (ArcGIS, PostGIS, Spatialite ecc.) Sia il più veloce, ma senza dubbio è necessario un indice spaziale.
Uffe Kousgaard

1
Non dovrebbe essere un problema se i poligoni non sono complessi. Il guadagno dall'indice (in PostGIS) dipenderà dalla dimensione dei poligoni. Più piccoli sono i poligoni (più piccoli riquadri), più gli indici saranno d'aiuto. Probabilmente è possibile
Nicklas Avén,

1249 poligoni con ~ 600 punti per poligono.
Uffe Kousgaard

3
@Uffe Kousgaard, sì, è assolutamente possibile. Mi hai fatto provare. Vedi la risposta qui sotto.
Nicklas Avén,

Complimenti per essere all'altezza della sfida! In alcuni test al banco, SpatialLite si comporta in modo più veloce di PostGIS, ma devi fare attenzione a come imposti i tuoi RTree. Inoltre, ho spesso riscontrato che ArcGIS è più lento quando si esegue da "interno" ma più veloce quando si esegue con un "modulo ArcPy autonomo" all'esterno ".
MappaGnosis,

Risposte:


27

ST_DWithin è stato più veloce nel mio test rispetto a ST_Intersects. Ciò è sorprendente, soprattutto perché si suppone che l'algoritmo della geometria preparata dia inizio a casi come questo. Penso che ci sia una possibilità che questo sarà molto più veloce di quello che ho mostrato qui.


Ho fatto qualche altro test e due cose hanno quasi raddoppiato la velocità. In primo luogo, ho provato su un computer più recente, ma ancora un laptop abbastanza ordinario, forse ad eccezione dei dischi sd3 SATA3.

Quindi la query seguente ha richiesto 18 secondi anziché 62 secondi sul vecchio laptop. Successivamente ho scoperto di aver sbagliato completamente prima quando ho scritto che l'indice sulla tabella dei punti non era necessario. Con questo indice in atto, ST_Intersects si è comportato come previsto e le cose sono diventate molto veloci. Ho aumentato il numero di punti nella tabella dei punti a 1 milione di punti e la query:

CREATE TABLE points_ct AS
SELECT imported_ct.gid as ct_gid, t.gid as point_id 
FROM imported_ct , t WHERE ST_Intersects(imported_ct.geom , t.geom);

dura 72 secondi. Poiché ci sono 1249 poligoni, i test 1249000000 vengono eseguiti in 72 secondi. Ciò rende circa 17000000 test al secondo. O testando quasi 14000 punti contro tutti i poligoni al secondo.

Da questo test i 400000000 punti da testare dovrebbero durare circa 8 ore senza problemi con la distribuzione del carico su più core. PostGIS non smette mai di impressionarmi :-)


Innanzitutto, per visualizzare il risultato è possibile aggiungere la geometria del punto alla tabella risultante, aprirla ad esempio in QGIS e modellarla con valori univoci nel campo Import_ct.

In secondo luogo, sì, puoi ottenere anche i punti che cadono fuori da qualsiasi poligono usando un join destro (o sinistro) in questo modo:

CREATE TABLE points_ct AS
SELECT imported_ct.gid as ct_gid, t.gid as point_id 
FROM imported_ct right join t ON ST_Intersects(imported_ct.the_geom , t.geom);

Ho fatto alcuni test per verificare se PostGIS sembra possibile.

Prima cosa che non capisco. Hai due punti per riga. Entrambi i punti sono sempre nello stesso poligono? Quindi è sufficiente fare i calcoli su uno dei punti. Se possono trovarsi in due poligoni diversi, avrai bisogno di un modo per collegare una fila di punti a due poligoni.

Dai test sembra fattibile, ma potresti aver bisogno di una soluzione creativa per distribuire il carico su più di un CPU.

Ho testato su un laptop di 4 anni con CPU dual core centrino (credo a circa 2,2 GHz), 2 GB di RAM. Se hai 48 BG RAM, suppongo che tu abbia anche molta più potenza della CPU.

Quello che ho fatto è stato creare una tabella di punti casuale con 100000 punti come questo:

CREATE TABLE t AS
WITH r AS
(SELECT ST_Extent(the_geom)::geometry ext FROM imported_ct)
SELECT ST_Point(x,y) AS geom FROM 
(SELECT GENERATE_SERIES(1,100000)) s,
(SELECT ST_Xmin(ext)+(random()*(ST_Xmax(ext)-ST_Xmin(ext))) x, ST_Ymin(ext)+(random()*(ST_Ymax(ext)-ST_Ymin(ext))) y FROM r
) f;

Quindi aggiungendo un gid come:

ALTER TABLE t ADD COLUMN GID SERIAL;

Quindi in esecuzione:

CREATE TABLE points_ct AS
SELECT imported_ct.gid as ct_gid, t.gid as point_id FROM imported_ct , t WHERE ST_Dwithin(imported_ct.the_geom , t.geom,0);

dura circa 62 secondi (confronta con il risultato ArcGIS con lo stesso numero di punti). Il risultato è una tabella che collega i punti nella mia tabella t con la gid nella tabella con tratto di censimento.

Con quella velocità otterrai circa 200 punti in circa 34 ore. Quindi, se è sufficiente verificare uno dei punti, il mio vecchio laptop può farlo con un core.

Ma se è necessario controllare entrambi i punti, potrebbe essere più difficile.

Quindi è possibile distribuire manualmente il carico su più core avviando più sessioni sul db ed eseguendo query diverse.

Nel mio esempio con 50000 punti e due CPU-core ho provato:

CREATE TABLE t1 as
SELECT imported_ct.gid as ct_gid, t.gid as point_id FROM imported_ct , t WHERE t.gid >50000 and  ST_Dwithin(imported_ct.the_geom , t.geom,0);

su una sessione db contemporaneamente a esecuzione:

CREATE TABLE t2 as
SELECT imported_ct.gid as ct_gid, t.gid as point_id FROM imported_ct , t WHERE t.gid <=50000 and  ST_Dwithin(imported_ct.the_geom , t.geom,0);

in un'altra sessione db.

Ci sono voluti circa 36 secondi, quindi è un po 'più lento del primo esempio, probabilmente a seconda della scrittura del disco contemporaneamente. Ma dal momento che i core Bith funzionano contemporaneamente, non ci sono voluti più di 36 secondi del mio tempo.

Per unire le tabelle t1 e t2 a provato:

CREATE TABLE t3 AS 
SELECT * FROM t1
UNION ALL
SELECT * FROM t2;

usando circa mezzo secondo.

Quindi, con hardware più fresco e distribuzione del carico su molti core, ciò dovrebbe essere assolutamente possibile anche se il mondo reale sarà più lento del test case.

Vale la pena notare che l'esempio è di Linux (Ubuntu). L'uso di Windows sarà un'altra storia. Ma ho tutte le altre applicazioni quotidiane in esecuzione, quindi il laptop è abbastanza carico da prima. Quindi questo potrebbe simulare il caso di Windows abbastanza bene forse, senza aprire altro che pgadmin.


1
Ho appena rinominato .tl_2011_08_trac in import_ct perché era più facile da scrivere. Quindi, basta importare import_ct nella mia query in .tl_2011_08_trac e dovresti andare bene.
Nicklas Avén,

2
@meer BTW, non è consigliabile utilizzare template_postgis_20 come nient'altro che un modello per database futuri. Poiché sembra che tu abbia PostGIS 2.0, se hai anche PostgreSQL 9.1 puoi semplicemente creare un nuovo db ed eseguire "CREATE EXTENSION POSTGIS;"
Nicklas Avén,

1
Sì, quello era un altro errore di battitura che penso di aver risolto pochi minuti fa. Mi dispiace per quello. Prova anche la versione ST_Intersects, che dovrebbe essere molto più veloce.
Nicklas Avén,

1
@meer Il motivo per cui non tutti i punti sono interessati è che i punti casuali sono posizionati in un rettangolo e immagino che la mappa non sia esattamente un rettangolo. Farò una modifica nel post per mostrare come vedere il risultato.
Nicklas Avén,

1
@Uffe Kousgaard, Sì, immagino che tu possa dirlo in quel modo. Prende un poligono alla volta e lo prepara costruendo un albero dei bordi. Quindi controlla tutti i punti (che l'indice ha classificato come interessanti da sovrapporre bbox) a quel poligono preparato.
Nicklas Avén,

4

Probabilmente il modo più semplice è con PostGIS. Ci sono alcuni tutorial su Internet su come importare i dati dei punti csv / txt in PostGIS. link1

Non sono sicuro dell'esecuzione delle ricerche point-in-polygon in PostGIS; dovrebbe essere più veloce di ArcGIS. L'indice spaziale GIST utilizzato da PostGIS è piuttosto veloce. Link2 Link3

Puoi anche testare l' indice geospaziale MongoDB . Ma questo richiede poco più tempo per iniziare. Credo che MongoDB potrebbe essere davvero veloce. Non l'ho testato con ricerche point-in-polygon quindi non posso esserne sicuro.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.