Dada una lista de ubicaciones de puntos (preferiblemente en coordenadas proyectadas, para que las distancias sean fáciles de calcular), este problema se puede resolver con cinco operaciones más simples :
Calcular distancias punto-punto.
Para cada punto i, i = 1, 2, ..., identifique los índices de esos puntos a distancias inferiores al radio del búfer (como 1500).
Restrinja esos índices para que sean i o mayores.
Retenga solo el primer grupo consecutivo de índices sin interrupción.
Salida de la cuenta de ese grupo.
En R
, cada uno de estos corresponde a una operación. Para aplicar esta secuencia a cada punto, es conveniente encapsular la mayor parte del trabajo dentro de una función que definimos , por lo tanto:
#
# 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)))
}
(Consulte a continuación una versión más eficiente de esta función).
He hecho esta función lo suficientemente flexible como para aceptar varias listas de puntos ( xy
) y distancias de búfer ( r
) como parámetros.
Normalmente, leería un archivo de ubicaciones de puntos (y, si fuera necesario, los ordenaría por hora). Aquí, para mostrar esto en acción, solo generaremos algunos datos de muestra al azar :
# 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")
Su espaciado típico es 300 * Sqrt (2) = aproximadamente 500. Hacemos el cálculo aplicando esta función a los puntos en la matrizxy
(y luego añadiendo sus resultados nuevamente xy
, porque este sería un formato conveniente para exportar a un SIG ):
radius <- 1500
z <- sapply(1:n, function(u){forward(u,xy,radius)})
result <- cbind(xy, z) # List of points, counts
Luego analizaría más a fondo la result
matriz, ya sea R
escribiéndola en un archivo e importándola a otro software. Aquí está el resultado para los datos de muestra :
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
(Recuerde que los recuentos incluyen los puntos en los que se basan, por lo que cada recuento debe ser 1 o mayor).
Si tiene muchos miles de puntos, este método es demasiado ineficiente : calcula demasiadas distancias punto a punto que son innecesarias. Pero debido a que hemos encapsulado el trabajo dentro de la forward
función, la ineficiencia es fácil de solucionar. Aquí hay una versión que funcionará mejor cuando estén involucrados más de unos pocos cientos de puntos:
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 probar esto, creé puntos aleatorios como anteriormente, pero varié dos parámetros: n
(el número de puntos) y su desviación estándar (codificada como 300 arriba). La desviación estándar determina el número promedio de puntos dentro de cada búfer ("promedio" en la tabla a continuación): cuantos más haya, más tardará en ejecutarse este algoritmo. (Con algoritmos más sofisticados, el tiempo de ejecución no dependerá tanto de cuántos puntos haya en cada búfer). Aquí hay algunos tiempos:
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