【问题标题】:Conditional sum in R – multiple columnsR中的条件和 - 多列
【发布时间】:2018-04-10 02:45:56
【问题描述】:

我试图弄清楚如何从非常大的表(例如,30'000 行和 50 列)中提取一些特定信息。

想象一下我有这个数据框:

S1 <- c(1,2,1,1,3,1)
S2 <- c(2,1,3,2,1,1)
S3 <- c(1,2,2,1,3,1)
S4 <- c(3,3,4,2,3,1)
S5 <- c(3,2,5,3,2,2)
count <- c(10,5,3,1,1,1)
df <- data.frame(count,S1,S2,S3,S4,S5)

例如,当 S1 和 S3 共享相同的值(哪个值无关紧要)但没有其他列具有相同的值时,我需要对“count”列求和。

在这个例子中,它应该返回值 11,因为我应该只考虑第 1 行和第 4 行中“count”列的值。

在第 2、5 和 6 行中,S1 和 S3 具有相似的值,但我不想考虑它们,因为还有其他列具有相同的值。最后,不考虑第 3 行,因为 S1 和 S3 具有不同的值。

我知道如何在 excel 中轻松做到这一点,但我想知道如何在 R 中做到这一点。我尝试了 dplyr 的一些命令,但失败了。

如果有人能提供帮助,我将非常感激。

【问题讨论】:

    标签: r dataframe sum dplyr conditional


    【解决方案1】:

    有点复杂,但它有效。仅使用 R 基础。来自this question的形式,以简单的方式比较多列。

    sum(df[df$S1==df$S3 & rowSums(sapply(df[,c(3,5,6)],`==`,e2=df$S1)) == 0,1])
    
    [1] 11
    

    最复杂的部分是如何检查多个列。在这种情况下,我们使用sapply 来比较列c(3,5,6)'==')与S1,(e2== 函数的第二个参数)。

    正如 ycw 所提到的,通过向量定义所有列可能有点复杂,因此这种形式允许您检查除我们不想要的列之外的所有列。

    sum(df[df$S1==df$S3 & rowSums(sapply(df[,!(colnames(df) %in% c("count", "S1", "S3"))],`==`,e2=df$S1)) == 0,1])
    

    对两个比较应用相同的过程并只定义相同值的向量:

    equals <- c("S1", "S3")
    not_equals <- !(colnames(df) %in% c("count", equals))
    
    sum(df[rowSums(sapply(df[,equals,drop=FALSE],`==`,e2=df[equals[1]])) == length(equals) &
               rowSums(sapply(df[,not_equals,drop=FALSE],`==`,e2=df[equals[1]])) == 0, 1])
    

    注意:使用drop=FALSE 仅选择一列数据框并避免“提升到向量”问题或以这种方式省略,

    sum(df[rowSums(sapply(df[equals],`==`,e2=df[equals[1]])) == length(equals) &
               rowSums(sapply(df[not_equals],`==`,e2=df[equals[1]])) == 0, 1])
    

    【讨论】:

    • 我要给你一个赞成票,因为这是一个不错的基础 R 解决方案。我唯一的评论是开发一种方法来指定列而不知道索引会很棒,例如c(3, 5, 6)。当有很多列要比较时,这部分可能会很乏味。
    • 感谢您的更新!我希望我能给两个更新投票,不幸的是我不能。这是一个非常好的解决方案。
    • 嗨,@Patricio。这实际上是一个非常有用的解决方案。非常感谢。在某些情况下,我想比较三列甚至更多列。例如,如果我比较 S1、S3 和 S5,我可以调整您的命令:sum(df[df$S1==df$S3 & df$S1==df$S5 & rowSums(sapply(df[,! (colnames(df) %in% c("count", "S1", "S3", "S5"))],==,e2=df$S1)) == 0,1]) 但是,我是想知道是否可以预先提供一个向量,并要求命令比较所有这些,而不是手动输入所有可能的比较。
    • 我注意到这些命令不适用于单列。例如,如果我尝试 equals = c("S1"),则命令返回 0。(我应该有 3)。另一方面,如果我尝试 equals = c("S2"),它们返回 3;但实际上它应该返回 19。你知道我可以在哪里进行一些修改以使命令既适用于多个对象也适用于单个对象?
    • @Luiz,确实是这样。问题是,默认情况下,当只选择数据帧的一列时,输出被提升为向量,而实际上它假设它仍然是数据帧。因此,如果是这种情况,您必须这样做:df[,equals,drop=FALSE]df[,not_equals,drop=FALSE] 感谢您的建议,编辑了我的答案..
    【解决方案2】:

    使用dplyr 的解决方案。有两个步骤。第一个filter 函数查找带有S1 == S3 的行。第二个filter_at函数检查除S1S3count以外的列都不等于S1,在第一个filter函数之后应该与S3相同。

    library(dplyr)
    
    df2 <- df %>%
      filter(S1 == S3) %>%
      filter_at(vars(-S1, -S3, -count), all_vars(. != S1))
    df2
      count S1 S2 S3 S4 S5
    1    10  1  2  1  3  3
    2     1  1  2  1  2  3
    

    那么总计数如下。

    sum(df2$count)
    [1] 11
    

    【讨论】:

    • @Luiz 我很高兴它有效。如果可能,请接受此处发布的答案之一。它不一定是我的,只要选择你认为足够或你最喜欢的一个。它将帮助未来的读者知道这个问题有可行的解决方案。
    【解决方案3】:

    使用dplyrrowwisefilter

    library(dplyr)
    df %>%
      rowwise() %>%
      filter(S1==S3 & !S1 %in% c(S2,S4,S5)) %>% 
      pull(count) %>% 
      sum() 
    # [1] 11
    

    【讨论】:

    • 谢谢,穆迪。这是一种优雅的解决方案。甚至对我在 R 方面的基本知识也很优雅;)
    • 确实是一个不错的dplyr 解决方案,所以我给了它一个赞成票。我的一条评论是,如果不指定要比较的列,例如c(S2,S4,S5),开发一个解决方案会很棒。
    • 这几乎会成为您的解决方案,不是吗:)。开玩笑,我想了想,但由于我不知道 OP 的真实数据集,我担心我所采用的规则(例如,像你一样采用除 S1 S3 之外的所有东西)不会反映真实情况,所以我去了这种替代方法比您的复杂,但更容易理解。
    • 确实,拥有更通用的管道会非常有趣。正如我所说,我有很大的数据集(有时有数百列),并且像在 c(S2,S4,S5) 中那样键入所有我不想考虑的列可能变得不切实际。幸运的是,在我的大多数情况下,我只想比较两到六列;所以使用 ycw 的解决方案更容易,因为我只需要输入我感兴趣的列。但无论如何,再次感谢。
    猜你喜欢
    • 1970-01-01
    • 2022-01-08
    • 2020-03-02
    • 2021-03-24
    • 2022-10-06
    • 2018-11-24
    • 1970-01-01
    • 2014-04-13
    • 1970-01-01
    相关资源
    最近更新 更多