【问题标题】:find duplicates of values with range and sum them up with R查找具有范围的值的重复项并将它们与 R 相加
【发布时间】:2021-04-16 15:18:07
【问题描述】:

我确实有一个带有 ID、value1、value2 和 value3 的 df,如下所示:

ID    val1   val2   val3
01   100.5   7.31   1000
02   100.0   7.33    100
03   100.1  10.40    500
04   105.3   7.28    100
05   100.4   7.30    500

val1 和 val2 有范围,让我们假设以下范围:val1=0.5 和 val=0.1

ID     val1        val2       val3
01   100.5±0.5    7.31±0.1    1000
02   100.0±0.5    7.33±0.1    100
03   100.1±0.5   10.40±0.1    500
04   105.3±0.5    7.28±0.1    100
05   100.4 ±0.5   7.30±0.1    500

我想总结在 val1 范围和 val2 范围内重叠的行。只有当这两个条件为真时,才会发生 val3 的总和。最后,该表应仅包含 ID 较低的行和汇总的 val3。在我的示例中,这仅适用于第一行、第二行和最后一行。此外,所有不满足这两个标准的行都应该保留。这是我正在寻找的结果:

ID    val1   val2   val3
01   100.5   7.31   1600
03   100.1  10.40    500
04   105.3   7.28    100

到目前为止,我尝试使用 distinct 函数来实现这一点。但是,这并没有考虑到我的范围和总结。

sum_up <- distinct(df, val1, val2, .keep_all = TRUE)

【问题讨论】:

  • 请注意,您要求 both 条件为真,并且这只发生在 ID 01 中。但是,对于您的预期输出,如果只有第一个条件 (val1) 为真。您能否更新您的问题以澄清?
  • 99.8(第 3 行)不与 100 行(2)重叠吗?还是每行只重叠一次?
  • @JakobGepp:你说得对,99.8 与 row(2) 中的 100.0 重叠,但 val2 不重叠。我只想总结满足我的两个条件的行。
  • @BenNorris:我试图澄清我的问题,因此我添加了满足这两个条件的 row(5)。只有同时满足这两个条件的行才应合并,所有其他行应保留。
  • 是具有最低 ID 的行作为参考还是最近的行?假设 A 行与 B 行重叠,B 行与 D 行重叠,但 A 行与 D 行不重叠。您是否将这三个加在一起,因为 B 行是一个公共链接?还是 D 行开始了新的分组?

标签: r duplicates range


【解决方案1】:

编辑:我对此进行了更多思考,并提出了一个更优雅的解决方案,该解决方案应该适用于任意长的数据帧。该解决方案假定数据已经按照 ID 排序。

n <- nrow(data)
used <- logical(n)
groups <- numeric(n)
i <- 1

for (j in 1:n) {
  
  if (used[j]) next
  
  indices1 <- abs(val1[j] - val1) <= range1
  indices2 <- abs(val2[j] - val2) <= range2
  indices <- which(indices1 & indices2 & !used)
  
  groups[indices] <- i
  used[indices] <- TRUE
  i <- i + 1
}

这里有一个解决方案,但我不得不说我不喜欢以循环开头的部分。我想有一个更优雅的解决方案,但现在应该这样做。

在第一步中,您想知道哪些值与其范围重叠。因此,您将使用outer 函数和abs 来计算val1val2 的每个值之间的绝对差。这会产生两个逻辑矩阵,您可以在下一步中组合它们以获得包含TRUE 的矩阵,此时对应的列和行在val1val2 中都重叠。例如,第 1 行和第 2 列中的 TRUE 表示原始数据帧中的第 1 行和第 2 行重叠。

到目前为止,一切都很好,但现在您必须处理此信息才能获得整个重叠组。这是我开始摆弄的地方。如果您想按照 Marcus 的评论中的说明组合只需要链接的行,那么使用较少的辅助变量会更容易。

data <- data.frame(
  ID = 1:5,
  val1 = c(100.5, 100, 100.1, 105.3, 100.4),
  val2 = c(7.31, 7.33, 10.4, 7.28, 7.3),
  val3 = c(1000, 100, 500, 100, 500),
  groups = rep(0, 5)
)

range1 <- .5
range2 <- .1

overlap1 <- abs(outer(data[, "val1"], data[, "val1"], "-")) <= range1
overlap2 <- abs(outer(data[, "val2"], data[, "val2"], "-")) <= range2
overlap_both <- overlap1 & overlap2

result <- list()
ignore <- numeric(ncol(overlap_both))

for (i in 1:ncol(overlap_both)) {
  for(j in i:ncol(overlap_both)) {
    
    # Ignore this index if the element was already combined with a previous
    # element. Otherwise a row in the dataframe would fall in two different
    # groups.
    if (j %in% ignore) {
      next
    }
    
    if (any(overlap_both[, i] & overlap_both[, j])) {
      result[[length(result) + 1]] <- overlap_both[, i] | overlap_both[, j]
      ignore[[j]] <- j
    }
  }
}

result <- unique(result)

for (i in seq_along(result)) {
  data[which(result[[i]]), "groups"] <- i
}

现在您可以使用附加列进行分组。我希望您的数据框没有太多行。这会使解决方案相当低效。

【讨论】:

  • 感谢您的解决方案,我适应了我的代码。我最终得到了一个结果表,但是你已经担心我目前正在使用的数据集有几行(~3000)。这会导致渲染时间过长...... Markus 所描述的链接(总是以最低 ID 链接)是我的目标。
  • 我已经用不同的解决方案更新了答案,该解决方案更短,可能更具表现力,并且应该也适用于长数据帧。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-17
  • 2019-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-23
相关资源
最近更新 更多