2015-04-23 72 views
8
位置

我敢肯定,这是简单的东西,但我有一个数据帧寻找独特的组合,不论

 df <- data.frame(a = c(1, 2, 3), 
         b = c(2, 3, 1), 
         c = c(3, 1, 4)) 

而且我要包含值的行中的独特组合,新的数据帧,不论他们在其列。因此,在上述情况下我会想

a b c 
    1 2 3 
    3 1 4 

我已经试过

unique(df[c('a', 'b', 'c')]) 

,但它认为(1,2,3)与(2,3,1)是唯一的,这是我不想要的。

回答

4

也许类似的东西

indx <- !duplicated(t(apply(df, 1, sort))) # finds non - duplicates in sorted rows 
df[indx, ] # selects only the non - duplicates according to that index 
# a b c 
# 1 1 2 3 
# 3 3 1 4 
1

作为一个替代方法,包sets提供检查组平等的快速方法:

library(sets) 
df.sets <- apply(df, 1, as.set) 
#[[1]] 
#{1, 2, 3} 
#[[2]] 
#{1, 2, 3} 
#[[3]] 
#{1, 3, 4} 
df[!duplicated(df.sets),] 
# a b c 
#1 1 2 3 
#3 3 1 4 
4

如果您data.frame相当大,速度可能是你的问题。使用以下想法可以更快地找到重复的集合。

让我们假想为行中的每个可能的值分配一个素数并计算每行的乘积。例如,对于给定的df,我们可以接受primenums = c(2,3,5,7)并计数产品c(30,30,70)。然后在这个产品向量中重复对应于我们data.frame中的重复集合。由于乘法的计算速度要快得多,所以可以提高效率。 代码如下。

require("numbers") 
primenums <- Primes(100)[1:4] 
dfmult <- apply(as.matrix(df), 1, function(z) prod(primenums[z])) 
my_indx <- !duplicated(dfmult) 
df[my_indx,] 

在这里,我们初始化向量primenums与功能Primes从包numbers的帮助,但你可以在其他的方式做手工。

看看这个例子。在这里我展示了效率的比较。

require("numbers") 

# generate all unique combinations 10 out of 20 
allcomb <- t(combn(20,10)) 
# make sample of 1 million rows 
set.seed(789) 
df <- allcomb[sample(nrow(allcomb), 1e6, T),] 
# lets sort matrix to show we have duplicates 
df <- df[do.call(order, lapply(1:ncol(df), function(i) df[, i])), ] 
head(df, 10) 
#  [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] 
# [1,] 1 2 3 4 5 6 7 8 9 10 
# [2,] 1 2 3 4 5 6 7 8 9 10 
# [3,] 1 2 3 4 5 6 7 8 9 10 
# [4,] 1 2 3 4 5 6 7 8 9 10 
# [5,] 1 2 3 4 5 6 7 8 9 11 
# [6,] 1 2 3 4 5 6 7 8 9 11 
# [7,] 1 2 3 4 5 6 7 8 9 11 
# [8,] 1 2 3 4 5 6 7 8 9 11 
# [9,] 1 2 3 4 5 6 7 8 9 11 
# [10,] 1 2 3 4 5 6 7 8 9 11 

# to be fair need to permutate numbers in rows before searching for identical sets 
df <- t(apply(df, 1, function(z) z[sample(10,10)])) 
df <- as.data.frame(df) 
names(df) <- letters[1:10] 
# how does it look like now? 
head(df, 10) 
#  a b c d e f g h i j 
# 1 2 3 7 9 10 1 4 8 5 6 
# 2 4 2 6 3 8 10 9 1 5 7 
# 3 4 2 6 8 5 1 10 7 3 9 
# 4 6 8 5 4 2 1 10 9 7 3 
# 5 11 2 7 6 8 1 9 4 5 3 
# 6 9 6 3 11 4 2 8 7 5 1 
# 7 5 2 3 11 1 8 6 9 7 4 
# 8 3 9 7 1 2 5 4 8 11 6 
# 9 6 2 8 3 4 1 11 5 9 7 
# 10 4 6 3 9 7 2 1 5 11 8 

# now lets shuffle rows to make df more plausible 
df <- df[sample(nrow(df), nrow(df)),] 

现在,当data.frame准备就绪时,我们可以测试不同的算法。

system.time(indx <- !duplicated(t(apply(df, 1, sort)))) 
# user system elapsed 
# 119.75 0.06 120.03 
# doesn't impress, frankly speaking 

library(sets) 
system.time(indx <- !duplicated(apply(df, 1, as.set))) 
# user system elapsed 
# 91.60 0.00 91.89 
# better, but we want faster! =) 

# now lets check out the method with prime numbers 
primenums <- Primes(100)[1:20] 
# [1] 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 
system.time({ 
    dfmult <- apply(as.matrix(df), 1, function(z) prod(primenums[z])) 
    my_indx <- !duplicated(dfmult) }) 
# user system elapsed 
# 6.44 0.16 6.61 
# not bad, isn't it? but lets compare results 
identical(indx, my_indx) 
# [1] TRUE 

# So, if there is no difference, why wait more? ;) 

有一个重要的假设在这里 - 我们使用as.matrix(df),但如果出现在我们的data.frame不仅是数值型变量?一个更统一的解决方案如下:

system.time({ 
    dfmult <- apply(
    apply(df, 2, function(colmn) as.integer(factor(colmn, 
                levels = unique(c(as.matrix(df)))))), 
    1, function(z) prod(primenums[z])) 
    my_indx <- !duplicated(dfmult) }) 
# user system elapsed 
# 27.48 0.34 27.84 
# is distinctly slower but still much faster then previous methods 

那么如果我们有很多列或非常不同的变量呢?在这种情况下,而不是prod(),我们可以使用sum(log())(这对于大数字来说可能更快)。看看这个。

pr <- Primes(5e7) 
length(pr) 
# [1] 3001134 
system.time(N <- sum(log(pr))) 
# user system elapsed 
# 0.12 0.00 0.13 
N 
# [1] 49993718 

很难想象df与3百万列,但在这里没关系。通过这种方式,我们可以携带任何难以置信的巨大尺寸的df和我们的RAM可容纳的列数。

+0

这是一个很好的,但不知道如果你在这里做'as.matrix(df)'有点作弊。 –

+1

+ 1非常快,我喜欢使用素数因子分解的想法,但这种方法有两个限制:1)如果有大量列使用素数的乘积将不起作用(例如'prod(Primes(200) )'等于'prod(Primes(201))')和2)如果数据帧包含大量不同的元素,它将不会工作(因为您需要为每个元素生成素数,这可能很麻烦,而且因为产品不会像以前那样被计算机所区分) – konvas

+0

为了保持冷静,不要留下作弊者而不是'as.matrix',我们可以应用(df,2,function(colmn)as.integer(因子(colmn,levels = unique(c(as.matrix(df))))))'。它会变慢,但不是很多,我会给明天的计时和更新答案,因为现在我远离PC。我同意,素数的使用有一定的局限性,但也许你可以尝试不同的r软件包,这些软件包可以处理非常庞大的数字? – inscaven