Dada uma lista de locais dos pontos (de preferência nas coordenadas projetadas, para facilitar a computação das distâncias), esse problema pode ser resolvido com cinco operações mais simples :
Calcular distâncias ponto a ponto.
Para cada ponto i, i = 1, 2, ..., identifique os índices desses pontos a distâncias menores que o raio do buffer (como 1500).
Restrinja esses índices a serem i ou superiores.
Reter apenas o primeiro grupo consecutivo de índices sem interrupção.
Saída a contagem desse grupo.
Em R
, cada um deles corresponde a uma operação. Para aplicar essa sequência a cada ponto, é conveniente encapsular a maior parte do trabalho dentro de uma função que definimos , assim:
#
# forward(j, xy, r) counts how many contiguous rows in array xy, starting at index j,
# are within (Euclidean) distance r of the jth row of xy.
#
forward <- function(j, xy, r) {
# Steps 1 and 2: compute an array of indexes of points within distance r of point j.
i <- which(apply(xy, 1, function(x){sum((x-xy[j,])^2) <= r^2}))
# Step 3: select only the indexes at or after j.
i <- i[i >= j]
# Steps 4 and 5: retain only the first consecutive group and count it.
length(which(i <= (1:length(i) + j)))
}
(Veja abaixo uma versão mais eficiente dessa função.)
Tornei essa função flexível o suficiente para aceitar várias listas de pontos ( xy
) e distâncias de buffer ( r
) como parâmetros.
Normalmente, você lia um arquivo de locais dos pontos (e, se necessário, os classifica por tempo). Aqui, para mostrar isso em ação, apenas geraremos alguns dados de amostra aleatoriamente :
# Create sample data
n<-16 # Number of points
set.seed(17) # For reproducibility
xy <- matrix(rnorm(2*n) + 1:n, n, 2) * 300
#
# Display the track.
plot(xy, xlab="x", ylab="y")
lines(xy, col="Gray")
Seu espaçamento típico é de 300 * Sqrt (2) = cerca de 500. Fazemos o cálculo aplicando essa função aos pontos da matrizxy
(e depois voltando a seus resultados xy
, pois esse seria um formato conveniente para exportar para um GIS ):
radius <- 1500
z <- sapply(1:n, function(u){forward(u,xy,radius)})
result <- cbind(xy, z) # List of points, counts
Você analisaria ainda mais a result
matriz, R
gravando-a em um arquivo e importando-a para outro software. Aqui está o resultado para os dados da amostra :
z
[1,] -4.502615 551.5413 4
[2,] 576.108979 647.8110 3
[3,] 830.103893 1087.7863 4
[4,] 954.819620 1390.0754 3
...
[15,] 4977.361529 4146.7291 2
[16,] 4783.446283 4511.9500 1
(Lembre-se de que as contagens incluem os pontos em que se baseiam, de modo que cada contagem deve ser 1 ou maior.)
Se você tem muitos milhares de pontos, esse método é muito ineficiente : calcula distâncias ponto a ponto demais desnecessárias. Mas, como encapsulamos o trabalho dentro da forward
função, a ineficiência é fácil de corrigir. Aqui está uma versão que funcionará melhor quando mais de algumas centenas de pontos estiverem envolvidos:
forward <- function(j, xy, r) {
n <- dim(xy)[1] # Limit the search to the number of points in xy
r2 <- r^2 # Pre-compute the squared distance threshold
z <- xy[j,] # Pre-fetch the base point coordinates
i <- j+1 # Initialize an index into xy (just past point j)
# Advance i while point i remains within distance r of point j.
while(i <= n && sum((xy[i,]-z)^2) <= r2) i <- i+1
# Return the count (including point j).
i-j
}
Para testar isso, criei pontos aleatórios como anteriormente, mas variei dois parâmetros: n
(o número de pontos) e seu desvio padrão (codificado como 300 acima). O desvio padrão determina o número médio de pontos dentro de cada buffer ("média" na tabela abaixo): quanto mais houver, mais tempo esse algoritmo demora para ser executado. (Com algoritmos mais sofisticados, o tempo de execução não depende tanto de quantos pontos existem em cada buffer.) Aqui estão alguns horários:
Time (sec) n SD Average Distances checked per minute
1.30 10^3 3 291 13.4 million
1.72 10^4 30 35.7 12.5
2.50 10^5 300 3.79 9.1
16.4 10^6 3000 1.04 3.8