在 data.table 中过滤出重复/非唯一行

102

编辑2019:此问题是在2016年11月data.table更改之前提出的,请参见下面的接受答案,了解当前和以前的方法。

我有一个包含大约250万行的data.table表格。有两列。我想删除在两列中都重复的任何行。以前对于数据框架,我会这样做:df -> unique(df[,c('V1', 'V2')])但是这在data.table中不起作用。我尝试过unique(df[,c(V1,V2), with=FALSE]),但它似乎仍然只在数据表的键上操作,而不是整个行。

有什么建议吗?

谢谢, Davy

示例

>dt
      V1   V2
[1,]  A    B
[2,]  A    C
[3,]  A    D
[4,]  A    B
[5,]  B    A
[6,]  C    D
[7,]  C    D
[8,]  E    F
[9,]  G    G
[10,] A    B

在上述的 data.table 中,其中 V2 是表格键,只有第 4、7 和 10 行将被删除。
dt <- data.table::data.table(
  V1 = c("B", "A", "A", "A", "A", "A", "C", "C", "E", "G"),
  V2 = c("A", "B", "B", "B", "C", "D", "D", "D", "F", "G"),
)

3
不使用独特的 dt[, .N,by=.(V1,V2)][,1:2] 语句。 - Akki
5个回答

125

v1.9.8+2016年11月发布

?unique.data.table来看,默认情况下会使用所有列(这与?unique.data.frame一致)。

unique(dt)
   V1 V2
1:  A  B
2:  A  C
3:  A  D
4:  B  A
5:  C  D
6:  E  F
7:  G  G

或者可以使用by参数来获取特定列的唯一组合(就像之前使用键一样)

unique(dt, by = "V2")
   V1 V2
1:  A  B
2:  A  C
3:  A  D
4:  B  A
5:  E  F
6:  G  G

早于v1.9.8版本:

?unique.data.table得知,在数据表上调用unique函数只能对关键列进行操作。这意味着您必须在调用unique之前将关键列重置为所有列。

library(data.table)
dt <- data.table(
  V1=LETTERS[c(1,1,1,1,2,3,3,5,7,1)],
  V2=LETTERS[c(2,3,4,2,1,4,4,6,7,2)]
)

使用单个列作为键调用unique函数:

setkey(dt, "V2")
unique(dt)
     V1 V2
[1,]  B  A
[2,]  A  B
[3,]  A  C
[4,]  A  D
[5,]  E  F
[6,]  G  G


2
只有在没有设置密钥的情况下才能正常工作。我会编辑上面的问题以澄清这一点。抱歉。 - Davy Kavanagh
2
正如akrun在这里回答的那样:http://stackoverflow.com/questions/40949023/r-somehow-unique-is-not-working-for-my-data-table 现在第一个版本需要一个by=选项才能工作。 - Jakob
@robertevansanders 奇怪。那现在有什么可用的方法吗?对我来说,by= 选项仍然有效。 - Jakob
17
@Andrie 这个解决方案已经失效了,正如@PeterPan所指出的那样。data.table现在不再将unique()视为键值(keys)的一部分。现在必须使用选项unique(, by = c(keys)) - altabq
4
请注意,altabq是正确的,而在keys中的内容必须用引号括起来。因此您需要使用unique(dt, by=c("V1","V2"))来获得答案。 - Corey Levinson
显示剩余3条评论

9

使用您的示例数据表...

> dt<-data.table(V1 = c("B", "A", "A", "A", "A", "A", "C", "C", "E", "G"), V2 = c("A", "B", "B", "B", "C", "D", "D", "D", "F", "G"))
> setkey(dt,V2)

考虑以下测试:

> haskey(dt) # obviously dt has a key, since we just set it
[1] TRUE

> haskey(dt[,list(V1,V2)]) # ... but this is treated like a "new" table, and does not have a key
[1] FALSE

> haskey(dt[,.SD]) # note that this still has a key
[1] TRUE

因此,您可以列出表的列,然后对其进行unique()操作,无需将密钥设置为所有列或删除它(将其设置为NULL),如@Andrie(由@MatthewDowle编辑)的解决方案所要求。 @Pop和@Rahul提出的解决方案对我不起作用。
请参见下面的尝试3,它与您的初始尝试非常相似。 您的示例不够清晰,因此我不确定为什么它不起作用。 此外,您发布问题时已经过去几个月了,也许已更新data.table
> unique(dt) # Try 1: wrong answer (missing V1=C and V2=D)
   V1 V2
1:  B  A
2:  A  B
3:  A  C
4:  A  D
5:  E  F
6:  G  G

> dt[!duplicated(dt)] # Try 2: wrong answer (missing V1=C and V2=D)
   V1 V2
1:  B  A
2:  A  B
3:  A  C
4:  A  D
5:  E  F
6:  G  G

> unique(dt[,list(V1,V2)]) # Try 3: correct answer; does not require modifying key
   V1 V2
1:  B  A
2:  A  B
3:  A  C
4:  A  D
5:  C  D
6:  E  F
7:  G  G

> setkey(dt,NULL)
> unique(dt) # Try 4: correct answer; requires key to be removed
   V1 V2
1:  B  A
2:  A  B
3:  A  C
4:  A  D
5:  C  D
6:  E  F
7:  G  G

3
也许一个新的 unique(...,use.key=FALSE) 参数会有所帮助;现在已作为 FR#2483 提交。 - Matt Dowle
嗨@MatthewDowle。是的,那将是一个不错的便利。我认为您在FR中的评论也是正确的——如果键是唯一的,则可以忽略use.key=FALSE - dnlbrky
1
data.table 1.9.6(不包括早期版本)具有选项“by =”,可用于覆盖键。设置“by = NULL”将“使用所有列并像类似的data.frame方法一样运行”。 - JWilliman

1

这应该适合你的需求

dt <- unique(dt, by = c('V1', 'V2'))

1
OP想要删除整个数据表中重复的行,而不仅仅是关键字。以下代码可以实现此功能。 - Magma

1

unique(df) 在你的例子中可以使用。


0

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