首页 > 解决方案 > 不一致的 dist() foreach 结果

问题描述

我的数据大致采用以下格式,但非常大,但使用类和 uniqueId 变量按组分解。其中每个位置都是一对逐行 (x, y)。

df <- 
  data.frame(
    x = c(1, 2, 3, 4, 5, 6, 8, 9, 10), 
    y = c(1, 2, 3, 4, 5, 6, 8, 9, 10), 
    class = c(0, 0, 0, 0, 0, 1, 0, 1, 0), 
    uniqueId = c("1-2-3", "1-2-3", "1-2-3", "1-2-4", "1-2-4", "1-2-4", "1-3-2", "1-3-2", "1-3-2"),
    partialId = c("1.2", "1.2", "1.2", "1.2", "1.2", "1.2", "1.3", "1.3", 1.3") 
  )

我正在使用的函数应该通过数据框并计算到与当前行相同的 uniqueId 但不同类的另一个对象的最小距离。为此,我通过以下方式将数据分成块。

indexes <-
  df %>%
  select(partialId) %>%
  unique()

j <- 1

library(doParallel)

class_separation <- c()

cl <- makePSOCKcluster(24)

registerDoParallel(cl)


while(j <= nrow(indexes)) {

  test <- df %>% filter(partialId == indexes$partialId[j])
  n <- nrow(test)
  vec <- numeric(n)
  vec <- foreach(k = 1:n, .combine = 'c', .multicombine = F) %dopar% {
    c(
      min(
        apply(
          test[test$uniqueId == test$uniqueId[k] & test$class != test$class[k], c("x","y")],
          1,
          function(x) dist(rbind(c(test$x[k],test$y[k]), c(x[1], x[2])))
        )
      )
    )
  }
  class_separation <- c(class_separation, vec)
  j <- j + 1
}
endtime <- Sys.time()
stopwatch <- endtime - starttime
closeAllConnections()
registerDoSEQ()
gc()
df <- cbind(df, class_separation)

在处理单次播放或小批量时,此代码似乎运行良好。但是,在处理完整数据集时,我得到的结果显然不正确。我知道我计算这些距离的方式一定有缺陷,因为 dist() 函数本身或 %dopar% 出错的可能性很小。我已更改为 %do% 并且我的结果没有改变。

作为差异的示例,下图显示了执行完整数据运行时的 class_separation 列与我提供一个小示例时的对比。如您所见,结果大相径庭,但我不确定为什么。

结果图片

标签: rdistancedoparallel

解决方案


经过一天的思考,问题在于我如何将我的 df 发送到 dist()。

例如,如果我们打算通过

dist(rbind(c(1, 1), c(6, 6)))

dist(rbind(c(1, 1), c(9, 9)))

我们实际通过的是dist(rbind(c(1, 1), c(6, 6, 9, 9)))

这显然不是我想要的。我需要两个距离,然后选择它们之间的最小值或添加其他条件。我发现的方法是使用 rdist 包。

foreach(i = 1:nrow(df), .combine = 'c', .multicombine = F, .packages = c('tidyverse', 
  'rdist')) %dopar% {
    min(
      cdist(
        df[df$class != df$class[i] & df$uniqueId == df$uniqueId[i], ] %>% select(x, y), 
        df %>% select(x, y) %>% slice(i)
      )
    )
}

对于我们的测试数据,这将返回向量

Inf Inf Inf 2.828427 1.414214 1.414214 1.414214 1.414214 1.414214

这正是我所需要的。前三个条目的 uniqueId 没有 class == 1 选项应返回 Inf,第 4 行距第 6 行的距离是第 5 行的两倍,而它们都具有相同的 uniqueId,而第 9 行与第 8 行和第 10 行的距离相等。是否这个解决方案将足够快,我将进行测试。


推荐阅读