如何快速搜索随机游走在R中相交的情况?

时间:2014-12-03 18:38:02

标签: r performance geometry vectorization spatial

我有一个带时间戳的动物轨迹,并希望 快速 逐步走过这条轨迹,同时向后看一个给定的时间间隔,以确定动物何时与其相交自己的路。

有关如何对以下内容进行矢量化/并行化的建议将非常感谢!

输入数据是100000 * 6矩阵'traj',其中每行代表一条直线段(在轨迹内),由两个连续的时间戳坐标(X1,Y1,T1和X2)构成, Y2,T2):

X1 <- runif(11)
Y1 <- runif(11)
T1 <- c(0.0, 0.5, 2.0, 3.5, 7.0, 7.5, 8.0, 12.0, 12.5, 13.0, 13.5)
X2 <- X1[2:11]
Y2 <- Y1[2:11]
T2 <- T1[2:11]
traj <- cbind(X1 = X1[1:10], Y1 = Y1[1:10], T1 = T1[1:10], X2, Y2, T2)

我定义了一个函数,它将两个带时间戳的坐标转换为psp线段(spatstat),然后使用crossing.pspspatstat)测试是否该段与另一个线段相交。如果是,则该函数写入两个段之间的时间差:

INTERSECTS <- function(x)  
{ 
  PastSeg <- psp(x["X1"], x["Y1"], x["X2"], x["Y2"], window = windy,  marks = x["T1"] , check = F)  ## A single line-segment, in which marks give times associated with the 2 coordinate pairs that define the segment
  Crossing <- crossing.psp(NowSeg,PastSeg)       ## Test if the 2 segments (PastSeg & NowSeg) intersect      
  if (Crossing$n > 0)
  {                                              ## print(paste("Crossing at ",x["T1"] ))
  out <-  cbind( NowSeg$marks, x["T1"],        ## Get the time of the current trajectory segment
                  NowSeg$marks - x["T1"],       ## Time elapsed between current segment, and past segment
                  Crossing$x, Crossing$y)
  write.table(out, file = "Crossings.txt", append = T, row.names = F, col.names = F)
  plot(Crossing, add = T)
  }
}

最后,我逐步通过'traj'步骤。在每个步骤t,我考虑轨迹(t-*WINDOW*):(t-1)的前一部分,称为过去,在其中应用 INTERSECTS

Window <- 3
library(spatstat)
windy <- owin()  
plot(windy,main="")
points(traj[,"X1"],traj[,"Y1"],type="l")

for(FR in 3: nrow(traj))  
 { 
 ## Define the current trajectory segment 
 NowSeg  <- psp(traj[FR,"X1"], traj[FR,"Y1"],
             traj[FR,"X2"], traj[FR,"Y2"],  
             marks=traj[FR,"T1"], check=F, window=windy)    ## The mark gives the time-stamp

 ## Extract coords up to WINDOW seconds in the past
 Past <- traj [traj[,"T1"] < traj[FR-1,"T1"] & traj[,"T1"] > (traj[FR,"T1"]-WINDOW),]  

 ## apply function over rows of Past, to successively test if each trajectory segment in PAST intersects with NowSeg
 if (!is.null(dim(Past))) { apply(Past, 1, INTERSECTS) }   
 }##FR

我试图用 traj 的行替换这个循环,但没有成功,因为过去需要索引到一个行块,而不仅仅是单排。

1 个答案:

答案 0 :(得分:1)

这只是一个部分答案,在SO中不受欢迎,但是因为你问了几乎一天......

一种方法是使用包SpatialLines将您的轨迹转换为sp对象,然后使用包gIntersects(...)中的rgeos来计算轨迹的所有交点本身。这产生逻辑n×n矩阵M,其中M[i,j] = TRUE如果i th 段与j th 段相交。但有三个问题:

首先,每个线段都与自身相交,因此所有对角线元素都是TRUE;我们必须将它们设置为FALSE。其次,如果j>i我们检测到一个段和一个未来路径之间的交叉点,那么我们必须将矩阵的下三角形设置为FALSE。第三,根据定义,每个线段j与以下线段j+1相交,因为它们有一个共同点。因此,我们必须将元素[j,j+1]设置为FALSE

n <- 10
set.seed(123)
X1 <- runif(n+1); Y1 <- runif(n+1)
T1 <- c(0.0,0.5,2.0,3.5,7.0,7.5,8.0,12.0,12.5,13.0,13.5)
# T1 <- 1:(n+1)
traj <- cbind(X1=X1[1:n],Y1=Y1[1:n],T1=T1[1:n],
              X2=X1[-1],Y2=Y1[-1],T2=T1[-1])

library(sp)
library(rgeos)
sp.lines <- SpatialLines(sapply(1:nrow(traj),
                         function(i)Lines(Line(rbind(traj[i,1:2],traj[i,4:5])),
                                          ID=i)))
M <- gIntersects(sp.lines,sp.lines,byid=TRUE)
diag(M)         <- FALSE
M[lower.tri(M)] <- FALSE
M[cbind(1:(nrow(traj)-1),2:nrow(traj))] <- FALSE
apply(M,2,any)
#     1     2     3     4     5     6     7     8     9    10 
# FALSE FALSE  TRUE FALSE  TRUE FALSE  TRUE  TRUE FALSE  TRUE 
crossings <- traj[apply(M,2,any),"T1"]
crossings
# [1]  2  7  8 12 13

plot(sp.lines,col=1:n)
points(traj[,1],traj[,2],col="black",pch=c(as.character(1:9),"A"))

现在,对于M中的每一列,如果行的任何TRUE,则会与某个先前的段存在交集。这条线

apply(M,2,any)
#     1     2     3     4     5     6     7     8     9    10 
# FALSE FALSE  TRUE FALSE  TRUE FALSE  TRUE  TRUE FALSE  TRUE 
如果该段与现有路径具有交集,则

创建具有元素TRUE的逻辑向量。因此,在示例中,分段3,5,7,8和10具有与至少一个较早分段的交叉点。如果将其与上图进行比较,您将看到交叉发生的位置。

我们可以将此向量用作T1的{​​{1}}列的索引,以确定这些交叉点发生的时间。

traj

因此,在这个例子中,有T1 = 2,7,8,12,13的交叉点。

现在这是这种方法的问题:对于你的完整数据集,n~1e5,所以矩阵将有1e10(100亿)个元素。这太大了。即使不是这样,计算n = 1e4的交点需要大约3分钟,并且该过程按比例缩放为O(n 2 ),因此使用完整数据集需要大约5个小时。这比循环快得多,但仍然太慢。

如果片段不太长,例如如果动物在点之间没有走得太远,你可以对点进行采样。例如,

crossings <- traj[apply(M,2,any),"T1"]
crossings
# [1]  2  7  8 12 13

将每隔十行一次,

smpl <- traj[seq(1,nrow(traj),by=10),]

将随机抽取10%的积分。如果多个随机样本产生的交叉时间与均匀间隔样本没有明显差异,那么这就证明了抽样是合理的。