【问题标题】:Select all rows which are duplicates except for one column选择除一列之外的所有重复行
【发布时间】:2018-07-12 10:01:45
【问题描述】:

我想在数据集中查找所有列中的值(除了一列)匹配的行。在尝试多次尝试让 duplicated() 返回重复行的所有实例(不仅仅是第一个实例)失败之后,我找到了一种方法(如下)。

例如,我想识别 Iris 数据集中除了 Petal.Width 之外的所有行。

require(tidyverse)
x = iris%>%select(-Petal.Width)
dups = x[x%>%duplicated(),]
answer =  iris%>%semi_join(dups)

> answer 
   Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
1           5.1         3.5          1.4         0.2    setosa
2           4.9         3.1          1.5         0.1    setosa
3           4.8         3.0          1.4         0.1    setosa
4           5.1         3.5          1.4         0.3    setosa
5           4.9         3.1          1.5         0.2    setosa
6           4.8         3.0          1.4         0.3    setosa
7           5.8         2.7          5.1         1.9 virginica
8           6.7         3.3          5.7         2.1 virginica
9           6.4         2.8          5.6         2.1 virginica
10          6.4         2.8          5.6         2.2 virginica
11          5.8         2.7          5.1         1.9 virginica
12          6.7         3.3          5.7         2.5 virginica

如您所见,这是可行的,但这是我几乎可以肯定很多其他人需要此功能的时候之一,而且我不知道有一个功能可以通过更少的步骤或通常更整洁的方式。有什么建议吗?

至少从twoother 帖子中应用于这种情况的另一种方法是:

answer = iris[duplicated(iris[-4]) | duplicated(iris[-4], fromLast = TRUE),]

但这似乎也只是一种不同的解决方法,而不是单一功能。两种方法都需要相同的时间。 (在我的系统上为 0.08 秒)。没有更整洁/更快的方法吗?

例如就像是 iris%>%duplicates(all=TRUE,ignore=Petal.Width)

【问题讨论】:

  • 请用可重复的数据和期望的结果做一个小例子。谢谢。
  • 这基本上是iris[duplicated(iris[-4]) | duplicated(iris[-4], fromLast = TRUE),]
  • 是的,我之前看过那篇文章,这与大卫的答案非常相似,但对于我认为必须是一项相对常见的任务来说,这似乎也是一个不太优雅的解决方案......有没有返回除一列之外的所有重复项的单个函数?或者甚至只是返回所有重复的条目?
  • 关于“是的,我以前看过那个帖子,”你应该在你的帖子中包含这些信息,并清楚地解释为什么以前的答案不能满足你的需求。干杯
  • 我添加了一个 dplyr 解决方案,它需要更少的字符来编写,并且可能比基本的 R 解决方案更容易阅读。但是,我并没有对其进行微基准测试来测试速度。

标签: r tidyverse


【解决方案1】:
iris[duplicated(iris[,-4]) | duplicated(iris[,-4], fromLast = TRUE),]

重复行(无论第 4 列如何)duplicated(iris[,-4]) 给出重复集的第二行,第 18、35、46、133、143 和 145 行,duplicated(iris[,-4], fromLast = TRUE) 给出每个重复集的第一行,1 , 10, 13, 102, 125 和 129。通过添加 | 这将得到 12 TRUEs,因此它返回预期的输出。

或者也许使用 dplyr:基本上你对除 Petal.Width 之外的所有变量进行分组,计算它们出现的次数,并过滤那些出现多次的变量。

library(dplyr)
iris %>% 
  group_by_at(vars(-Petal.Width)) %>% 
  filter(n() > 1)

   Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
          <dbl>       <dbl>        <dbl>       <dbl>    <fctr>
 1          5.1         3.5          1.4         0.2    setosa
 2          4.9         3.1          1.5         0.1    setosa
 3          4.8         3.0          1.4         0.1    setosa
 4          5.1         3.5          1.4         0.3    setosa
 5          4.9         3.1          1.5         0.2    setosa
 6          4.8         3.0          1.4         0.3    setosa
 7          5.8         2.7          5.1         1.9 virginica
 8          6.7         3.3          5.7         2.1 virginica
 9          6.4         2.8          5.6         2.1 virginica
10          6.4         2.8          5.6         2.2 virginica
11          5.8         2.7          5.1         1.9 virginica
12          6.7         3.3          5.7         2.5 virginica

【讨论】:

  • 请提供一些解释,而不仅仅是代码。谢谢!
  • 我为我的基本 R 解决方案添加了一些解释,并且现在还添加了一种使用 dplyr 的方法。
  • 在我的数据集(约 600K 行,5 列)上,仅使用 duplicated() 的第一个选项最快,为 0.14 秒。然而,第二个使用 dplyr - 'tidy' 解决方案 - 对我来说更直观。不过,它比第一个选项慢(0.52 秒)。不出所料,我提出的初始解决方案是最慢的(8.6 秒)。谢谢!
【解决方案2】:

我查看了duplicated 的来源,但有兴趣看看是否有人能更快地找到任何东西。它可能涉及到Rcpp 或类似的东西。在我的机器上,基本方法是最快的,但您的原始方法实际上比最易读的dplyr 方法更好。我认为为您自己的目的包装这样的函数应该就足够了,因为无论如何您的运行时间似乎不会太长,如果这是主要问题,您可以简单地使用iris %&gt;% opts("Petal.Width") 进行管道化。

library(tidyverse)
library(microbenchmark)

opt1 <- function(df, ignore) {
  ignore = enquo(ignore)
  x <- df %>% select(-!!ignore)
  dups <- x[x %>% duplicated(), ]
  answer <- iris %>% semi_join(dups)
}

opt2 <- function(df, ignore) {
  index <-  which(colnames(df) == ignore)
  df[duplicated(df[-index]) | duplicated(df[-index], fromLast = TRUE), ]
}

opt3 <- function(df, ignore){
  ignore <-  enquo(ignore)
  df %>%
    group_by_at(vars(-!!ignore)) %>%
    filter(n() > 1)
}


microbenchmark(
  opt1 = suppressMessages(opt1(iris, Petal.Width)),
  opt2 = opt2(iris, "Petal.Width"),
  opt3 = opt3(iris, Petal.Width)
)
#> Unit: milliseconds
#>  expr      min       lq     mean   median       uq       max neval cld
#>  opt1 3.427753 4.024185 4.851445 4.464072 5.069216 12.800890   100  b 
#>  opt2 1.712975 1.908130 2.403859 2.133632 2.542871  7.557102   100 a  
#>  opt3 6.604614 7.334304 8.461424 7.920369 8.919128 24.255678   100   c

reprex package (v0.2.0) 于 2018 年 7 月 12 日创建。

【讨论】:

    猜你喜欢
    • 2013-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-01
    • 2012-02-01
    • 2018-12-16
    • 2013-02-08
    • 1970-01-01
    相关资源
    最近更新 更多