R - 如何使用准引号动态构造变异函数名称

Posted

技术标签:

【中文标题】R - 如何使用准引号动态构造变异函数名称【英文标题】:R - How to dynamically construct a function name in mutate with quasiquotation 【发布时间】:2019-04-18 11:17:08 【问题描述】:

首先这是我关于 *** 的第一个问题,希望我能写得好。如果没有,请不要犹豫告诉我...对不起我的近似英语!

我想使用 dplyr 中的 mutate 函数来更改 data.frame 的列类型,但事先不知道新类型。因此,我想动态创建函数名称(例如“as.numeric”、“as.factor”),从另一个 data.frame 获取新类型。

这是一个具体的例子(我想做的是具有超过 100 个变量的 data.frames,所以你会明白我不想手动执行此操作!):

library(tidyverse)

df <- data.frame(Name = c("Roger", "Steve"), Age = c("40", "32"), stringsAsFactors = FALSE)
glimpse(df)

Observations: 2
Variables: 2
$ Name <chr> "Roger", "Steve"
$ Age  <chr> "40", "32"

types <- data.frame(Field = c("Name", "Age"), OldType = c("character", "character"), NewType = c("factor", "integer"), stringsAsFactors = FALSE)
glimpse(types)

Observations: 2
Variables: 3
$ Field   <chr> "Name", "Age"
$ OldType <chr> "character", "character"
$ NewType <chr> "factor", "integer"

我搜索了很长时间,发现了很多关于quasiquotation的文档,我尝试了一些东西,但始终没有得到预期的结果。这是我做了两次尝试:

# First attempt
for(i in 1:nrow(types))
  field <- types$Field[i]
  field_quo <- enquo(field)
  new_type <- paste0("as.", types$NewType[i], "(", field, ")")
  new_type_quo <- enquo(new_type)
  df <- df %>% mutate(!!field_quo := !!new_type_quo)

glimpse(df)

Observations: 2
Variables: 2
$ Name <chr> "as.factor(Name)", "as.factor(Name)"
$ Age  <chr> "as.integer(Age)", "as.integer(Age)"

=> 函数调用被视为字符串,列的值被替换而不是它们的类型。

# Second attempt
for(i in 1:nrow(types))
  field <- types$Field[i]
  field_quo <- ensym(field)
  new_type <- paste0("as.", types$NewType[i], "(", field, ")")
  new_type_quo <- ensym(new_type)
  df <- df %>% mutate(!!field_quo := !!new_type_quo)

这里出现错误:

Error in mutate_impl(.data, dots) : Binding not found: as.factor(Name).

我猜想 mutate 函数将括号中的内容视为一个完整的变量名?

我尝试了其他方法,但没有成功。我必须承认我不是 R 专家,尽管文档质量很高,但我很难完全理解准引用的概念。所以我知道我做错了,但我不知道为什么也不知道如何正确地做...有人可以帮忙吗?

谢谢!

【问题讨论】:

library(dplyr) ,parse_guess 会帮助你,df %mutate_all(parse_guess)。它会自动检测数据框的结构。 谢谢Hunaidkhan,我不知道这个功能。我试过了,在某些情况下它可以满足我的需求,但不是这个。例如,在这里,“名称”字段类型被设置为“字符”,我希望有“因素”。而且我可能想将一些字符字段强制转换为整数:我的数据有时不干净并且可能存在错误 - 例如我期望整数的字符串 - 这可能使“parse_guess”函数认为变量是字符串,而我想要整数 - 整数中的强制会将“错误”字符串转换为 NA,这适合我。不过谢谢你的回答。 【参考方案1】:

有趣的问题。我想我找到了一个使用 tidyverse 中 purrr 包中的 map2 的解决方案。

# Data
df <- data.frame(Name = c("Roger", "Steve"), Age = c("40", "32"), stringsAsFactors = FALSE)
types <- data.frame(Field = c("Name", "Age"), OldType = c("character", "character"), NewType = c("factor", "integer"), stringsAsFactors = FALSE)

library(tidyverse)

# Create a column with function names that is needed. I.e. adding as.
types <- types %>% 
  mutate(newType2 = paste0("as.", NewType))

# Then loop over column names and functions
df2 <- map2_dfc(types$Field, 
         types$newType2, 
         ~df %>% 
           select_(.x) %>% 
           mutate_all(.y)
  ) %>% as_tibble()

给你

> df2
# A tibble: 2 x 2
  Name    Age
  <fct> <int>
1 Roger    40
2 Steve    32

但是为了方便数据类型转换试试。它为每列提供了合适的数据类型。但是,它从不暗示因素。但是你可以根据需要使用 convert。

library(hablar)

df %>% 
    retype() %>%
    convert(fct(Name))

【讨论】:

感谢 davsjob,太完美了!并且作为 for 循环更快(我已经针对具有大型数据集的 for 循环(使用 taiki-sakai 的解决方案)测试了您的解决方案,它是 fastet)。【参考方案2】:

我想你想要的是get。这允许您通过将对象名称作为字符传递来检索对象,因此get('as.factor') 将返回as.factor 函数。将此融入您之前的尝试中:

for(i in 1:nrow(types)) 
    field <- sym(types$Field[i])
    typeFun <- get(paste0('as.', types$NewType[i]))
    df <- df %>% 
        mutate(!!field := typeFun(!!field))

glimpse(df)

Observations: 2
Variables: 2
$ Name <fct> Roger, Steve
$ Age  <int> 40, 32

【讨论】:

感谢@taiki-sakai,函数“get”正是我在使用for循环的上下文中所寻找的!这很好用。我不会接受您的答案作为最佳解决方案,因为下面 davsjob 的解决方案对我来说似乎更好(并且经过一些测试后更快),但我会牢记您的未来!

以上是关于R - 如何使用准引号动态构造变异函数名称的主要内容,如果未能解决你的问题,请参考以下文章

如何纠正 R 函数中的变异和过滤错误

如何使用 R 在校准图中添加黄土线、斜率和截距?

如何使用 Android R8 保留类构造函数参数名称

如何解析文本文件并使用构造函数中的文件输入来创建对象容器

ora-04091 表正在变异-

在我的情况下如何使用动态名称访问资源?