如何在R中删除唯一的条目并保留重复项

23
ID     Cat1  Cat2    Cat3   Cat4
A0001   358 11.25   37428   0
A0001   279 14.6875 38605   0
A0013   367 5.125   40152   1
A0014   337 16.3125 38624   0
A0020   367 8.875   37797   0
A0020   339 9.625   39324   0

我需要帮助学习如何删除文件中的唯一行,同时保留重复行或三重行。例如,输出应该像下面这样:

ID     Cat1  Cat2    Cat3   Cat4
A0001   358 11.25   37428   0
A0001   279 14.6875 38605   0
A0020   367 8.875   37797   0
A0020   339 9.625   39324   0

如果您能给我建议,告诉我如何处理这个问题,非常感谢。

谢谢大家的建议。我想计算不同类别(即Cat2、Cat3)在重复测量(按独特ID)之间的价值差异。欢迎任何建议。

3个回答

22
在R语言中,使用duplicated函数是另一个选项。
dx[dx$ID %in% dx$ID[duplicated(dx$ID)],]

#      ID Cat1    Cat2  Cat3 Cat4
# 1 A0001  358 11.2500 37428    0
# 2 A0001  279 14.6875 38605    0
# 5 A0020  367  8.8750 37797    0
# 6 A0020  339  9.6250 39324    0

使用duplicated的data.table

使用 duplicatedfromLast 版本,你可以得到:

library(data.table)
setkey(setDT(dx),ID) # or with data.table 1.9.5+: setDT(dx,key="ID")
dx[duplicated(dx) |duplicated(dx,fromLast=T)]

#       ID Cat1    Cat2  Cat3 Cat4
# 1: A0001  358 11.2500 37428    0
# 2: A0001  279 14.6875 38605    0
# 3: A0020  367  8.8750 37797    0
# 4: A0020  339  9.6250 39324    0

这也适用于基本的R语言,但我更喜欢使用data.table来提供语法糖。

1
这个 base 解决方案比其他答案中的解决方案快得多(没有尝试任何 data.table 解决方案)。比 ave 快6倍,比 dplyr 快14倍。 - hmhensen

16

总体评论。

  • ave方法是唯一保留数据初始行顺序的方法。
  • by方法可能非常缓慢。我怀疑data.table和dplyr在选择组方面与avetapply的速度差不多(目前还没有更快的方法)。欢迎提供基准测试来证明我错了!

基础R(感谢@thelatemail提供前两种方法)

1)每个行被分配了其df$ ID组的长度,并根据长度向量进行过滤。

df[ ave(1:nrow(df), df$ID, FUN=length) > 1 , ]

2) 或者,我们按照 df$ID 分割行名或数字,选择要保留的组的行。tapply 返回一组行的列表,因此我们必须将它们 unlist 成单个行向量。

df[ unlist(tapply(1:nrow(df), df$ID, function(x) if (length(x) > 1) x)) , ]

以下方法不太理想,但更接近于data.table和dplyr的操作:

3)数据按照df $ ID进行拆分,对于每个子集数据SD(如果它具有超过一行的数据),将其保留。由于by函数返回一个列表,因此我们必须使用rbind将它们重新组合。

do.call( rbind, c(list(make.row.names = FALSE),
    by(df, df$ID, FUN=function(SD) if (nrow(SD) > 1) SD )))

data.table中的 .N 代表在 by=ID 分组内的行数,而 .SD 则是数据的子集。

library(data.table)
setDT(df)[, if (.N>1) .SD, by=ID]

#       ID Cat1    Cat2  Cat3 Cat4
# 1: A0001  358 11.2500 37428    0
# 2: A0001  279 14.6875 38605    0
# 3: A0020  367  8.8750 37797    0
# 4: A0020  339  9.6250 39324    0

dplyrn() 函数在 group_by(ID) 分组内相当于 nrow 函数。


library(dplyr)
df %>% group_by(ID) %>% filter( n() > 1 )

# Source: local data frame [4 x 5]
# Groups: ID
# 
#      ID Cat1    Cat2  Cat3 Cat4
# 1 A0001  358 11.2500 37428    0
# 2 A0001  279 14.6875 38605    0
# 3 A0020  367  8.8750 37797    0
# 4 A0020  339  9.6250 39324    0

1
避免所有的拆分和行绑定,使用 ave - dat[with(dat, ave(seq_along(ID),ID,FUN=length) )>1,] - thelatemail
@thelatemail 谢谢,这样更好。我已经将其与顶部标记一起编辑了。[我稍微修改了一下,以便在提到如何使用“ID”时,清楚地知道我正在引用代码的哪个部分(ave的第二个参数)。] - Frank
1
@Frank - 你还可以通过使用rownames进行子集筛选,使得by / tapply逻辑起作用 - dat[unlist(tapply(rownames(dat), dat$ID, FUN=function(x) if(length(x)>1) x)),] - thelatemail
@thelatemail 再次感谢。我已经添加了它,并重新排列以适应一些顶部的评论。 - Frank
如果我想要进行处理并只获取每个唯一ID的第一行,你有什么建议吗? - sleepyjoe
@sleepyjoe 你是指 df %>% distinct(ID) 或者使用 data.table 的 unique(df,by="ID") - Frank

1

我知道这是一个老问题,但我最近遇到了同样的问题,发现这个解决方案最简单:

data<- data[duplicated(data$ID)]

在这种情况下,您只保留重复对中的一个值。 - Igniste
那是错误的。请查看期望的输出和@agstudy给出的正确的“duplicated”答案。 - GuedesBF

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接