【问题标题】:How to conditionally summarize on other entries in the group - R如何有条件地总结组中的其他条目 - R
【发布时间】:2019-05-11 14:40:59
【问题描述】:

在我的数据集中,我有不同项目超时的笛卡尔坐标,由 EventID、event_type、ID 号、x 位置、y 位置、身份类型、广泛类别和框架 ID 号标识。我需要做的是为每个 EventID、event_type 对和框架 ID 号遍历每个 ID 号并计算具有不同广泛类别的其他 ID 号与当前行的最小距离。我想避免为此使用 for 循环,因为数据集有几百万行。

我尝试将其公式化为 group_by 并使用 dplyr 汇总调用,但无法完全理解如何在当前行 x 上调用函数,y 对所有其他 x 和 ys 调用,然后选择条件最低限度。

two_dim_euclid = function(x1, x2, y1, y2){
  a <- sqrt((x1 - x2)^2 + (y1 - y2)^2)
  return(a)
}


# Example Data
df <- data.frame(stringsAsFactors = FALSE,
                 EventID = c(1003, 1003, 1003, 1003),
                 event_type = c(893, 893, 893, 893),
                 ID_number = c(80427, 2346, 24954, 27765),
                 x = c(86.07, 72.4, 43.08, 80.13),
                 y = c(35.58, 26.43, 34.8, 34.79),
                 identity_type = c("A", "C", "B", "B"),
                 broad_category = c("set1", "set1", "set2", "set2"),
                 frame_id = c(1, 1, 1, 1))
df
#  EventID event_type ID_number x     y     identity_type broad_category frame_id
#1 1003    893        80427     86.07 35.58 A             set1           1
#2 1003    893        2346      72.40 26.43 C             set1           1
#3 1003    893        24954     43.08 34.80 B             set2           1
#4 1003    893        27765     80.13 34.79 B             set2           1

对于第 1 行,预期结果将返回 5.992303,它会查找所有不属于 set1 且具有相同 EventID、event_type 和 frame_id 的条目,然后返回给定这些参数的最小欧几里得距离。

另外,我想对每个身份类型为 A 的条目执行此操作。但是,identity_type 和 broad_category 并不总是捆绑在一起。 A 可以属于 set1 或 set2。

【问题讨论】:

  • 为什么它只是 5.992303(仅比较第 4 行)而不是 5.992303 + 42.997075(第 4 行和第 3 行)?请重新定义您的比较标准,这不是很容易遵循。
  • 因为我不想要所有可能的距离,只想要欧几里得距离的最小值。
  • 将等式中的总和更改为一分钟
  • 我做到了,就像一个魅力。谢谢!

标签: r dplyr data.table rcpp


【解决方案1】:

这是一个依赖dist() 的基本方法。

res <- as.matrix(dist(cbind(df$x, df$y)))
res[res == 0] <- Inf

apply(res, 1, min)

        1         2         3         4 
 5.992303 11.386066 30.491299  5.992303 

# or potentially  more performant
res[cbind(seq_len(nrow(res)), max.col(-res))]

[1]  5.992303 11.386066 30.491299  5.992303

的一种潜在方法是进行笛卡尔连接,但它需要大量内存并且可能会更慢:

library(data.table)
dt <- as.data.table(df)
dt[, ID := .I]

CJ.dt = function(X,Y) {
  stopifnot(is.data.table(X),is.data.table(Y))
  k = NULL
  X = X[, c(k=1, .SD)]
  setkey(X, k)
  Y = Y[, c(k=1, .SD)]
  setkey(Y, NULL)
  X[Y, allow.cartesian=TRUE][, k := NULL][]
}
CJ.dt(dt, dt)[ID != i.ID, min(sqrt((x - i.x)^2 + (y-i.y)^2)), by = i.ID]

   i.ID        V1
1:    1  5.992303
2:    2 11.386066
3:    3 30.491299
4:    4  5.992303

对于 data.table 笛卡尔连接,请参见此处: R: data.table cross-join not working

【讨论】:

    【解决方案2】:

    虽然我不确定您的标准,但如果您想迭代,似乎必须以某种方式使用 for 循环。我相信其他人可以为您提供非常快速的 Rcpp 解决方案。同时,这里有一种可能的方式与基础 R。

    # In the future, please provide the code to create your example data
    dat <- structure(list(EventID = c(1003L, 1003L, 1003L, 1003L), 
                      event_type = c(893L, 893L, 893L, 893L), 
                      ID_number = c(80427L, 2346L, 24954L, 27765L), 
                      x = c(86.07, 72.4, 43.08, 80.13), 
                      y = c(35.58, 26.43, 34.8, 34.79), 
                      identity_type = structure(c(1L, 3L, 2L, 2L), 
                                                .Label = c("A", "B", "C"), 
                                                class = "factor"), 
                      broad_category = structure(c(1L,  1L, 2L, 2L), 
                                                 .Label = c("set1", "set2"), 
                                                 class = "factor"), 
                      frame_id = c(1L,  1L, 1L, 1L)), 
                 .Names = c("EventID", "event_type", "ID_number","x", "y", 
                            "identity_type", "broad_category", "frame_id"), 
                 class = "data.frame", row.names = c("1", "2", "3", "4"))
    
    # Define your criteria here
    dat$uniqueID <- paste0(dat$EventID, dat$event_type, dat$frame_id, dat$broad_category)
    # made your function have two 2 dim vectors instead since that's simpler for passing in
    two_dim_euclid = function(a, b) return(sqrt((a[1] - b[1])^2 + (a[2] - b[2])^2))
    
    n <- nrow(dat)
    vec <- numeric(n)
    for(i in 1:n){
      vec[i] = sum(apply(dat[dat$uniqueID != dat$uniqueID[i], c("x","y")], 1, 
                         function(r) two_dim_euclid(dat[i,c("x","y")], r)), na.rm = T)
      if(i%%10000 == 0) cat(i,"completed...\n") # Progress check since >1mil rows
    }
    dat$result <- vec
    

    【讨论】:

    • 非常感谢您,我采纳了您的解决方案并将其修改为以下内容。 'foreach(i = 1:n) %dopar% etc' 它会在大约 3 秒内完成每个唯一的集合。以这样的速度,我应该在 12 小时内拥有我需要的一切,多亏了你!看起来我会等待更长的时间
    • 肖恩,等待的时间太长了!我可以建议创建第二个仅包含 uniqueIDxy 列的数据框吗?这应该会略微提高速度。我很高兴你有一些并行的东西工作。如果您对此答案满意,请将其标记为满意以结束问题。我建议您添加Rcpp 标签并让它打开一段时间,甚至是data.table 答案。
    • 这是公平的,会做的。在放手之前我确实修剪了不需要的列,认为任何边际改进都是好的。只留下我需要在后端匹配的东西。不过我会添加 Rcpp 标签。
    猜你喜欢
    • 1970-01-01
    • 2018-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-22
    • 2021-04-03
    • 2021-02-27
    相关资源
    最近更新 更多