【问题标题】:Returning observations that only occur once in a group [duplicate]返回仅在组中出现一次的观察结果[重复]
【发布时间】:2016-07-08 18:56:59
【问题描述】:

我正在尝试按因子变量对 data.frame 进行分组,然后返回与 在每个组中恰好出现一次的观察相对应的 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 行的数据集。

【问题讨论】:

标签: r dataframe grouping


【解决方案1】:

在base 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

【讨论】:

  • 请不要对这个答案投票。这显然是一种非常幼稚的方法,当组的数量很大时无法很好地扩展。这个is known(尽管该文章尚未更新以显示“dplyr”是否有所改进)。在这个问题上只有一个答案值得投票。请适度并相应地投票。结果,每个人都会有一个更愉快的一天。谢谢!
  • 这是一个冗长的评论。但是,我知道ave 解决方案非常快。起初,我没有尝试使用ave,因为我认为dplyr 会很快并且只包含它。所以,你应该得到选票。不过,我认为我必须处理这种偏见。
  • 我请求你把它从 communitywiki 放回你的名字,因为我的评论是基于对 dplyr 解决方案的误解
  • @akrun,CW 是一个不可逆转的决定。
  • @akrun,至少在“data.table”的开发版本中,你可以这样做:as.data.table(x1)[, .N, by = .(X1, X2)][N == 1]。不确定这是否适用于 CRAN 版本。另外,请取消删除您的答案。它信息丰富。我共享的ave 的修改版本可能比我共享的“dplyr”快,但不如您共享的“dplyr”或其他替代方案(base 或其他)快。
【解决方案2】:

我们可以使用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)

样本数据

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 方法

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 方法

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 方法

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”方法胜出,但如果您由于某种原因无法使用该软件包,使用 base R 中的duplicated 也可以很好地执行。

【讨论】:

  • 我现在对 3 票无能为力,但我的答案是 CW,所以我没有从中获得任何声誉。当我可以(我相信 2 天后)取消我迄今为止收到的选票时,我会在问题中添加 50 分的赏金。而且,祝某人美好的一天有什么问题?
  • @AHandcartAndMohair 我错了。对不起。我之前没有测试过你的 ave 解决方案,因为我认为 dplyr 是优越的,并且基于此进行了比较。可能有更多的团体和东西,dplyr 会超越。但是,在基准示例中,表现更好的是您的 ave 解决方案。我们无法改变人们的投票模式。我能做的就是收回我今天给一个我认为投票公平的人的两票。
  • 我认为 avesystem.time 存在问题,因为它对我来说远没有那么快,与 Paul 运行的基准相比,“dplyr”应该改善了这种行为.
  • @AHandcartAndMohair 我不知道这个问题。我运行了几次,我得到了相同的时间
  • 你只按一个变量分组,所以它会很快。当多个变量的相互作用开始发挥作用时,它会显着放缓。它甚至可能使您的系统停止运行。
【解决方案3】:

有基础,类似

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 拆分为由X1 级别拆分的data.frames 列表,然后将lapply 子集拆分为仅出现一次X2 值的行,列表通过tabledo.call(rbind 然后将生成的 data.frames 重新组合成一个。

【讨论】:

  • 哦,我完全看错了……正在编辑……
  • 或类似y &lt;- data.frame(table(x)); y[y$Freq == 1, -3]
猜你喜欢
  • 1970-01-01
  • 2021-06-27
  • 1970-01-01
  • 1970-01-01
  • 2021-10-31
  • 2012-01-02
  • 1970-01-01
  • 2017-12-23
  • 1970-01-01
相关资源
最近更新 更多