【问题标题】:Filter rows in a group based on the value for another group根据另一组的值过滤组中的行
【发布时间】:2022-01-12 13:36:40
【问题描述】:

我有一个数据表,其中包括一个 ID、一个(以某种方式排序的)分组列和一个日期。对于每个 ID,根据给定组的日期最小值,我想过滤掉另一个给定组在该日期之后发生的行。

我考虑过使用pivot_widerpivot_longer,但是我无法同时对包含列表值和单个值的列进行操作。

我怎样才能有效地做到这一点(如果可能,使用任何tidyverse 方法)?

例如,给定

library(dplyr)
tbl <- tibble(id = c(rep(1,5), rep(2,5)),
              type = c("A","A","A","B","C","A","A","B","B","C"),
              dat = as.Date("2021-12-07") - c(3,0,1,2,0,3,6,2,4,3))

# A tibble: 10 × 3
#       id type  dat       
#    <int> <chr> <date>    
#  1     1 A     2021-12-04
#  2     1 A     2021-12-07
#  3     1 A     2021-12-06
#  4     1 B     2021-12-05
#  5     1 C     2021-12-07
#  6     2 A     2021-12-04
#  7     2 A     2021-12-01
#  8     2 B     2021-12-05
#  9     2 B     2021-12-03
# 10     2 C     2021-12-04

我想要以下结果,其中我丢弃了出现在第一个 B 类型元素之后的 A 类型元素,但没有一个 C 类型元素:

# A tibble: 7 × 3
#      id type  dat       
#   <int> <chr> <date>    
# 1     1 A     2021-12-04
# 2     1 B     2021-12-05
# 3     1 C     2021-12-07
# 4     2 A     2021-12-01
# 5     2 B     2021-12-05
# 6     2 B     2021-12-03
# 7     2 C     2021-12-04

【问题讨论】:

  • 我假设C类型的日期是2021-12-07 ?
  • 是的,这是一个复制粘贴错误。谢谢
  • 您的示例在多大程度上代表了您的真实用例? IE。你的类型总是 A/B/C = 字母吗?您是否只对过滤类型 A 与 B 感兴趣,或者是否还有过滤 B 与 C 或 A 与 C 的情况?请澄清。
  • 这个例子有点抽象。在我的特殊情况下,type 是一种排序的事件集(例如,购买

标签: r dplyr


【解决方案1】:

在这种情况下,我喜欢使用 pivot_wider 和 pivot_longer。它可以解决问题,但也许您正在寻找更短的东西。

tbl <- tibble(id = 1:5, type = c("A","A","A","B","C"), dat = as.Date("2021-12-07") - c(3,4,1,2,0)) %>% 
  pivot_wider(names_from = type, values_from = dat) %>% 
  filter(A < min(B, na.rm = TRUE) | is.na(A)) %>% 
  pivot_longer(2:4, names_to = "type", values_to = "dat") %>% 
  na.omit()

# A tibble: 4 × 3
     id type  dat       
  <int> <chr> <date>    
1     1 A     2021-12-04
2     2 A     2021-12-03
3     4 B     2021-12-05
4     5 C     2021-12-07

【讨论】:

  • 感谢您的快速回答。但是,通过检查您的解决方案,我意识到我在示例中犯了一个错误(如果可能,请再次检查)。 ID 在列中不是唯一的,因此您的扩展过程会产生重复。
【解决方案2】:

一种使用 SQL 逻辑的简单方法:

tbl_to_delete <- tbl %>% dplyr::filter(type == "A" & dat > min(tbl$dat[tbl$type=="B"]))
tbl2 <- tbl %>% dplyr::anti_join(tbl_to_delete,by=c("type","dat"))

首先隔离要删除的行,然后将它们从原始数据中丢弃。

您当然可以将之前的两行合并为一行,以便更好地管理代码:

tbl %>% anti_join(tbl %>% filter(type == "A" & dat > min(tbl$dat[tbl$type=="B"])),by=c("type","dat"))

或者如果你真的讨厌 rbase:

tbl %>% anti_join(tbl %>% filter(type == "A" & dat > tbl %>% filter(type == "B") %>% pull(dat) %>% min()),by=c("type","dat"))

【讨论】:

  • 对不起,我的问题可能不够清楚。我的意思是最小 PER ID,而不是全局最小值。但是您的anti_join 想法(我第一次听说这种方法,谢谢!)给了我一条可以遵循的道路。
  • 然后只需添加一个“group_by(id)”然后(可能)从条件中删除“type ==“B””,那么我想你会得到你想要的
猜你喜欢
  • 1970-01-01
  • 2022-01-22
  • 2022-08-11
  • 1970-01-01
  • 1970-01-01
  • 2021-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多