编写函数:为何dplyr select()支持引号列名但df$col_name不行?
我一直在学习tidy evaluation(整洁评估)的相关内容,但实际写代码时遇到了和所学不符的情况,我的函数如下:
ttsplit = function(prepped_data, outcome_column){ sample <- sample(c(TRUE, FALSE), nrow(prepped_data), replace=TRUE, prob=c(0.7,0.3)) train <- prepped_data[sample, ] test <- prepped_data[!sample, ] Xtrain = train %>% select(!outcome_column) ytrain = train$outcome_column Xtest = test %>% select(!outcome_column) ytest = test$outcome_column return(list(Xtrain, ytrain, Xtest, ytest)) }
我的数据结构(简化版):
model1data <- structure(list(Id = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), LotArea = c(8450, 9600, 11250, 9550, 14260, 14115, 10084, 10382, 6120, 7420), SalePrice = c(208500, 181500, 223500, 140000, 250000, 143000, 307000, 2e+05, 129900, 118000)), row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame"))
问题1:为什么select()语句不用{{outcome_column}}也能正常运行?
因为你调用函数时传入的是字符串形式的列名(比如"SalePrice"),而dplyr的select()函数本身就支持直接使用字符串来指定列名。!outcome_column在这里是对字符串变量的否定选择,dplyr可以直接解析这个字符串,不需要额外用{{}}解引用。
{{}}(大括号语法)主要用于处理裸变量输入的场景——也就是当你调用函数时直接写列名(不带引号,比如ttsplit(model1data, SalePrice)),这时候需要{{}}把裸变量转换成dplyr能识别的列引用。
问题2:ytrain = train$outcome_column返回NULL,该如何正确解引用?
$是基础R的操作符,它会严格按字面量匹配列名——也就是说train$outcome_column会去找名为outcome_column的列,而不是去读取变量outcome_column里存储的字符串值,所以会返回NULL并报错。
正确的做法是用基础R的[[ ]]操作符,它支持接收字符串变量来访问列:
ytrain = train[[outcome_column]] ytest = test[[outcome_column]]
train${{outcome_column}}无效的原因是:{{}}是tidy eval的语法,仅在dplyr等支持整洁评估的函数内部生效,而$是基础R的操作符,不识别这种语法。
问题3:为什么函数外直接运行df$'column_name'能正常工作?
在基础R中,$操作符支持直接使用字符串字面量来指定列名。当你写model1data$'SalePrice'时,引号里的SalePrice会被直接当作列名去匹配,这是$操作符的原生语法,和函数内部的变量引用场景完全不同——这里没有变量替换的过程,直接是字面量匹配。
内容的提问来源于stack exchange,提问作者Maggie

