首页 文章

返回仅在组中发生一次的观察[重复]

提问于
浏览
1

这个问题在这里已有答案:

我试图通过因子变量对data.frame进行分组,然后返回与 occur exactly once in each group 的观察对应的data.frame行 . 例如,请考虑以下数据:

x = matrix(c(1,1,2,2,2,3,4,4,5,4), nrow = 5, ncol = 2, byrow = F)
x = data.frame(x)
x

#   X1 X2
# 1  1  3
# 2  1  4
# 3  2  4
# 4  2  5
# 5  2  4

我想按照第1列中的值对数据进行分组,然后返回第2列中的值仅在组中出现一次的行 . 这里,函数将返回第一行,第二行和第四行 .

期望的输出

#   X1 X2
# 1  1  3
# 2  1  4
# 4  2  5

我希望将其应用于行> 1mm的数据集 .

3 回答

  • 4

    在基数R中,您可以尝试 ave

    x[with(x, ave(X2, X1, X2, FUN = length)) == 1, ]
    #   X1 X2
    # 1  1  3
    # 2  1  4
    # 4  2  5
    

    因为 ave 在有多个组和多个分组变量时非常差,所以您可能需要先创建一个新组:

    x[with(x, ave(X2, sprintf("%s__%s", X1, X2), FUN = length)) == 1, ]
    

    根据数据的性质,速度会有很大差异 .

    你也可以尝试:

    library(dplyr)
    x %>%
      group_by(X1, X2) %>%
      filter(n() == 1)
    # Source: local data frame [3 x 2]
    # Groups: X1, X2 [3]
    # 
    #      X1    X2
    #   (dbl) (dbl)
    # 1     1     3
    # 2     1     4
    # 3     2     5
    
  • 3

    我们可以使用 data.table . 我们将'data.frame'转换为'data.table'( setDT(x) ,按第一列分组,即"X1", if ,只有一个观察,返回行,否则删除所有重复项并仅返回唯一行 .

    library(data.table)
    setDT(x)[, if(.N==1) .SD else 
       .SD[!(duplicated(X2)|duplicated(X2, fromLast=TRUE))], X1]
    #   X1 X2
    #1:  1  3
    #2:  1  4
    #3:  2  5
    

    如果我们同时使用“X1”和“X2”作为分组变量

    setDT(x)[x[, .I[.N==1], .(X1, X2)]$V1]
    #   X1 X2
    #1:  1  3
    #2:  1  4
    #3:  2  5
    

    注意:Data.table非常快且紧凑 .


    或者不使用任何分组选项,我们可以使用 base R

    x[!(duplicated(x)|duplicated(x, fromLast=TRUE)),]
    #  X1 X2
    #1  1  3
    #2  1  4
    #4  2  5
    

    tally 来自 dplyr

    library(dplyr)
    x %>%
      group_by_(.dots= names(x)) %>%
      tally() %>%
      filter(n==1) %>%
      select(-n)
    

    请注意,这应该比其他dplyr解决方案更快 .

    基准

    library(data.table)
    library(dplyr)
    

    Sample data

    set.seed(24)
    x1 <- data.frame(X1 = sample(1:5000, 1e6, replace=TRUE), 
                     X2 = sample(1:10000, 1e6, replace=TRUE))
    x2 <- copy(as.data.table(x1))
    

    Base R approaches

    system.time(x1[with(x1, ave(X2, sprintf("%s__%s", X1, X2), FUN = length)) == 1, ])
    #    user  system elapsed 
    #  20.245   0.002  20.280 
    
    system.time(x1[!(duplicated(x1)|duplicated(x1, fromLast=TRUE)), ])
    #    user  system elapsed 
    #   1.994   0.000   1.998
    

    dplyr approaches

    system.time(x1 %>% group_by(X1, X2) %>% filter(n() == 1))
    #    user  system elapsed 
    #  33.400   0.006  33.467 
    
    system.time(x1 %>% group_by_(.dots= names(x2)) %>% tally() %>% filter(n==1) %>% select(-n))
    #    user  system elapsed 
    #   2.331   0.000   2.333
    

    data.table approaches

    system.time(x2[x2[, .I[.N==1], list(X1, X2)]$V1])
    #    user  system elapsed 
    #   1.128   0.001   1.131 
    
    system.time(x2[, .N, by = list(X1, X2)][N == 1][, N := NULL][])
    #    user  system elapsed 
    #   0.320   0.000   0.323
    

    摘要:"data.table"方法胜出,但如果由于某种原因无法使用该程序包,则使用基础R中的 duplicated 也可以很好地执行 .

  • 1

    有了基础,就像

    do.call(rbind, lapply(split(x, x$X1), 
                          function(y){y[table(y$X2) == 1,]}))
    #     X1 X2
    # 1.1  1  3
    # 1.2  1  4
    # 2    2  5
    

    其中 splitx 拆分为data.frames列表,按 X1 的级别拆分,然后 lapply 子集划分为只有一次出现 X2 值的行,由 table 列表 . do.call(rbind 然后将生成的data.frames重新组合回一个 .

相关问题