Vamos dividir isso em pedaços simples. Ao fazer isso, todo o trabalho é realizado em apenas meia dúzia de linhas de código facilmente testado.
Primeiro, você precisará calcular as distâncias. Como os dados estão em coordenadas geográficas, aqui está uma função para calcular distâncias em um dado esférico (usando a fórmula 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)))
}
Substitua por sua implementação favorita, se desejar (como uma que usa um dado elipsoidal).
Em seguida, precisaremos calcular as distâncias entre cada "ponto base" (que está sendo verificado quanto à estagnação) e sua vizinhança temporal. Isso é simplesmente uma questão de se aplicar dist
ao bairro:
#
# 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, ...))
Terceiro - essa é a idéia principal - pontos estacionários são encontrados detectando bairros de 11 pontos com pelo menos cinco em uma linha cujas distâncias são suficientemente pequenas. Vamos implementar isso um pouco mais geralmente, determinando o comprimento da subsequência mais longa de valores verdadeiros dentro de uma matriz lógica de valores booleanos:
#
# 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)))
(Nós encontramos os locais dos valores falsos , em ordem, e calculamos suas diferenças: estes são os comprimentos das subsequências de valores não falsos. O maior tamanho é retornado.)
Quarto, aplicamos max.subsequence
para detectar pontos estacionários.
#
# 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
Essas são todas as ferramentas que precisamos.
Como exemplo, vamos criar alguns dados interessantes com alguns grupos de pontos estacionários. Vou dar um passeio aleatório perto do Equador.
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)
As matrizes lon
e lat
contêm as coordenadas, em graus, dos n
pontos em sequência. A aplicação de nossas ferramentas é simples após a primeira conversão em radianos:
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))
O argumento p[max(1,i-5):min(n,i+5), ]
diz para olhar para trás em 5 etapas de tempo ou até 5 etapas de tempo a partir do ponto base p[i,]
. A inclusão k=5
diz para procurar uma sequência de 5 ou mais em uma fileira que esteja a 100 km do ponto base. (O valor de 100 km foi definido como padrão, is.stationary
mas você pode substituí-lo aqui.)
A saída p.stationary
é um vetor lógico indicando estacionariedade: nós temos o que buscamos. No entanto, para verificar o procedimento, é melhor plotar os dados e esses resultados em vez de inspecionar matrizes de valores. No gráfico a seguir, mostro a rota e os pontos. Cada décimo ponto é rotulado para que você possa estimar quantos podem se sobrepor dentro dos grupos estacionários. Os pontos estacionários são redesenhados em vermelho sólido para destacá-los e cercados por seus amortecedores de 100 km.
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")
Para outras abordagens (baseadas em estatística) para encontrar pontos estacionários em dados rastreados, incluindo código de trabalho, visite /mathematica/2711/clustering-of-space-time-data .