首页 > 解决方案 > 根据组属性过滤组中的行

问题描述

假设我有一个带有分组变量和逻辑变量的小标题,该变量指示一行是否是该组的主要响应。

我想做以下事情:

  1. 如果 a 中的任何行group被标记为is_primary保留该行但组中的其他行都没有
  2. 如果没有行group标记为is_primary,则保留所有行
  3. 根据上述过滤行

以下是一些示例数据:

library(tidyverse)
data <- tibble(group=c("A","A","A","B","B","C","C","C","C"),
               is_primary=c(FALSE, FALSE, FALSE,FALSE,TRUE,FALSE,FALSE,TRUE,TRUE),
               value=c(1,2,3,4,5,6,7,8,9))

在上面的示例中,我想保留所有A行,因为没有带有的行,is_primary==TRUE只保留第二B行,并保留最后两C行。

我认为明显的解决方案是:

data %>%
  group_by(group) %>%
  mutate(keep_row=ifelse(any(is_primary),is_primary,TRUE))

但这会导致以下结果,不符合上述标准。

# A tibble: 9 x 4
# Groups:   group [3]
  group is_primary value keep_row
  <chr> <lgl>      <dbl> <lgl>   
1 A     FALSE          1 TRUE    
2 A     FALSE          2 TRUE    
3 A     FALSE          3 TRUE    
4 B     FALSE          4 FALSE   
5 B     TRUE           5 FALSE   
6 C     FALSE          6 FALSE   
7 C     FALSE          7 FALSE   
8 C     TRUE           8 FALSE   
9 C     TRUE           9 FALSE 

但是,如果我创建一个指示该组是否具有主键的中间变量,它就可以工作。

data %>%
  group_by(group) %>%
  mutate(has_primary=ifelse(any(is_primary),TRUE,FALSE)) %>%
  mutate(keep_row=ifelse(has_primary,is_primary,TRUE))

这导致keep_row正确:

# A tibble: 9 x 5
# Groups:   group [3]
  group is_primary value has_primary keep_row
  <chr> <lgl>      <dbl> <lgl>       <lgl>   
1 A     FALSE          1 FALSE       TRUE    
2 A     FALSE          2 FALSE       TRUE    
3 A     FALSE          3 FALSE       TRUE    
4 B     FALSE          4 TRUE        FALSE   
5 B     TRUE           5 TRUE        TRUE    
6 C     FALSE          6 TRUE        FALSE   
7 C     FALSE          7 TRUE        FALSE   
8 C     TRUE           8 TRUE        TRUE    
9 C     TRUE           9 TRUE        TRUE

ifelse第一个解决方案不起作用是怎么回事?

标签: rdplyr

解决方案


if/else当“is_primary”中没有 TRUE 元素时,我们可以使用条件返回行,或者else仅返回“is_primary”为 TRUE 的行

library(dplyr)
data %>%
    group_by(group) %>%
    filter(if(!any(is_primary)) TRUE else is_primary)
# A tibble: 6 x 3
# Groups:   group [3]
#  group is_primary value
#  <chr> <lgl>      <dbl>
#1 A     FALSE          1
#2 A     FALSE          2
#3 A     FALSE          3
#4 B     TRUE           5
#5 C     TRUE           8
#6 C     TRUE           9

也可以通过|条件来完成

data %>%
   group_by(group) %>%
   filter(!any(is_primary) | is_primary)
# A tibble: 6 x 3
# Groups:   group [3]
#  group is_primary value
#  <chr> <lgl>      <dbl>
#1 A     FALSE          1
#2 A     FALSE          2
#3 A     FALSE          3
#4 B     TRUE           5
#5 C     TRUE           8
#6 C     TRUE           9

或者另一种选择是

data %>%
  group_by(group) %>%
  filter(sum(is_primary) == 0 | is_primary)
# A tibble: 6 x 3
# Groups:   group [3]
#  group is_primary value
#  <chr> <lgl>      <dbl>
#1 A     FALSE          1
#2 A     FALSE          2
#3 A     FALSE          3
#4 B     TRUE           5
#5 C     TRUE           8
#6 C     TRUE           9

或使用slice

data %>% 
  group_by(group) %>% 
  slice(if(!any(is_primary)) row_number() else which(is_primary))

上面的一个data.table选项是

library(data.table)
setDT(data)[data[, .I[!any(is_primary)|is_primary], by = group]$V1]

或使用base R

data[with(data, !ave(is_primary, group, FUN = any) | is_primary),]

的问题ifelse是,根据?ifelse

ifelse(测试,是,否)

如果是或否太短,它们的元素将被回收。当且仅当任何测试元素为真时才会评估 yes ,并且类似地为 no。

在OP的代码中

 ifelse(any(is_primary),TRUE,FALSE)

any返回 1 的逻辑向量length。根据?any

该值是长度为 1 的逻辑向量。

根据上面的ifelse文档,这些值被回收


推荐阅读