Come funziona il poligono spaziale% su% poligono quando si aggregano valori in r?


12

Sto lavorando a un progetto di epidemiologia ambientale in cui ho esposizioni puntuali (~ 2.000 operazioni di maiale industriale - IHO). Questi IHO spruzzano sui campi vicini, ma le feci goccioline d'acqua e l'odore possono percorrere miglia. Quindi queste esposizioni puntuali ottengono buffer di 3 mi e voglio sapere il numero di esposizioni IHO (di vario tipo - somma della quantità di letame, numero di maiali, qualunque cosa; la più semplice, solo il numero di buffer di esposizione sovrapposti) per blocchi di censimento NC (~ 200.000). I blocchi di censimento di esclusione (blu) sono (1) qualsiasi cosa nelle prime 5 città più popolose e (2) contee che non confinano con una contea con un IHO (nota: che è stato fatto con la funzione gRelate e i codici DE-9IM - molto liscio!). Vedi sotto l'immagine per un visual

inserisci qui la descrizione dell'immagine

L'ultimo passaggio consiste nell'aggregare la rappresentazione dell'esposizione bufferizzata in ogni blocco censimento. Ecco dove sono perplesso.

Finora mi sono divertito molto con le funzioni% over% nel pacchetto sp, ma capisco dalla vignetta over che il poly-poly e il poly-line over sono implementati in rgeos. La vignetta copre solo il poli-linea e il poli autoreferenziale, e non con aggregazione, quindi sono un po 'confuso su quali sono le mie opzioni per il poli-poli con aggregazione di funzioni, come somma o media.

Per un caso di prova, prendi in considerazione lo snippet di seguito, piuttosto dettagliato, che funziona con il file dei confini dei paesi del mondo. Questo dovrebbe essere in grado di essere copiato ed eseguito così com'è, dal momento che sto usando un seme casuale per i punti e dal momento che sto scaricando e decomprimendo il file mondiale in codice.

Innanzitutto, creiamo 100 punti, quindi utilizziamo la funzione over con l'argomento fn per aggiungere l'elemento nel frame di dati. Ci sono molti punti qui, ma dai un'occhiata all'Australia: 3 punti, il numero 3 come etichetta. Fin qui tutto bene.

inserisci qui la descrizione dell'immagine

Ora trasformiamo le geometrie in modo da poter creare buffer, trasformare indietro e mappare quei buffer. (Incluso nella mappa precedente, dato che sono limitato a due link.) Vogliamo sapere quanti buffer si sovrappongono in ciascun paese - nel caso dell'Australia, a occhio, sono 4. Non posso per la vita di me capire cosa sta succedendo però per ottenerlo con la funzione over. Vedi il mio pasticcio di un tentativo nelle ultime righe di codice.

EDIT: Nota che un commentatore su r-sis-geo ha menzionato la funzione aggregata - riferita anche sulla domanda di scambio di stack 63577 - quindi un aggancio / flusso potrebbe essere attraverso quella funzione, ma non capisco perché dovrei andare aggregare per polipolio quando finito sembra avere quella funzionalità per altri oggetti spaziali.

require(maptools)
require(sp)
require(rgdal)
require(rgeos)

download.file("http://thematicmapping.org/downloads/TM_WORLD_BORDERS_SIMPL-0.3.zip", destfile="world.zip")
unzip("world.zip")
world.map = readOGR(dsn=".", "TM_WORLD_BORDERS_SIMPL-0.3", stringsAsFactors = F)
orig.world.map = world.map #hold the object, since I'm going to mess with it.

#Let's create 500 random lat/long points with a single value in the data frame: the number 1
set.seed(1)
n=100
lat.v = runif(n, -90, 90)
lon.v = runif(n, -180, 180)
coords.df = data.frame(lon.v, lat.v)
val.v = data.frame(rep(1,n))
names(val.v) = c("val")
names(coords.df) = c("lon", "lat")
points.spdf = SpatialPointsDataFrame(coords=coords.df, proj4string=CRS("+proj=longlat +datum=WGS84"), data=val.v)
points.spdf = spTransform(points.spdf, CRS(proj4string(world.map)))
plot(world.map, main="World map and points") #replot the map
plot(points.spdf, col="red", pch=20, cex=1, add=T) #...and add points.

#Let's use over with the point data
join.df = over(geometry(world.map), points.spdf,  fn=sum)
plot(world.map, main="World with sum of points, 750mi buffers") #Note - happens to be the count of points, but only b/c val=1.
plot(points.spdf, col="red", pch=20, cex=1, add=T) #...and add points.
world.map@data = data.frame(c(world.map@data, join.df))
#world.map@data = data.frame(c(world.map@data, over(world.map, points.spdf, fun="sum")))
invisible(text(getSpPPolygonsLabptSlots(world.map), labels=as.character(world.map$val), cex=1))
#Note I don't love making labels like above, and am open to better ways... plus I think it's deprecated/ing

#Now buffer...
pointbuff.spdf = gBuffer(spTransform(points.spdf, CRS("+init=EPSG:3358")), width=c(750*1609.344), byid=T)
pointbuff.spdf = spTransform(pointbuff.spdf, world.map@proj4string)
plot(pointbuff.spdf, col=NA, border="pink", add=T)



#Now over with the buffer (poly %over% poly).  How do I do this?
world.map = orig.world.map
join.df = data.frame(unname(over(geometry(world.map), pointbuff.spdf, fn=sum, returnList = F)) ) #Seems I need to unname this...?
names(join.df) = c("val")
world.map@data = data.frame(c(world.map@data, join.df)) #If I don't mess with the join.df, world.map's df is a mess..
plot(world.map, main="World map, points, buffers...and a mess of wrong counts") #replot the map
plot(points.spdf, col="red", pch=20, cex=1, add=T) #...and add points.
plot(pointbuff.spdf, col=NA, border="pink", add=T)
invisible(text(getSpPPolygonsLabptSlots(world.map), labels=as.character(world.map$val), cex=1)) 
#^ But if I do strip it of labels, it seems to be misassigning the results?
# Australia should now show 4 instead of 3.  I'm obviously super confused, probably about the structure of over poly-poly returns.  Help?

Apprezzo il reindirizzamento: devo eliminare da qui e ripubblicare laggiù? Qual è la mossa migliore? Grazie.
Mike Dolan Fliss,

Risposte:


5

Grazie per la domanda chiara e l'esempio riproducibile.

La tua comprensione è corretta, e questo si riduce a un bug in rgeos :: over, che è stato corretto un mese fa ma non è ancora diventato una versione CRAN. Quanto segue è una soluzione alternativa se sei interessato solo al numero di incroci:

world.map$val = sapply(over(geometry(world.map), pointbuff.spdf, returnList = TRUE), NROW)

Sto usando NROWqui invece lengthche in modo che funzioni con i rgeos errati (0,3-8, da CRAN) e con quelli corretti (0,3-10, da r-forge). Il suggerimento precedente di utilizzare

a = aggregate(pointbuff.spdf, world.map, sum)

conta anche il numero di incroci, ma solo con la versione di rgeos fissa installata. Il suo vantaggio, oltre a un nome più intuitivo, è che restituisce direttamente un Spatialoggetto, con la geometria di world.map.

Per far funzionare rgeos 0,3-8, aggiungi

setMethod("over",
    signature(x = "SpatialPolygons", y = "SpatialPolygonsDataFrame"),
        rgeos:::overGeomGeomDF)

alla tua sceneggiatura, prima di usarla over.


Molto utile, grazie. In particolare, desidero celebrare la tua offerta di una soluzione che funziona prima e dopo la correzione. Ti dispiacerebbe approfondire: (1) Qual è il bug che sto colpendo qui-rgeos :: over sta restituendo una geografia poligonale spaziale, non un frame di poli dati spaziale? Alcune funzioni non restituiscono solo i frame di dati ...? (2) In che modo dovrebbe funzionare generalmente con aggregato e oltre? Sono un po 'confuso riguardo alle differenze e ai casi d'uso previsti. Apprezzo molto il tuo peso, grazie. E sidenote: qualche suggerimento per comprendere il ciclo di rilascio di CRAN?
Mike Dolan Fliss,

Inoltre, per quanto riguarda la domanda originale: ho bisogno di contare il numero di esposizioni, ma ho anche davvero bisogno di sommarle - cose come il numero di maiali in ogni esposizione. Il conteggio delle sovrapposizioni è un inizio ... ma sembra che la soluzione di cui ho bisogno sia quella di inserire i nuovi rgeos, sì? Non c'è modo di fare quell'aggregazione funzionale (non solo il conteggio) senza di essa?
Mike Dolan Fliss,

(1) rgeos :: over per la firma SpatialPolygons,SpatialPolygonsDataFramedovrebbe restituire a data.frame, ma restituisce un vettore indice identico a quando ysarebbe stato SpatialPolygons. sp::aggregatefa quello che fai con over in un modo più user-friendly, restituendo l' Spatialoggetto invece di data.frame. I pacchetti CRAN sono gestiti da volontari.
Edzer Pebesma,

OK, grazie Edzer. Sembra che l'aggregato si basi sui rgeos, quindi per ottenere questa funzionalità in anticipo rispetto al ciclo di rilascio di CRAN (quando lo è), dovrò scoprire come scaricare i rgeos più recenti e risolverli. Grazie. E grazie per tutto il tuo lavoro sul pacchetto !!
Mike Dolan Fliss,

Inoltre, Edzer, grazie mille per la nota su R-sis-geo. Non ero sicuro di dove fosse il posto migliore per pubblicare, quindi sono contento che la discussione ora punti qui.
Mike Dolan Fliss,

1

Nel frattempo ho creato un over-sostituto rapido (e scarsamente codificato) che crea il frame di dati di cui ho bisogno, poiché la mia domanda non ha una risposta completa con la sola soluzione di conteggio precedente o "elimina i nuovi rgeos", che non sono abbastanza abile da capire come fare.

Questa funzione è chiaramente (1) incompleta (nota come ignoro l'argomento fn) e (2) inefficiente, poiché ci arrivo senza le potenti manipolazioni dell'array di R / sapply ... (chiaramente vengo da altre lingue senza quel potere) ma onestamente, sono ancora confuso su ciò che la struttura della funzione over restituisce (elenco di liste ...? E liste vuote se NA?). Per quello che vale (modifiche benvenute), questa funzione svolge il lavoro di cui ho bisogno, con successo, e imita l'azione delle altre funzioni.

Modifiche benvenute:

overhelper <- function(pol, pol.df, fn=sum, verbose=F){
   if(verbose) {cat("Building over geometry...\n"); t=Sys.time(); t}
   geolist = over(geometry(pol), pol.df, returnList = T)
   if(verbose) {cat("Geometry done. Aggregating df. \n"); Sys.time()-t;t=Sys.time();t;}
   results = data.frame(matrix(0,nrow=length(pol), ncol=ncol(pol.df)))
   names(results) = names(pol.df)
   end = length(geolist)

   for (i in 1:end){
     if(verbose) cat(i, "...")
     results[i,] = sapply(pol.df@data[unlist(geolist[i]),], fn)
   }
   if(verbose) cat("Aggregation done! (", Sys.time()-t, ") \n Returning result vector.")
   return (results)
}

1
Ho aggiunto un'alternativa per ottenere rgeos 0,3-8 riparati, alla mia risposta.
Edzer Pebesma,
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.