Mari kita bagi menjadi beberapa bagian sederhana. Dengan melakukan itu, semua pekerjaan diselesaikan hanya dalam setengah lusin baris kode yang mudah diuji.
Pertama, Anda harus menghitung jarak. Karena data berada dalam koordinat geografis, berikut adalah fungsi untuk menghitung jarak pada datum bola (menggunakan rumus 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)))
}
Ganti ini dengan implementasi favorit Anda jika Anda mau (seperti yang menggunakan datum ellipsoidal).
Selanjutnya, kita perlu menghitung jarak antara setiap "titik dasar" (diperiksa untuk staionaritas) dan lingkungan temporal. Itu hanya masalah melamar dist
ke lingkungan:
#
# 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, ...))
Ketiga - ini adalah ide kunci - titik-titik stasioner ditemukan dengan mendeteksi lingkungan dari 11 titik yang memiliki setidaknya lima berturut-turut yang jaraknya cukup kecil. Mari kita terapkan ini sedikit lebih umum dengan menentukan panjang nilai true nilai terlama dalam array logis nilai boolean:
#
# 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)))
(Kami menemukan lokasi dari nilai - nilai salah , secara berurutan, dan menghitung perbedaannya: ini adalah panjang dari nilai-nilai non-palsu selanjutnya. Panjang terbesar akan dikembalikan.)
Keempat, kami menerapkan max.subsequence
untuk mendeteksi titik stasioner.
#
# 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
Itu semua adalah alat yang kita butuhkan.
Sebagai contoh, mari kita buat beberapa data menarik yang memiliki beberapa rumpun titik stasioner. Saya akan mengambil jalan acak di dekat Khatulistiwa.
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)
Array lon
dan lat
berisi koordinat, dalam derajat, n
titik secara berurutan. Menerapkan alat kami sangat mudah setelah pertama kali mengubahnya menjadi radian:
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))
Argumen p[max(1,i-5):min(n,i+5), ]
mengatakan untuk melihat sejauh 5 langkah waktu atau sejauh 5 langkah dari titik dasar p[i,]
. Termasuk k=5
mengatakan untuk mencari urutan 5 atau lebih dalam satu baris yang berada dalam 100 km dari titik dasar. (Nilai 100 km ditetapkan sebagai default is.stationary
tetapi Anda dapat menimpanya di sini.)
Outputnya p.stationary
adalah vektor logis yang menunjukkan stasioneritas: kita memiliki apa yang kita inginkan. Namun, untuk memeriksa prosedur, lebih baik memplot data dan hasil ini daripada memeriksa array nilai. Di plot berikut ini saya menunjukkan rute dan titik-titiknya. Setiap titik kesepuluh diberi label sehingga Anda dapat memperkirakan berapa banyak yang mungkin tumpang tindih dalam rumpun stasioner. Titik stasioner digambar ulang dengan warna merah solid untuk menyorotnya dan dikelilingi oleh buffer 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")
Untuk pendekatan (berbasis statistik) lainnya untuk menemukan titik diam dalam data yang dilacak, termasuk kode kerja, silakan kunjungi /mathematica/2711/clustering-of-space-time-data .