Розбиймо це на прості шматки. Тим самим вся робота виконується лише за півдесятка рядків легко перевіреного коду.
По-перше, вам потрібно буде обчислити відстані. Оскільки дані знаходяться в географічних координатах, тут є функція для обчислення відстаней на сферичній даті (за формулою Гаверсіна):
#
# 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)))
}
За бажанням замініть це улюбленою реалізацією (наприклад, такою, яка використовує еліпсоїдальну дату).
Далі нам потрібно буде обчислити відстані між кожною "базовою точкою" (перевіряється на стаціонарність) та її тимчасовим сусідством. Це просто питання звернення dist
до мікрорайону:
#
# 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, ...))
По-третє - це ключова ідея - стаціонарні точки знаходять, виявляючи мікрорайони з 11 пунктів, що мають принаймні п’ять поспіль, відстань яких досить мала. Давайте реалізуємо це трохи більш загально, визначивши довжину найдовшого підпорядкування справжніх значень у логічному масиві булевих значень:
#
# 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)))
(Ми знаходимо розташування помилкових значень в порядку і обчислюємо їх відмінності: це довжини підрядів неправдивих значень. Повертається найбільша така довжина.)
По-четверте, ми застосовуємо max.subsequence
для виявлення стаціонарних точок.
#
# 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
Це всі необхідні нам інструменти.
Як приклад, давайте створимо кілька цікавих даних, що мають кілька скупчень нерухомих точок. Я пройдусь випадковою прогулянкою біля Екватора.
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)
Масиви lon
і lat
містять координати в градусах, n
точки в послідовності. Застосування наших інструментів просте після першого перетворення в радіани:
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))
Аргумент p[max(1,i-5):min(n,i+5), ]
говорить, що слід дивитися на відстань на 5 кроків часу або вперед, як на 5 часових кроків від базової точки p[i,]
. В тому числі k=5
говорить, щоб шукати послідовність з 5 або більше підряд, які знаходяться в межах 100 км від базової точки. (Значенням 100 км було встановлено за замовчуванням, is.stationary
але ви можете його замінити тут.)
Вихід p.stationary
є логічним вектором, що вказує на стаціонарність: ми маємо те, за що прийшли. Однак для перевірки процедури краще побудувати дані та ці результати, а не перевіряти масиви значень. На наступному сюжеті я показую маршрут та пункти. Кожна десята точка позначена міткою, так що ви можете оцінити, скільки може перекриватися в межах нерухомих скупчень. Стаціонарні точки перемальовуються суцільним червоним кольором, щоб виділити їх та оточити їхніми 100 км буферами.
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")
Для інших (на статистиці) підходів до пошуку стаціонарних точок у відстежуваних даних, включаючи робочий код, відвідайте /mathematica/2711/clustering-of-space-time-data .