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

问题描述

我有一个像这样的数据框:

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",!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 这样更符合 tidyverse 习惯的结构中时,有没有办法过滤 l

我考虑过这个 SO answer,但是,表达式不是动态的。

解决方法

我们可以在 across 中使用 filter 来循环 'l' 的 names,通过使用列名({{1 }}) 和否定 (cur_column())。请注意,! 目前仅适用于 cur_column() 而不适用于 acrossif_all/if_any -dplyr1.0.6

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_all

或者用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
,

这里的另一个潜在选择是使用 purrr 创建一个逻辑向量,该向量允许 &| 条件,并且可以访问当前列名 (.y) 而无需cur_column,只能在 across 内使用:

df %>% 
  filter(imap(l,~ !df[[.y]] %in% .x) %>% reduce(`&`)) # can use magrittr::and

输出

   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

变体是:

df %>% 
  filter(imap(l,~ !df[[.y]] %in% .x) %>% reduce(`|`)) # can use magrittr::or

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...