【问题标题】:Using mutate in custom function with mutation condition as argument在以突变条件为参数的自定义函数中使用 mutate
【发布时间】:2021-11-11 02:44:15
【问题描述】:

是否可以构造一个函数,比如my_mut(df, condition),使得df 是一个数据框,condition 是一个描述突变的字符串,在函数的某个地方,df 的突变根据@987654325使用@?

例如,如果df 有一个foomy_mut(df, "foo = 2*foo"),那么在my_mut() 内的某处将有一行产生与df %>% mutate(foo = 2*foo) 相同的数据帧。

我设法使用evalparsefilter 做了类似的事情。

update_filt <- function(df,
                        filt,
                        col){

  sub <- df %>%
    filter(eval(parse(text = filt))) %>%
    mutate("{{col}}" := 2*{{ col }})

  remain <- df %>%
    filter(eval(parse(
                text = paste0("!(",filt,")")
                ))
           )

  return(rbind(sub, remain))
}

我不确定update_filt 函数是否无故障,但至少在某些情况下它可以工作,例如library(gapminder) date_filt(gapminder, "year == 1952", pop) 返回预期结果。

同样的技巧似乎不适用于mutate。例如,

update_mut <- function(df, mutation){
  # Evaluate mutation expression
  df %>% mutate(eval(parse(text = mutation))
}

产生类似的结果

library(gapminder)
update_mut(gapminder, "year = 2*year")
# A tibble: 1,704 × 7
   country     continent  year lifeExp      pop gdpPercap `eval(parse(text = mutation))`
   <fct>       <fct>     <int>   <dbl>    <int>     <dbl>                          <dbl>
 1 Afghanistan Asia       1952    28.8  8425333      779.                           3904
 2 Afghanistan Asia       1957    30.3  9240934      821.                           3914
 3 Afghanistan Asia       1962    32.0 10267083      853.                           3924
 4 Afghanistan Asia       1967    34.0 11537966      836.                           3934
 5 Afghanistan Asia       1972    36.1 13079460      740.                           3944
 6 Afghanistan Asia       1977    38.4 14880372      786.                           3954
 7 Afghanistan Asia       1982    39.9 12881816      978.                           3964
 8 Afghanistan Asia       1987    40.8 13867957      852.                           3974
 9 Afghanistan Asia       1992    41.7 16317921      649.                           3984
10 Afghanistan Asia       1997    41.8 22227415      635.                           3994
# … with 1,694 more rows

而不是预期的

gapminder %>% mutate(year = 2*year)

# A tibble: 1,704 × 6
   country     continent  year lifeExp      pop gdpPercap
   <fct>       <fct>     <dbl>   <dbl>    <int>     <dbl>
 1 Afghanistan Asia       3904    28.8  8425333      779.
 2 Afghanistan Asia       3914    30.3  9240934      821.
 3 Afghanistan Asia       3924    32.0 10267083      853.
 4 Afghanistan Asia       3934    34.0 11537966      836.
 5 Afghanistan Asia       3944    36.1 13079460      740.
 6 Afghanistan Asia       3954    38.4 14880372      786.
 7 Afghanistan Asia       3964    39.9 12881816      978.
 8 Afghanistan Asia       3974    40.8 13867957      852.
 9 Afghanistan Asia       3984    41.7 16317921      649.
10 Afghanistan Asia       3994    41.8 22227415      635.
# … with 1,694 more rows

【问题讨论】:

    标签: r dataframe dplyr


    【解决方案1】:

    如果您的公式总是像 originl = do_something_original(),这可能会有所帮助。(对于 dplyr 版本 >= 1.0)

    library(dplyr)
    library(stringr)
    
    update_mut <- function(df, mutation){
      xx <- word(mutation, 1)
      df %>% 
        mutate("{xx}" := eval(parse(text = mutation)))
    }
    update_mut(gapminder, "year = 2*year")
    
       country     continent  year lifeExp      pop gdpPercap
       <fct>       <fct>     <dbl>   <dbl>    <int>     <dbl>
     1 Afghanistan Asia       3904    28.8  8425333      779.
     2 Afghanistan Asia       3914    30.3  9240934      821.
     3 Afghanistan Asia       3924    32.0 10267083      853.
     4 Afghanistan Asia       3934    34.0 11537966      836.
     5 Afghanistan Asia       3944    36.1 13079460      740.
     6 Afghanistan Asia       3954    38.4 14880372      786.
     7 Afghanistan Asia       3964    39.9 12881816      978.
     8 Afghanistan Asia       3974    40.8 13867957      852.
     9 Afghanistan Asia       3984    41.7 16317921      649.
    10 Afghanistan Asia       3994    41.8 22227415      635.
    

    【讨论】:

      【解决方案2】:

      问题在于 mutate 不理解赋值,因为所有语法都在 eval 内部进行评估。所以 mutate 只是认为这是一个无名表达式,并将表达式的整个文本指定为其名称。

      避免这种情况的一种方法是 eval 整个事情,包括 mutate 动词,如下所示。

      update_mut <- function(df, mutation) {
        # Evaluate the mutation expression
        eval(parse(text = paste0("mutate(df, ", mutation, ")")))
      }
      

      另一种方法是,在 update_mut 函数中,将 mutation 参数除以 = 字符,从而获得变量的名称和表达式。因此,您可以在 mutate 中使用动态变量分配。然而,这只会做更多的事情,因为上面的代码只是解决了问题。

      【讨论】:

        【解决方案3】:
        library(dplyr, warn.conflicts = FALSE)
        my_mut <- function(df, df_filter, ...){
          df %>% 
            filter({{ df_filter }}) %>% 
            mutate(newvar = 'other function stuff',
                   ...)
        }
        
        example_df <- data.frame(a = c('zebra', 'some value'),
                                 b = 1:2)
        
        example_df %>% 
          my_mut(df_filter = a == 'some value', 
                 b = b*5)
        #>            a  b               newvar
        #> 1 some value 10 other function stuff
        

        reprex package (v2.0.1) 于 2021 年 11 月 11 日创建

        如果您不能使用...,因为您已经在函数中将它用于其他用途,您可以在调用函数时将mutation 参数包装在tibble 中。

        library(dplyr, warn.conflicts = FALSE)
        my_mut <- function(df, df_filter, mutation){
          df %>% 
            filter({{ df_filter }}) %>% 
            mutate(newvar = 'other function stuff',
                   {{ mutation }})
        }
        
        example_df <- data.frame(a = c('zebra', 'some value'),
                                 b = 1:2)
        
        example_df %>% 
          my_mut(df_filter = a == 'some value', 
                 mutation = tibble(b = b*5))
        #>            a  b               newvar
        #> 1 some value 10 other function stuff
        

        reprex package (v2.0.1) 于 2021 年 11 月 11 日创建

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-15
          • 2019-09-10
          • 2019-05-14
          • 1970-01-01
          • 2020-05-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多