Prossimità nello spazio e nel tempo


10

Ho alcuni dati puntuali che rappresentano le posizioni giornaliere del lat-lon di un animale, con un timestamp associato.

Vorrei identificare tutti i punti in cui STATIONARY = TRUE. Un punto si qualifica stazionario se un buffer di 100 km attorno ad esso si sovrappone ad altri 5 (ad esempio) punti temporalmente adiacenti . Quindi, se il giorno 10 è il mio punto di interesse, voglio chiedermi se 5 giorni temporalmente adiacenti si trovano all'interno di un buffer di 100 km da questo punto. Se giorni 5,6,7,8 & 9; OR giorni 11,12,13,14 e 15; O i giorni 8,9,11,12,13 (ecc.) Sono all'interno del buffer, quindi STATIONARY = TRUE. Se, tuttavia, i giorni 5,7,9,11 e 13 sono all'interno del buffer, ma non i giorni (pari) alternativi tra, quindi STATIONARY = FALSE

Penso che una sorta di spostamento del buffer della finestra fornirà la soluzione, ma non so come implementarlo.

Ho cercato di risolvere il problema con ArcGIS e R, ma finora non ho avuto onde cerebrali. Questa è la soluzione più vicina a una soluzione, ma non si adatta perfettamente, non credo: identificazione di punti consecutivi all'interno di un buffer specificato

Ecco alcuni dati fittizi, che approssimano la mia struttura di dati (anche se in realtà ho due posizioni giornaliere (mezzogiorno e mezzanotte) con alcune posizioni mancanti - ma mi preoccuperò più avanti)

x<-seq(0,15,length.out=20)
y<-seq(10,-10,length.out=20)
t<-seq(as.POSIXct('2013-07-01'), length.out = 20, by = "days")
data<-data.frame(cbind(x,y,t=as.data.frame.POSIXct(t)))


            x           y          t
1   0.0000000  10.0000000 2013-07-01
2   0.7894737   8.9473684 2013-07-02
3   1.5789474   7.8947368 2013-07-03
4   2.3684211   6.8421053 2013-07-04
5   3.1578947   5.7894737 2013-07-05
6   3.9473684   4.7368421 2013-07-06
7   4.7368421   3.6842105 2013-07-07
... ...         ...       ...

1
Domanda? Supponendo che tutti e 10 i punti siano all'interno del buffer e si abbia una separazione della data (a partire dal giorno 1) di 1-3-4-12-13-20-21-22-29-30, allora stai dicendo che sei interessato solo a selezionare i punti che sono in giorni 1,2,3,4 e 12?
Hornbydd,

No, sarei interessato solo nei giorni 1-4. Se l'animale "lascia" il buffer ritorna il giorno 12 (o il giorno 6), ciò "annulla" quel periodo di sosta, ovvero l'animale deve trovarsi nel buffer il giorno 1-2-3-4-5 per il punto al centro del buffer da contare. Ha senso? Non ne sono sicuro ..
Tom Finch,

1
Solo per verificare, se il punto di interesse fosse il 7 ° giorno, ti interesserebbero i punti che rientrano nei 100 km per i giorni 7,8,9,10 e 11?
Hornbydd,

Il punto 7 verrebbe selezionato come punto fermo se i giorni 8,9,10, 11 e 12 fossero entro 100 km. O giorni 5,6,8,9,10. Pertanto, ogni punto viene selezionato se altri 5 punti temporalmente adiacenti (i 5 giorni precedenti, i 5 giorni successivi o alcuni giorni su entrambi i lati) sono tutti all'interno del buffer. Penso che la finestra mobile sia il modo migliore per concettualizzarla. Per ogni punto "focale" si può dimenticare qualsiasi punto nel passato / futuro per più di 5 giorni. Potrei aggiornare la mia domanda originale, poiché ora la capisco un po 'di più ...
Tom Finch,

Qual è il formato dei dati? Ad esempio, hai ogni ora / posizione come punto vettoriale in uno shapefile e una tabella di attributi che memorizza l'ora? O ogni volta / luogo sono memorizzati separatamente in diversi shapefile? I dati non sono nemmeno in un formato geospaziale e semplicemente in un file Excel? Sapere questo ci aiuterebbe a rispondere.

Risposte:


12

Dividiamolo in pezzi semplici. In questo modo, tutto il lavoro viene svolto in una mezza dozzina di righe di codice facilmente testabile.

Innanzitutto, dovrai calcolare le distanze. Poiché i dati sono in coordinate geografiche, ecco una funzione per calcolare le distanze su un dato sferico (usando la formula di Haversine):

#
# Spherical distance.
# `x` and `y` are (long, lat) pairs *in radians*.
dist <- function(x, y, R=1) {
  d <- y - x
  a <- sin(d[2]/2)^2 + cos(x[2])*cos(y[2])*sin(d[1]/2)^2
  return (R * 2*atan2(sqrt(a), sqrt(1-a)))
}

Sostituiscilo con la tua implementazione preferita, se lo desideri (come uno che utilizza un dato ellissoidale).

Successivamente, dovremo calcolare le distanze tra ciascun "punto base" (in fase di verifica della stabilità) e il suo vicinato temporale. È semplicemente una questione di applicazione distal vicinato:

#
# Compute the distances between an array of locations and a base location `x`.
dist.array <- function(a, x, ...) apply(a, 1, function(y) dist(x, y, ...))

Terzo - questa è l'idea chiave - i punti fissi vengono rilevati rilevando quartieri di 11 punti con almeno cinque di fila le cui distanze sono sufficientemente piccole. Cerchiamo di implementarlo un po 'più in generale, determinando la lunghezza della sottosequenza più lunga dei valori reali all'interno di una matrice logica di valori booleani:

#
# Return the length of the longest sequence of true values in `x`.
max.subsequence <- function(x) max(diff(c(0, which(!x), length(x)+1)))

(Troviamo le posizioni dei valori falsi , in ordine, e calcoliamo le loro differenze: queste sono le lunghezze delle sottosequenze di valori non falsi. Viene restituita la lunghezza più grande di tali.)

In quarto luogo, applichiamo max.subsequenceper rilevare punti fissi.

#
# Determine whether a point `x` is "stationary" relative to a sequence of its
# neighbors `a`.  It is provided there is a sequence of at least `k`
# points in `a` within distance `radius` of `x`, where the earth's radius is
# set to `R`.
is.stationary <- function(x, a, k=floor(length(a)/2), radius=100, R=6378.137) 
  max.subsequence(dist.array(a, x, R) <= radius) >= k

Questi sono tutti gli strumenti di cui abbiamo bisogno.


Ad esempio, creiamo alcuni dati interessanti con alcuni gruppi di punti stazionari. Farò una passeggiata casuale vicino all'equatore.

set.seed(17)
n <- 67
theta <- 0:(n-1) / 50 - 1 + rnorm(n, sd=1/2)
rho <- rgamma(n, 2, scale=1/2) * (1 + cos(1:n / n * 6 * pi))
lon <- cumsum(cos(theta) * rho); lat <- cumsum(sin(theta) * rho)

Le matrici lone latcontengono le coordinate, in gradi, dei npunti in sequenza. L'applicazione dei nostri strumenti è semplice dopo la prima conversione in radianti:

p <- cbind(lon, lat) * pi / 180 # Convert from degrees to radians
p.stationary <- sapply(1:n, function(i) 
  is.stationary(p[i,], p[max(1,i-5):min(n,i+5), ], k=5))

L'argomento p[max(1,i-5):min(n,i+5), ]dice di guardare fino a 5 passi temporali o fino a 5 passi temporali dal punto base p[i,]. Includendo k=5dice di cercare una sequenza di 5 o più di fila che si trovano entro 100 km dal punto base. (Il valore di 100 km è stato impostato come predefinito in is.stationaryma è possibile sovrascriverlo qui.)

L'output p.stationaryè un vettore logico che indica la stazionarietà: abbiamo quello per cui siamo venuti. Tuttavia, per verificare la procedura è meglio tracciare i dati e questi risultati piuttosto che ispezionare le matrici di valori. Nella trama seguente mostro il percorso e i punti. Ogni decimo punto è etichettato in modo da poter stimare quanti potrebbero sovrapporsi all'interno dei gruppi fissi. I punti fermi vengono ridisegnati in rosso fisso per evidenziarli e circondati dai loro respingenti di 100 km.

figura

plot(p, type="l", asp=1, col="Gray", 
     xlab="Longitude (radians)", ylab="Latitude (radians)")
points(p)
points(p[p.stationary, ], pch=19, col="Red", cex=0.75)
i <- seq(1, n, by=10)
#
# Because we're near the Equator in this example, buffers will be nearly 
# circular: approximate them.
disk <- function(x, r, n=32) {
  theta <- 1:n / n * 2 * pi
  return (t(rbind(cos(theta), sin(theta))*r + x))
}
r <- 100 / 6378.137  # Buffer radius in radians
apply(p[p.stationary, ], 1, function(x) 
  invisible(polygon(disk(x, r), col="#ff000008", border="#00000040")))
text(p[i,], labels=paste(i), pos=3, offset=1.25, col="Gray")

Per altri approcci (basati su statistiche) alla ricerca di punti fissi nei dati tracciati, incluso il codice di lavoro, visitare /mathematica/2711/clustering-of-space-time-data .


Wow grazie! non vedo l'ora di farmi la testa. grazie ancora per il tuo tempo e impegno
Tom Finch,
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.