我所做的工作涉及在关键查找变量上合并来自不同来源的多个数据库。该变量是一个字符串变量,通常根据数据源(即“纽约市”,“纽约市”)拼写许多不同的方式。
我编写了一个简单的函数来清理每个数据集中的查找变量,并以这种方式使用它:
clean.names <- function(x){
x %>%
str_remove_all('[:punct:]') %>%
str_to_lower() %>%
str_squish() %>%
str_trim()
}
#df_1 and df_2 are dataframes, with variables key that I use to merge.
df_1 %>%
mutate(clean_name = clean.names(key1)) %>%
left_join(df_2 %>%
mutate(clean_name = clean.names(key2)),
by = 'clean_name')
此功能可以很好地完成预期的工作。但是,该代码有点冗长。我的问题是:如何创建与dplyr配合使用的函数(即无引号等),其效果与上述效果相同?我希望它是真正的dplyr形式,并在join函数中充当包装器。我已经尝试过了但没有成功,请看这里:
clean.names <- function(x =df, y = merge.vary){ # function adds
x$merge.vary <- y %>% # a new variable
str_remove_all('[:punct:]') %>% # to existing dataframe
str_to_lower() %>%
str_squish() %>%
str_trim()
}
clean.names(df_1, key1) %>% # then use the function as a wrapper
left_join(clean.names(df_2, key2))# for the dplyr join functions
有没有办法做到这一点?我想要的是一个类似dplyr的代理函数:function(dataframe,variable)。谢谢。
答案 0 :(得分:2)
您可以使用rlang::ensym()
捕获变量名并将其直接传递给mutate_at
:
library(tidyverse)
clean.names2 <- function( .df, .var ) {
f <- compose( partial(str_remove_all, pattern='[:punct:]'),
str_to_lower, str_squish, str_trim )
.df %>% mutate_at( vars(!!ensym(.var)), f )
}
该函数可以使用带引号和不带引号的变量名:
X <- tibble( Cities = c(" New York City, NY", "Denver, CO;;") )
clean.names2( X, Cities )
# # A tibble: 2 x 1
# Cities
# <chr>
# 1 new york city ny
# 2 denver co
clean.names2( X, "Cities" ) ## equivalent
简要说明:
第一行通过将str_remove_all
,str_to_lower
,str_squish
,str_trim
串在一起,并使用partial()
为pattern
的{{1}}参数。结果函数str_remove_all
与原始f
相同。 (我只是想使clean.names
成为独立的。)
第二行使用clean.names2
将新的复合函数f
(或等效地,原始clean.names
)应用于.df
中的单个列。该列是使用两个tidyverse机制指定的。第一个是mutate_at
,它允许用户指定带引号和不带引号的列名。例如,以下两行是等效的:
vars()
第二种机制属于处理quasiquoatation的函数类,它使程序员可以直接使用未求值的表达式,包括由函数用户提供的表达式。特别是,我们使用mydf %>% mutate_at( vars("mycolumn"), myfunction ) # vars can be dropped here
mydf %>% mutate_at( vars(mycolumn), myfunction )
来捕获调用环境提供给函数的符号,并将该符号传递给ensym
。 vars
很重要,因为它告诉!!
继续并计算表达式vars
并将结果用作列名。如果没有ensym(.var)
,!!
将会尝试查找名称为“ ensym(.var)”的列。