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 - 如何使用准引号动态构造变异函数名称的主要内容,如果未能解决你的问题,请参考以下文章