36 votos

Busco la solución más rápida para el análisis de puntos en polígonos de 200 millones de puntos

Tengo un CSV que contiene 200 millones de observaciones con el siguiente 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"

Para cada conjunto de coordenadas (x1/y1 y x2/y2), quiero asignar el Census Tract o Census Block de EE.UU. en el que se encuentra (he descargado el shapefile TIGER del Census tract aquí: ftp://ftp2.census.gov/geo/tiger/TIGER2011/TRACT/tl_2011_08_tract.zip ). Por lo tanto, tengo que hacer una operación de punto en polígono dos veces para cada observación. Es importante que las coincidencias sean muy precisas.

¿Cuál es la forma más rápida de hacerlo, incluyendo el tiempo para aprender el software? Tengo acceso a un ordenador con 48GB de memoria -- en caso de que eso pueda ser una restricción relevante.

Varios hilos recomiendan usar PostGIS o Spatialite (Spatialite parece más fácil de usar, pero ¿es tan eficiente como PostGIS?) Si esas son las mejores opciones, ¿es imprescindible rellenar un índice espacial (RTree?)? Si es así, ¿cómo se hace (por ejemplo, utilizando el Census Tract Shapefile)? Estaría muy agradecido por cualquier recomendación que incluya un código de ejemplo (o un puntero a un código de ejemplo).

Mi primer intento (antes de encontrar este sitio) consistió en utilizar ArcGIS para hacer una unión espacial (sólo x1/y1) de la submuestra de datos (100.000 puntos) en US Census Block. Eso me llevó más de 5 horas antes de matar el proceso. Espero una solución que pueda ser implementada en todo el conjunto de datos en menos de 40 horas de tiempo de computación.

Pido disculpas por hacer una pregunta que ya se ha hecho antes. He leído las respuestas y me he quedado con la duda de cómo poner en práctica las recomendaciones. Nunca he utilizado SQL, Python, C, y sólo he utilizado ArcGIS una vez antes - soy un completo principiante.

28voto

Lars Mæhlum Puntos 4569

ST_DWithin fue más rápido en mi prueba que ST_Intersects. Esto es sorprendente, sobre todo porque se supone que el algoritmo de geometría preparada entra en acción en casos como éste. Creo que existe la posibilidad de que esto sea mucho más rápido de lo que he mostrado aquí.


Hice más pruebas y dos cosas casi duplicaron la velocidad. En primer lugar, he probado en un ordenador más nuevo, pero todavía un portátil bastante ordinario, tal vez a excepción de SATA3 ssd -discos.

Entonces, la consulta que aparece a continuación tardó 18 segundos en lugar de 62 segundos en el antiguo portátil. Luego descubrí que estaba totalmente equivocado cuando escribí que el índice en la tabla de puntos no era necesario. Con ese índice en su lugar ST_Intersects se comportó como se esperaba y las cosas se volvieron muy rápidas. Aumenté el número de puntos en la tabla de puntos a 1 millón de puntos y la consulta:

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);

se ejecuta en 72 segundos. Como hay 1249 polígonos, se hacen 1249000000 pruebas en 72 segundos. Eso hace unas 17000000 pruebas por segundo. O bien, probar casi 14000 puntos contra todos los polígonos por segundo.

A partir de esta prueba tus 400000000 puntos a probar deberían tardar unas 8 horas sin ningún problema con la distribución de la carga a varios núcleos. PostGIS nunca deja de impresionarme :-)


En primer lugar, para visualizar el resultado puedes añadir la geometría de puntos a la tabla resultante, abrirla en QGIS por ejemplo y darle estilo con valores únicos en el campo imported_ct.

En segundo lugar, sí, puedes obtener también los puntos que caen fuera de cualquier polígono utilizando una unión a la derecha (o a la izquierda) como esta:

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);

Hice algunas pruebas para verificar si parece posible PostGIS.

Primero algo que no entiendo. Tienes dos puntos por fila. ¿Están siempre los dos puntos en el mismo polígono? Entonces basta con hacer los cálculos sobre uno de los puntos. Si pueden estar en dos polígonos diferentes necesitarás una forma de conectar una fila de puntos a dos polígonos.

Por las pruebas parece que se puede hacer, pero puede que necesites alguna solución creativa para repartir la carga entre más de un núcleo de la cpu.

Lo he probado en un portátil de 4 años con cpu centrino de doble núcleo (unos 2,2GHz creo) , 2GB de RAM. Si tienes 48 BG de RAM supongo que también tienes mucha más potencia de cpu.

Lo que hice fue crear una tabla de puntos aleatorios con 100000 puntos así:

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;

Entonces añadiendo un gid como:

ALTER TABLE t ADD COLUMN GID SERIAL;

Entonces, corriendo :

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);

tarda unos 62 segundos (compárese con el resultado de ArcGIS con la misma cantidad de puntos). El resultado es una tabla que conecta los puntos de mi tabla t con el gid de la tabla con tramo censal.

Con esa velocidad harás 200 millones de puntos en unas 34 horas. Así que, si es suficiente con comprobar uno de los puntos, mi viejo portátil puede hacerlo con un solo núcleo.

Pero si tiene que comprobar ambos puntos puede ser más difícil.

Entonces puedes distribuir manualmente la carga a más de un núcleo iniciando varias sesiones contra la base de datos y ejecutando diferentes consultas.

En mi ejemplo con 50000 puntos y dos núcleos de cpu he probado :

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);

en una sesión db al mismo tiempo que se ejecuta:

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);

en otra sesión db.

Eso tomó alrededor de 36 segundos por lo que es un poco más lento que el primer ejemplo, probablemente en función de la escritura del disco al mismo tiempo. Pero como los dos núcleos están trabajando al mismo tiempo, no me llevó más de 36 segundos.

Para la unión de la tabla t1 y t2 se intentó:

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

utilizando alrededor de medio segundo.

Así que, con un hardware más fresco y distribuyendo la carga entre muchos núcleos, esto debería ser absolutamente posible aunque el mundo real sea más lento que el caso de prueba.

Cabe destacar que el ejemplo es de Linux (Ubuntu). Usar Windows será otra historia. Pero tengo todas las demás aplicaciones diarias en funcionamiento por lo que el portátil está bastante cargado de antes. Así que podría simular el caso de Windows bastante bien tal vez, sin abrir nada más que pgadmin.

4voto

UberAlex Puntos 1854

Probablemente la forma más fácil es con PostGIS. Hay algunos tutoriales en Internet sobre cómo importar datos de puntos csv/txt a PostGIS. Enlace1

No estoy seguro del rendimiento de las búsquedas de puntos en polígonos en PostGIS; debería ser más rápido que ArcGIS. El índice espacial GIST que utiliza PostGIS es bastante rápido. Enlace2 Enlace3

También puede probar Índice geoespacial de MongoDB . Pero esto requiere un poco más de tiempo para empezar. Creo que MongoDB podría ser muy rápido. No lo he probado con búsquedas de puntos en polígonos así que no puedo estar seguro.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X