从R中的数据框中删除重复行

Removing duplicate rows from data frame in R

我有两列,想只保留不可交换的 rows.For 我的输出下面的数据应该包含 (1 2) 的一种组合。即对于我的查询 (1 2) 与 (2 1) 相同。在 R 中有没有一种简单的方法可以做到这一点。已经尝试过转置。并保留上三角矩阵。但是重新转回数据变得很痛苦。

A B prob
1 2 0.1
1 3 0.2
1 4 0.3
2 1 0.3
2 3 0.1
2 4 0.4

我的最终输出应该是:

A B prob
1 2 0.1
1 3 0.2
1 4 0.3
2 3 0.1
2 4 0.4

这应该有效:

d <- data.frame(A=rep(1:2, each=4), B=rep(1:4, 2), p=rnorm(n=8))
> d
  A B           p
1 1 1 -1.26282557
2 1 2 -0.03627707
3 1 3  1.50063527
4 1 4 -0.30038114
5 2 1 -0.01509190
6 2 2  0.13634069
7 2 3 -0.39612927
8 2 4 -0.10895007
l <- 1:nrow(d) # Create an index vector
v <- apply(cbind(d$A, d$B), 1, sort) # Make (1, 2) look like (2, 1)
v <- paste(v[1,], v[2,]) # Create vector where (1, 2) and (2, 1) both look like '1 2'
fun <- function(x) return(x[1]) # Function for tapply to only return the first match for the pattern
i <- tapply(l, v, fun) # get relevant index
res <- d[i, ] # Create result vektor
> res
  A B          p
1 1 1 -0.6742351
2 1 2 -1.5895396
3 1 3 -1.5975784
4 1 4 -1.4764792
6 2 2 -0.1682946
7 2 3 -0.5799141
8 2 4  2.4104019

请注意,这将使用第一次出现的模式。

我们可以独立sort() each row and then use !duplicated()找到要保留的行:

df[!duplicated(t(apply(df[1:2],1L,sort))),];
##   A B prob
## 1 1 2  0.1
## 2 1 3  0.2
## 3 1 4  0.3
## 5 2 3  0.1
## 6 2 4  0.4

数据

df <- data.frame(A=c(1L,1L,1L,2L,2L,2L),B=c(2L,3L,4L,1L,3L,4L),prob=c(0.1,0.2,0.3,0.3,0.1,0.4
));

说明

第一步是只提取感兴趣的两列:

df[1:2];
##   A B
## 1 1 2
## 2 1 3
## 3 1 4
## 4 2 1
## 5 2 3
## 6 2 4

然后我们用 apply()sort():

独立地对每一行进行排序
apply(df[1:2],1L,sort);
##      [,1] [,2] [,3] [,4] [,5] [,6]
## [1,]    1    1    1    1    2    2
## [2,]    2    3    4    2    3    4

如您所见,apply() returns 导致意外的转置,因此我们必须用 t() 修复它,为即将到来的 duplicated() 调用做准备:

t(apply(df[1:2],1L,sort));
##      [,1] [,2]
## [1,]    1    2
## [2,]    1    3
## [3,]    1    4
## [4,]    1    2
## [5,]    2    3
## [6,]    2    4

现在我们可以使用 duplicated() 来获取一个逻辑向量,指示哪些行与之前的行重复:

duplicated(t(apply(df[1:2],1L,sort)));
## [1] FALSE FALSE FALSE  TRUE FALSE FALSE

然后我们用一个否定来反转逻辑向量,只得到那些与之前任何行重复的行:

!duplicated(t(apply(df[1:2],1L,sort)));
## [1]  TRUE  TRUE  TRUE FALSE  TRUE  TRUE

最后,我们可以使用生成的逻辑向量来索引 df 中与之前任何行都不重复的那些行:

df[!duplicated(t(apply(df[1:2],1L,sort))),];
##   A B prob
## 1 1 2  0.1
## 2 1 3  0.2
## 3 1 4  0.3
## 5 2 3  0.1
## 6 2 4  0.4

因此,每组 post 排序重复项的第 将被保留,其余的将被删除。


来自@RichardScriven 的极好的建议;我们可以用 duplicated()MARGIN 参数替换 t() 调用,这可能会稍微快一些:

df[!duplicated(apply(df[1:2],1L,sort),MARGIN=2L),];
##   A B prob
## 1 1 2  0.1
## 2 1 3  0.2
## 3 1 4  0.3
## 5 2 3  0.1
## 6 2 4  0.4

我们可以使用data.table。将'data.frame'转换为'data.table'(setDT(df1)),按pmin(A, B)pmax(A,B)分组,if的行数大于1,我们得到第一行或 else return 行。

 library(data.table)
 setDT(df1)[, if(.N >1) head(.SD, 1) else .SD ,.(A=pmin(A, B), B= pmax(A, B))]
 #   A B prob
 #1: 1 2  0.1
 #2: 1 3  0.2
 #3: 1 4  0.3
 #4: 2 3  0.1
 #5: 2 4  0.4

或者我们可以只在 pmax 上使用 duplicatedpmin 输出到 return 一个逻辑索引并根据该索引对数据进行子集化。

 setDT(df1)[!duplicated(cbind(pmax(A, B), pmin(A, B)))]
 #   A B prob
 #1: 1 2  0.1
 #2: 1 3  0.2
 #3: 1 4  0.3
 #4: 2 3  0.1
 #5: 2 4  0.4

这是另一个使用 base R 的解决方案。想法是在 df 的后半部分(使用 sapply)搜索是否有任何重复。然后我们得到 secondHalf 向量。我们进一步从 df.

中删除这些行
n <- nrow(df)
secondHalf <- sapply(seq(n/2), function(i) nrow(df[df$A==df[i,2] & df$B==df[i,1],]))
# [1] 1 0 0
toRemove <- ((floor(n/2)+1):n)*secondHalf
df <- df[-toRemove,]

  # A B prob
# 1 1 2  0.1
# 2 1 3  0.2
# 3 1 4  0.3
# 5 2 3  0.1
# 6 2 4  0.4