dplyr中跨组的中间/高级过滤

问题描述

我主动帮助遇到问题的朋友,并很快意识到这超出了我的能力。我有兴趣进行过滤以删除属于另一个组的第一条记录或在其后的某个组的记录。

我正在按“物种”,“年”和“性别”分组,并希望删除在“ m”的第一个“ observation_doy”之后出现的“ sex”为“ f”的记录。在此示例中,我要删除的记录以粗体显示

library(tidyverse)
library(janitor)

原始

    species               generations  year  observation_doy sex
1   Linnaea borealis       partially bimodal    2009    165 f
2   Linnaea borealis       partially bimodal    2010    150 f
3   Linnaea borealis       partially bimodal    2010    155 f
4   Linnaea borealis       partially bimodal    2010    160 m
**5 Linnaea borealis       partially bimodal    2010    160 f**
6   helianthus deserticola  partially bimodal   2009    174 f
7   helianthus deserticola  partially bimodal   2009    174 f
8   helianthus deserticola  partially bimodal   2009    180 m
**9 helianthus deserticola  partially bimodal   2009    180 f**
10  helianthus deserticola  partially bimodal   2009    184 m
11  helianthus deserticola  partially bimodal   2010    174 f
12  helianthus deserticola  partially bimodal   2010    174 f
**13    helianthus deserticola  partially bimodal   2010    180 f**
14  helianthus deserticola  partially bimodal   2010    180 m
15  helianthus deserticola  partially bimodal   2010    184 m
16  helianthus deserticola  partially bimodal   2011    174 f
17  helianthus deserticola  partially bimodal   2011    174 f
18  helianthus deserticola  partially bimodal   2011    180 f
19  helianthus deserticola  partially bimodal   2011    180 m
**20    helianthus deserticola  partially bimodal   2011    184 f
21  helianthus deserticola  partially bimodal   2011    184 f**
22  helianthus bolanderi    partially bimodal   2009    174 f
23  helianthus bolanderi    partially bimodal   2009    174 f
24  helianthus bolanderi    partially bimodal   2009    180 m
**25    helianthus bolanderi    partially bimodal   2009    180 f**
26  helianthus bolanderi    partially bimodal   2009    184 m

期望的结果:

    
  species                generations       year observation_doy sex 
1   Linneae borealis        partially bimodal   2009    165 f
2   Linneae borealis        partially bimodal   2010    150 f
3   Linneae borealis        partially bimodal   2010    155 f
4   Linneae borealis       partially bimodal    2010    160 m
5   helianthus deserticola  partially bimodal   2009    174 f
6   helianthus deserticola  partially bimodal   2009    174 f
7   helianthus deserticola  partially bimodal   2009    180 m
8   helianthus deserticola  partially bimodal   2009    180 f
9   helianthus deserticola  partially bimodal   2009    184 m
10  helianthus deserticola  partially bimodal   2010    174 f
11  helianthus deserticola  partially bimodal   2010    174 f
12  helianthus deserticola  partially bimodal   2010    180 m
13  helianthus deserticola  partially bimodal   2010    184 m
14  helianthus deserticola  partially bimodal   2011    174 f
15  helianthus deserticola  partially bimodal   2011    174 f
16  helianthus deserticola  partially bimodal   2011    180 m
17  helianthus bolanderi    partially bimodal   2009    174 f
18  helianthus bolanderi    partially bimodal   2009    174 f
19  helianthus bolanderi    partially bimodal   2009    180 m
20  helianthus bolanderi    partially bimodal   2009    184 m

原始数据集大约有10列10k记录,因此非常易于管理。但是,我似乎没有解决此问题的好方法。以下是我尝试过的一些方法-为什么我怀疑它们失败了;我再次怀疑这是无效的方法

按组,过滤器和切片查找第一个雄性出现日期非常容易。我怀疑之后是否可以创建一个数字字符串,并使用%notin%(取反%in%)来删除该日期之后的女性记录。但是我不知道如何将%notin%限制为该子集,除非将数据集过滤掉。但是,这种方法似乎效果不佳,会制造出多种中间体。

first_male_emergence <- df %>% dplyr::filter(generations == 'partially bimodal') %>% 
  dplyr::group_by(species,year,sex) %>% 
  dplyr::filter(sex == 'm') %>% 
  dplyr::slice_min(sampling_doy,n=1)

我也曾尝试创建一个“双面”滤镜,这似乎与dplyr哲学不符。我认为这种方法的问题是使dplyr识别主要的过滤条件,在这种情况下为'

clean_df <- raw_df %>%  
  group_by(species,sex) %>%
  dplyr::filter(sex == 'f' & sampling_doy <= print(filter(sex == 'm',(slice_min(sampling_doy,n = 1)))))

请注意,我在右边的表达式上进行了“打印”,以尝试返回用于

最后,我尝试使用case_when,但是在允许该函数确定LHS和RHS归因于许多运算符的地方也存在问题。

clean_df <- raw_df %>% 
  group_by(species,sex) %>% 
  mutate(sampling_doy_1 = case_when(
    (sex == 'f' & sampling_doy <=
       filter(sex == 'm',(slice_max(sampling_doy,n = 1)) ~ sampling_doy,(sex == 'f' & sampling_doy >= 
       filter(sex == 'm',n = 1)) ~ NA,))))))

我也在此尝试了一个变体:

clean_df <- raw_df %>% 
  dplyr::filter(generations == 'partially bivoltine') %>% 
  group_by(species,sex) %>% 
  mutate(sampling_doy_1 = case_when(
  sex == 'f' & sampling_doy < sex == 'm',sampling_doy ~ sampling_doy,sex == 'f' & sampling_doy > sex == 'm',sampling_doy ~ NA,))

我还考虑过在分组依据中使用“排列”,并尝试在第一个男性记录之前对所有女性记录进行切片。但是,这似乎不是一个方法

所以我的第一个问题是:谁能解决这个问题?在我看来,使用case_when并将左手表达式转换为在case_when内调用函数是最好的方法。但是,与此同时,我觉得从根本上说,这并不是基于我发现的其他示例设置使用case_when的方式。有时我会把一些非常简单的数学运算混入其中,但是通常情况下,相当简单的数学运算在感兴趣的列之间作用相同。

第二个问题是:对于这个主题,是否有一种普遍建议的方法,还是我必须依靠编写函数来完成类似的事情?

对这篇文章的长度深表歉意,但是对您的帮助非常感谢。我还标记了数据表,因为它似乎有一个不错的简单解决方案。

解决方法

这可以通过联接来完成。

male_first_obs <- my_data %>%
  group_by(species,year) %>%
  filter(sex == "m") %>%
  summarize(male_first_obs_doy = min(observation_doy))
 
my_data %>%
  left_join(male_first_obs,by = c(species,year)) %>%
  group_by(species,year) %>%
  filter(!(sex == "f" & observation_day > male_first_obs_doy)) %>%
  select(-male_first_obs_doy)

如果您不关心dbplyr的兼容性等,则可能会更加简洁,例如:

my_data %>%
  group_by(species,year) %>%
  mutate(male_first_obs_day = min(observation_doy[which(sex == "m")])) %>%
  filter(!(sex == "f" & observation_day > male_first_obs_doy)) %>%
  select(-male_first_obs_doy)