【问题标题】:Finding unique combinations irrespective of position [duplicate]无论位置如何,都能找到独特的组合[重复]
【发布时间】:2015-07-01 10:20:05
【问题描述】:

我确定这很简单,但我有一个数据框

      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) 不同,这是我不想要的。

【问题讨论】:

    标签: r duplicates combinations


    【解决方案1】:

    也许是这样的

    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
    

    【讨论】:

      【解决方案2】:

      如果您的 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,]
      

      这里我们借助 numbers 包中的函数 Primes 初始化向量 primenums,但您可以通过其他方式手动进行。

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

      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
      

      如果我们有很多列或非常不同的变量怎么办?在这种情况下,我们可以使用sum(log()) 代替prod()(对于大数,它的计算速度可能更快)。看看这个。

      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 有 300 万列,但这里没问题。通过这种方式,我们可以使用 RAM 可以容纳的尽可能多的列来携带任何令人难以置信的巨大大小的 df

      【讨论】:

      • 这是一个不错的选择,但不确定你在这里as.matrix(df) 是否有点作弊。
      • + 1 非常快,我喜欢使用素数分解的想法,但这种方法有两个限制: 1) 如果有大量列取素数的乘积将不起作用(例如prod(Primes(200)) 等于 prod(Primes(201))) 和 2) 如果数据框包含大量不同的元素,它将不起作用(因为您需要为每个元素生成一个素数,这可能很麻烦,而且因为产品不会不能像上一点那样被计算机区分)
      • 为了保持冷静而不是骗子而不是as.matrix,我们可以做apply(df, 2, function(colmn) as.integer(factor(colmn, levels = unique(c(as.matrix(df))))))。它会更慢但不是很慢,我会在明天给出时间和更新答案,因为现在远离 PC。我同意,素数的使用有局限性,但也许你可以尝试不同的 r 包来处理非常大的数字?
      • 编辑了我的答案以发展这个想法
      【解决方案3】:

      作为一种替代方法,包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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-03
        • 2021-06-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多