让我们将其分解为简单的部分。这样,所有工作只需六行容易测试的代码即可完成。
首先,您需要计算距离。因为数据在地理坐标中,所以这里有一个函数来计算球形基准上的距离(使用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)))
}
如果需要,可将其替换为您喜欢的实现(例如,使用椭圆形基准的实现)。
接下来,我们将需要计算每个“基点”(被检查是否具有同质性)与其时间邻域之间的距离。这仅仅是申请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
说要在距基准点100公里以内的位置连续查找5个或更多的序列。(将100 km的值设置为默认值,is.stationary
但您可以在此处覆盖它。)
输出p.stationary
是指示平稳性的逻辑向量:我们有自己的目标。但是,要检查该过程,最好是绘制数据和这些结果,而不是检查值的数组。在下图中,我显示了路线和点。每十分之一点都带有标签,以便您可以估计固定块中可能有多少重叠。固定点以红色重画以突出显示并被其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")
有关在跟踪数据(包括工作代码)中查找静止点的其他(基于统计的)方法,请访问/mathematica/2711/clustering-of-space-time-data。