【问题标题】:Dynamically determine if a dataframe column exists and mutate if it does动态确定数据框列是否存在,如果存在则改变
【发布时间】:2018-10-25 21:38:03
【问题描述】:

我的代码可以根据客户名称从数据库中提取和处理数据。某些客户端的数据可能不包含特定列名,例如 last_namefirst_name。对于不使用last_namefirst_name 的客户,我不在乎。对于确实使用其中任何一个字段的客户,我需要使用toupper() mutate() 这些列,以便稍后在 ETL 流程中加入这些标准化字段。

现在,我正在使用一系列 if() 语句和一些辅助函数来查看数据框的名称,然后在它们存在时进行变异。 I'm using if() statements because ifelse() is mostly vectorized and doesn't handle dataframes well.

library(dplyr)
set.seed(256)

b <- data.frame(id = sample(1:100, 5, FALSE), 
                col_name = sample(1000:9999, 5, FALSE), 
                another_col = sample(1000:9999, 5, FALSE))

d <- data.frame(id = sample(1:100, 5, FALSE), 
                col_name = sample(1000:9999, 5, FALSE), 
                last_name = sample(letters, 5, FALSE))

mutate_first_last <- function(df){

  mutate_first_name <- function(df){
    df %>%
      mutate(first_name = first_name %>% toupper())
  }

  mutate_last_name <- function(df){
    df %>%
      mutate(last_name = last_name %>% toupper())
  }


  n <- c("first_name", "last_name") %in% names(df)

  if (n[1] & n[2]) return(df %>% mutate_first_name() %>% mutate_last_name())
  if (n[1] & !n[2]) return(df %>% mutate_first_name())
  if (!n[1] & n[2]) return(df %>% mutate_last_name())
  if (!n[1] & !n[2]) return(df)

}

我得到了我期望得到的东西

> b %>% mutate_first_last()
  id col_name another_col
1 48     8318        6207
2 39     7155        7170
3 16     4486        4321
4 55     2521        8024
5 15     1412        4875
> d %>% mutate_first_last()
  id col_name last_name
1 64     7438         A
2 43     4551         Q
3 48     7401         K
4 78     3682         Z
5 87     2554         J

但这是处理此类任务的最佳方式吗?动态查看数据框中是否存在列名,如果存在则对其进行变异?在这个函数中必须有多个 if() 语句似乎很奇怪。 是否有更简化的方式来处理这些数据?

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    您可以使用mutate_atone_of,两者都来自dplyr。只有当它与c("first_name", "last_name") 之一匹配时,这才会改变列。如果不匹配,它将生成一个简单的警告,但您可以忽略或禁止它。

    library(dplyr)
    
    d %>%
      mutate_at(vars(one_of(c("first_name", "last_name")), toupper)
    
      id col_name last_name
    1 19     7461         V
    2 52     9651         H
    3 56     1901         P
    4 13     7866         Z
    5 25     9527         U
    
    # example with no match
    b %>%
      mutate_at(vars(one_of(c("first_name", "last_name"))), toupper)
    
      id col_name another_col
    1 34     9315        8686
    2 26     5598        4124
    3 17     3318        2182
    4 32     1418        4369
    5 49     4759        6680
    Warning message:
    Unknown variables: `first_name`, `last_name`
    

    这是dplyr 中的其他?select_helpers -

    这些函数允许您根据名称选择变量。

    starts_with():以前缀开头

    ends_with():以前缀结尾

    contains(): 包含一个文字字符串

    matches():匹配正则表达式

    num_range():一个数值范围,如 x01、x02、x03。

    one_of():字符向量中的变量。

    everything():所有变量。

    【讨论】:

      【解决方案2】:

      更新 dplyr 1.0.0

      dplyr 1.0 中,mutate 的作用域变体(例如_at_all)被across() 替换。

      此外,这种情况下最好的 tidy_select 助手是any_of,因为它将对存在的变量执行,但忽略那些不存在的变量(没有警告消息)。

      因此,您可以编写以下内容:

      # purrr syntax
      d %>% mutate(across(any_of(c("first_name", "last_name")), ~toupper(.x)))
      
      # function name syntax
      d %>% mutate(across(any_of(c("first_name", "last_name")), toupper))
      

      它们都返回变异的列

        id col_name last_name
      1 19     4398         Q
      2 72     1135         S
      3 54     9767         V
      4 60     4364         K
      5 35     1564         X
      

      同时

      b %>% mutate(across(any_of(c("first_name", "last_name")), toupper))
      

      忽略列并因此返回(没有警告消息):

        id col_name another_col
      1 42     7601        4482
      2 22     1773        7072
      3 47     2719        5884
      4  1     9595        5945
      5 81     8044        3927 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-06-20
        • 1970-01-01
        • 2022-06-13
        • 1970-01-01
        • 1970-01-01
        • 2012-04-15
        • 2020-01-19
        • 1970-01-01
        相关资源
        最近更新 更多