【问题标题】:Select groups based on number of unique / distinct values根据唯一/不同值的数量选择组
【发布时间】:2024-01-11 09:21:01
【问题描述】:

我有一个如下所示的数据框

sample <- data.frame(ID = 1:9,
                     Group = c('AA','AA','AA','BB','BB','CC','CC','BB','CC'),
                     Value = c(1,1,1,2,2,2,3,2,3))

ID       Group    Value
1        AA       1
2        AA       1
3        AA       1
4        BB       2
5        BB       2
6        CC       2
7        CC       3
8        BB       2
9        CC       3

我想根据每个组中不同(唯一)值的数量来选择组。例如,选择组内所有值都相同的组(每组一个不同的值)。如果您查看组 CC,它具有多个不同的值(2 和 3),因此应该被删除。应保留仅具有一个不同值的其他组。期望的输出:

ID       Group    Value
1        AA       1
2        AA       1
3        AA       1
4        BB       2
5        BB       2
8        BB       2

您能告诉我解决问题的简单快速的 R 代码吗?

【问题讨论】:

  • 你为解决这个问题做了什么?
  • 我使用了我检查的答案。
  • 我翻译了这篇文章以在ctrlaltdel上与韩国开发者分享

标签: r dataframe unique


【解决方案1】:

这是使用 dplyr 的解决方案:

library(dplyr)

sample <- data.frame(
  ID = 1:9,  
  Group= c('AA', 'AA', 'AA', 'BB', 'BB', 'CC', 'CC', 'BB', 'CC'),  
  Value = c(1, 1, 1, 2, 2, 2, 3, 2, 3)
)

sample %>%
  group_by(Group) %>%
  filter(n_distinct(Value) == 1)

我们将数据按Group分组,然后只选择Value的不同值个数为1的组。

【讨论】:

    【解决方案2】:

    data.table版本:

    library(data.table)
    sample <- as.data.table(sample)
    sample[ , if(uniqueN(Value) == 1) .SD, by = Group]
    
    #   Group ID Value
    #1:    AA  1     1
    #2:    AA  2     1
    #3:    AA  3     1
    #4:    BB  4     2
    #5:    BB  5     2
    #6:    BB  8     2
    

    如果数据是数字,则使用 ave 的另一种方法是检查方差是否为 0:

    sample[with(sample, ave(Value, Group, FUN=var ))==0,]
    

    在大数据上可能更快的替代解决方案是:

    setkey(sample, Group, Value)
    ans <- sample[unique(sample)[, .N, by=Group][N==1, Group]]
    

    关键是,当有更多组时,为每个组计算 unique 值可能会很耗时。相反,我们可以在data.table 上设置键,然后通过键获取unique 值(这非常快),然后计算每个组的总值。然后我们只需要它为 1 的那些。然后我们可以执行join(这又一次非常快)。这是大数据的基准:

    require(data.table)
    set.seed(1L)
    sample <- data.table(ID=1:1e7, 
            Group = sample(rep(paste0("id", 1:1e5), each=100)), 
            Value = sample(2, 1e7, replace=TRUE, prob=c(0.9, 0.1)))
    
    system.time (
        ans1 <- sample[,if(length(unique(Value))==1) .SD ,by=Group]
    )
    # minimum of three runs
    #   user  system elapsed 
    # 14.328   0.066  14.382 
    
    system.time ({
        setkey(sample, Group, Value)
        ans2 <- sample[unique(sample)[, .N, by=Group][N==1, Group]]
    })
    # minimum of three runs
    #   user  system elapsed 
    #  5.661   0.219   5.877 
    
    setkey(ans1, Group, ID)
    setkey(ans2, Group, ID)
    identical(ans1, ans2) # [1] TRUE
    

    【讨论】:

    • @RicardoSaporta - 我记得与 mnel here 讨论过,使用 if 会更快或更有效。我不记得为什么了。
    • 我没有想到这一点,这完全有道理!使用if 将调用保存到.SD
    【解决方案3】:

    您可以使用ave 多种不同方式为sample 创建选择器。

    sample[ ave( sample$Value, sample$Group, FUN = function(x) length(unique(x)) ) == 1,]
    

    sample[ ave( sample$Value, sample$Group, FUN = function(x) sum(x - x[1]) ) == 0,]
    

    sample[ ave( sample$Value, sample$Group, FUN = function(x) diff(range(x)) ) == 0,]
    

    【讨论】:

      【解决方案4】:

      这是一种方法

      > ind <- aggregate(Value~Group, FUN=function(x) length(unique(x))==1, data=sample)[,2]
      > sample[sample[,"Group"] %in% levels(sample[,"Group"])[ind], ]
        ID Group Value
      1  1    AA     1
      2  2    AA     1
      3  3    AA     1
      4  4    BB     2
      5  5    BB     2
      8  8    BB     2
      

      【讨论】:

        最近更新 更多