Несогласованные результаты 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") 
  )

Используемая мной функция должна проходить через фрейм данных и вычислять наименьшее расстояние до другого объекта в пределах того же уникального идентификатора, но другого класса, что и текущая строка. Для этого я разбил свои данные на куски следующим образом.

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 при выполнении полного прогона данных и при подаче небольшого примера. Как видите, результаты сильно отличаются, но я не уверен, почему.

изображение результатов


person Sean Clement    schedule 27.02.2020    source источник


Ответы (1)


После дня размышлений об этом проблема заключается в том, как я отправлял свой 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)
      )
    )
}

Для наших тестовых данных это возвращает вектор

Инф Инф Инф 2,828427 1,414214 1,414214 1,414214 1,414214 1,414214

Это именно то, что мне было нужно. Первые три записи, не имеющие параметров class == 1 для их uniqueId, должны возвращать Inf, строка 4 в два раза дальше от строки 6, чем строка 5, при этом все они имеют одинаковый uniqueId, а строка 9 находится на одинаковом расстоянии от строк 8 и 10. это решение будет достаточно быстрым, я проверю.

person Sean Clement    schedule 28.02.2020