【问题标题】:R - Set values by group based on a condition in a dataframeR - 根据数据框中的条件按组设置值
【发布时间】:2021-12-28 21:11:39
【问题描述】:

我有以下数据集。

group value row_name overlaps
group_a 4 1 2
group_a 5 2 3, 5
group_a 48 3 4, 5
group_a 54 4 5
group_a 12 5
group_b 12 6 7
group_b 1 7

重叠列指示哪些行具有一定的“重叠”。

我希望,仅对于值大于 10 的行,将相应“重叠”列中指示的所有行的值列中的数据替换为零。

预期输出:

group value row_name overlaps
group_a 4 1 2
group_a 5 2 3, 5
group_a 48 3 4, 5
group_a 0 4 5
group_a 0 5
group_b 12 6 7
group_b 0 7

可重现的例子:

data <- data.frame(group = c("group_a", "group_a", "group_a", "group_a",
                             "group_a", "group_b", "group_b"),
                   value = c(4, 5 , 48, 54, 12, 12, 1),
                   row_name = c("1", "2", "3", "4", "5", "6", "7"),
                   overlaps = c("2", "3, 5", "4, 5", "5", "", "7", ""))

我不知道这是否是一个非常复杂的问题,但我被困了几个小时,我不知道如何解决它。

有人对我如何使用 dplyr 或 data.table 按组解决此问题有任何建议吗?

【问题讨论】:

  • 我不遵循逻辑。为什么第三行也不是 0?它大于 10 并且位于重叠列表之一中。
  • @thelatemail 第三行在第二行的重叠列表中。第 2 行的值不大于 10。因此,第 3 行的值不会变为 0。与第 4 行和第 5 行不同,它们在值 > 10 的第 3 行的重叠列表中。

标签: r datatable data-wrangling


【解决方案1】:

另一种解决方案,基于tidyverse

library(tidyverse)

data <- data.frame(group = c("group_a", "group_a", "group_a", "group_a",
                             "group_a", "group_b", "group_b"),
                   value = c(4, 5 , 48, 54, 12, 12, 1),
                   row_name = c("1", "2", "3", "4", "5", "6", "7"),
                   overlaps = c("2", "3, 5", "4, 5", "5", "", "7", ""))

data %>% 
  separate(
    overlaps, into=c("o1", "o2"), sep=", ", fill="right", remove=F) %>% 
  mutate(across(o1:o2, ~ ifelse(value > 10, get(cur_column()), 0)),
         value = ifelse(row_number() %in% c_across(o1:o2), 0, value)) %>% 
  select(-o1, -o2)

#>     group value row_name overlaps
#> 1 group_a     4        1        2
#> 2 group_a     5        2     3, 5
#> 3 group_a    48        3     4, 5
#> 4 group_a     0        4        5
#> 5 group_a     0        5         
#> 6 group_b    12        6        7
#> 7 group_b     0        7

【讨论】:

    【解决方案2】:

    strsplit overlaps 列的子集,仅是 data$value &gt; 10 的子集,然后使用 row_names 的不同集合用 0 覆盖原始数据:

    gr10 <- data$value > 10
    sel <- Map(paste, data$group[gr10], strsplit(data$overlaps, ",\\s+")[gr10], sep="|")
    sel <- Reduce(union, sel)
    sel
    #[1] "group_a|4" "group_a|5" "group_a|"  "group_b|7"
    data$value[do.call(paste, c(data[c("group","row_name")], sep="|")) %in% sel] <- 0
    data
    #    group value row_name overlaps
    #1 group_a     4        1        2
    #2 group_a     5        2     3, 5
    #3 group_a    48        3     4, 5
    #4 group_a     0        4        5
    #5 group_a     0        5         
    #6 group_b    12        6        7
    #7 group_b     0        7         
    

    如果row_names 在整个数据集中是唯一的,您可以使用更简单的逻辑:

    sel <- Reduce(union, strsplit(data$overlaps, ",\\s+")[data$value > 10])
    sel
    #[1] "4" "5" "7"
    data$value[data$row_name %in% sel] <- 0
    

    奖励 data.table 解决方案:

    library(data.table)
    setDT(data)
    
    data[
      data[value > 10, .(row_name=unlist(strsplit(overlaps, ",\\s+"))), by=group],
      on=.(group, row_name),
      value := 0
    ]
    

    【讨论】:

    • 您知道是否有任何方法可以按组应用此逻辑,而不是将“数据框作为一个整体”考虑?
    • 当然可以。如果您可以展示 2 个组的外观示例,我可以更新答案。
    • @Quizicall - 查看更新
    • 请注意,如果行名是唯一的,即使跨组,原始逻辑也会起作用。然而,我不想假设这一点。
    猜你喜欢
    • 2021-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-02
    • 1970-01-01
    • 2021-01-29
    • 1970-01-01
    • 2022-10-08
    相关资源
    最近更新 更多