选择除一列外所有重复的行

时间:2018-07-12 10:01:45

标签: r tidyverse

我想在数据集中找到行,其中所有列中的值(一个除外)都匹配。在尝试不成功地获取duplicate()以返回重复行的所有实例(而不仅仅是第一个实例)后,我一团糟,然后我想出了一种方法(如下)。

例如,我想识别虹膜数据集中除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

就像您看到的那样,它行得通,但这是我几乎可以肯定很多其他人都需要此功能的一次,而我不知道单个功能可以用更少的步骤或一般比较整齐。有什么建议吗?

从至少two other个帖子中采用的另一种方法是:

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

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

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

2 个答案:

答案 0 :(得分:2)

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 TRUE秒,因此它将返回预期的输出。

或者也许使用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

答案 1 :(得分:0)

我调查了duplicated的来源,但想知道是否有人可以更快地找到任何东西。它可能涉及到Rcpp或类似的内容。在我的机器上,基本方法是最快的,但是您的原始方法实际上比最易读的dplyr方法要好。我认为为自己的目的包装一个这样的函数就足够了,因为无论如何您的运行时间似乎都不会太长,如果这是主要问题,则可以简单地iris %>% 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-07-12创建。