当列名称-值对存储在列表中时过滤数据框?

我有一个数据框,如:

df <- tibble::rownames_to_column(USArrests, "State") %>% 
  tidyr::pivot_longer(cols = -State)

head(df)
# A tibble: 6 x 3
  State   name     value
  <chr>   <chr>    <dbl>
1 Alabama Murder    13.2
2 Alabama Assault  236  
3 Alabama UrbanPop  58  
4 Alabama Rape      21.2
5 Alaska  Murder    10  
6 Alaska  Assault  263  

在一个单独的列表对象中,l我有列,我需要从数据框中删除。元素名称是列名称,值对应于我要删除的行:

l <- list(State = c("Alabama", "Pennsylvania", "Texas"),
          name = c("Murder", "Assault"))

硬编码它会这样做:

dplyr::filter(df, !State %in% c("Alabama", "Pennsylvania", "Texas"), !name %in% c("Murder", "Assault"))

   State      name     value
   <chr>      <chr>    <dbl>
 1 Alaska     UrbanPop  48  
 2 Alaska     Rape      44.5
 3 Arizona    UrbanPop  80  
 4 Arizona    Rape      31  
 5 Arkansas   UrbanPop  50  
 6 Arkansas   Rape      19.5
 7 California UrbanPop  91  
 8 California Rape      40.6
 9 Colorado   UrbanPop  78  
10 Colorado   Rape      38.7
# ... with 84 more rows

但是,l经常更改,因此我不能/不想进行硬编码。我尝试了以下操作,但只计算最后一个表达式:

library(purrr)
filter_expr <- imap_chr(l, ~ paste0("! ", 
                     .y, 
                     " %in% c("", 
                     paste(.x, collapse = "",""), 
                     "")")) %>% parse(text = .)

filter(df, eval(filter_expr))

   State      name     value
   <chr>      <chr>    <dbl>
 1 Alabama    UrbanPop  58  
 2 Alabama    Rape      21.2
 3 Alaska     UrbanPop  48  
 4 Alaska     Rape      44.5
 5 Arizona    UrbanPop  80  
 6 Arizona    Rape      31  
 7 Arkansas   UrbanPop  50  
 8 Arkansas   Rape      19.5
 9 California UrbanPop  91  
10 California Rape      40.6
# ... with 90 more rows

df当过滤条件存储在类似ltidyverse 更惯用的结构中时,有没有办法进行过滤?

我认为这个SO answer,但是,表达式不是动态的。

回答

我们可以使用acrossfilter循环过names的子集化利用列名(该键的“L”为“l”,创建的逻辑表达式cur_column())和否定(!)。请注意,cur_column()目前仅适用于across和不适用if_all/if_anydplyr- 1.0.6on R 4.1.0

library(dplyr)
df %>% 
   filter(across(all_of(names(l)), ~ !. %in% l[[cur_column()]]))

-输出

# A tibble: 94 x 3
#   State      name     value
#   <chr>      <chr>    <dbl>
# 1 Alaska     UrbanPop  48  
# 2 Alaska     Rape      44.5
# 3 Arizona    UrbanPop  80  
# 4 Arizona    Rape      31  
# 5 Arkansas   UrbanPop  50  
# 6 Arkansas   Rape      19.5
# 7 California UrbanPop  91  
# 8 California Rape      40.6
# 9 Colorado   UrbanPop  78  
#10 Colorado   Rape      38.7
# … with 84 more rows

我们可以利用if_allif 我们可以设置一个属性

library(magrittr)
df %>% 
  mutate(across(all_of(names(l)), ~ set_attr(., 'cn', cur_column()))) %>% 
  filter(if_all(all_of(names(l)), ~ ! . %in% l[[attr(., 'cn')]]))

或与 imap/reduce

library(purrr)
df %>%
    filter(imap(l, ~ !cur_data()[[.y]] %in% .x) %>%
                 reduce(`&`))

或者另一种选择是 anti_join

for(nm in names(l)) df <- anti_join(df, tibble(!! nm := l[[nm]]))


以上是当列名称-值对存储在列表中时过滤数据框?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>