从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
上使用 duplicated
,pmin
输出到 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
我有两列,想只保留不可交换的 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
上使用 duplicated
,pmin
输出到 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