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

问题描述 投票:1回答:3

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

我试图通过因子变量对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
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非常快且紧凑。


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

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

或者来自tallydplyr

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

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

Benchmarks

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))

基地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”方法取得了成功,但如果由于某种原因你无法使用该软件包,那么使用基础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重新组合成一个。

© www.soinside.com 2019 - 2024. All rights reserved.