在dplyr中的group_by条件下有条件地删除重复行

3
我知道有很多关于删除重复项的内容,但我的问题似乎不同。
我有一个类似于这个的data.frame:
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
x <- data.frame(id = c(1, 1, 1, 1, 2, 3, 3),
                date = as.Date(c("2016-04-24", "2016-04-24", "2016-04-24",
                                 "2016-04-24", "2016-04-24", "2016-04-28",
                                 "2016-04-28")),
                code = c("a", "b", "b", "a", "a", "a", "a"))
x
#>   id       date code
#> 1  1 2016-04-24    a
#> 2  1 2016-04-24    b
#> 3  1 2016-04-24    b
#> 4  1 2016-04-24    a
#> 5  2 2016-04-24    a
#> 6  3 2016-04-28    a
#> 7  3 2016-04-28    a

我希望筛选出所有code为"a"但不是"b"的重复项。预期输出如下所示:
x[c(1:3, 5:6), ]
#>   id       date code
#> 1  1 2016-04-24    a
#> 2  1 2016-04-24    b
#> 3  1 2016-04-24    b
#> 5  2 2016-04-24    a
#> 6  3 2016-04-28    a

我之前提出了一个类似的问题,链接在这里:Ignore value conditionally within group_by in dplyr。我基于这个问题尝试了一些解决方法,但它们都没有成功,这让我很烦恼。

x %>% group_by(id, date) %>% 
  filter(!(code == "a" & duplicated(code) == "a"))
#> # A tibble: 7 x 3
#> # Groups:   id, date [3]
#>      id date       code 
#>   <dbl> <date>     <fct>
#> 1    1. 2016-04-24 a    
#> 2    1. 2016-04-24 b    
#> 3    1. 2016-04-24 b    
#> 4    1. 2016-04-24 a    
#> 5    2. 2016-04-24 a    
#> 6    3. 2016-04-28 a    
#> 7    3. 2016-04-28 a


x %>% group_by(id, date) %>% 
  filter(!(duplicated(code) == "a" & "a" %in% code))
#> # A tibble: 7 x 3
#> # Groups:   id, date [3]
#>      id date       code 
#>   <dbl> <date>     <fct>
#> 1    1. 2016-04-24 a    
#> 2    1. 2016-04-24 b    
#> 3    1. 2016-04-24 b    
#> 4    1. 2016-04-24 a    
#> 5    2. 2016-04-24 a    
#> 6    3. 2016-04-28 a    
#> 7    3. 2016-04-28 a

这段文字是关于 IT 技术的,创建日期为 2018 年 8 月 17 日,使用了 reprex package(版本为 v0.2.0)。

我猜问题出在 duplicated() 函数调用上,它没有返回 TRUE 或者 FALSE,但我不确定。

5个回答

4

按'id'和'date'分组后,获取'code'为'a'的逻辑向量,对此应用duplicated函数,或者在'code'不是'a'的情况下应用。

x %>% 
  group_by(id, date) %>% 
  filter(!duplicated(code == "a") | code != 'a')
# A tibble: 5 x 3
# Groups:   id, date [3]
#     id date       code 
#  <dbl> <date>     <fct>
#1     1 2016-04-24 a    
#2     1 2016-04-24 b    
#3     1 2016-04-24 b    
#4     2 2016-04-24 a    
#5     3 2016-04-28 a    

请问您能否解释一下为什么这个代码可以用 'a' 但是不能用 "a"?当我使用x %>% group_by(id, date) %>% filter(!duplicated(code == "a") | code != "a") 它会忽略所有的 b 代码。 - Frederick
@Frederick。引号可以是单引号或双引号。这只是我在打字。 - akrun
我还有一个跟进的问题:filter(!duplicated(code == "a")filter(!duplicated(code) 看起来是一样的。因此,在duplicated()中指定 == "a"似乎是多余的。为什么? - Frederick
@Frederick 这可能是一个例子的情况。 - akrun
@Frederick 举个例子,检查 v1 <- c('a', 'a', 'b', 'c', 'b'); !duplicated(v1 == 'a'); !duplicated(v1) - akrun

2

使用 slice 的另一种方法。按照 iddatecode 进行分组。如果该组中有任何一个 a(它应该是全部为 a 或全部为其他内容),则取第一行,否则返回整个组:

library(dplyr)

x %>% 
  group_by(id, date, code) %>% 
  slice(if(any(code == "a")) 1 else 1:n())

结果:

# A tibble: 5 x 3
# Groups:   id, date, code [4]
     id date       code 
  <dbl> <date>     <fct>
1     1 2016-04-24 a    
2     1 2016-04-24 b    
3     1 2016-04-24 b    
4     2 2016-04-24 a    
5     3 2016-04-28 a 

2
使用data.table,您可以执行以下操作:
library(data.table)
setDT(x)

x[ code != "a" | !duplicated(x, by=c("id", "date", "code")) ]

   id       date code
1:  1 2016-04-24    a
2:  1 2016-04-24    b
3:  1 2016-04-24    b
4:  2 2016-04-24    a
5:  3 2016-04-28    a

这与@akrun的答案类似,但不需要使用group-by,因为具有参数。使用基本的R语言(感谢@Moody_Mudskipper),可以将其转换为:
x[ code != "a" | !duplicated(x[c("id", "date", "code")]) ]

1
在基本的R中,可以使用以下代码实现:x[x$code != "a" | !duplicated(x[c("id", "date", "code")]),] 或者 subset(x,code != "a" | !duplicated(cbind(id, date, code))) - moodymudskipper

0

这是一个没有使用重复的示例:

      data.frame(x%>%
      filter(code=="a")%>%
      group_by(id, date)%>%
      summarise(code=first(code)))%>%
      rbind(data.frame(x%>%filter(code=="b")))

0

在基本R中的另一种方法:

x$y <- cumsum(x$code=="b") * (x$code == "b")
unique(x)[-4]
#   id       date code
# 1  1 2016-04-24    a
# 2  1 2016-04-24    b
# 3  1 2016-04-24    b
# 5  2 2016-04-24    a
# 6  3 2016-04-28    a

(但我可能更倾向于使用我在Frank的回答下面评论的方法)

使用tidyverse,我会这样做:

library(tidyverse)
x %>% split(.$code) %>% map_at("a",distinct) %>% bind_rows 
#   id       date code
# 1  1 2016-04-24    a
# 2  2 2016-04-24    a
# 3  3 2016-04-28    a
# 4  1 2016-04-24    b
# 5  1 2016-04-24    b

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接