用户定义函数中的子集,可将值分配给目标列

时间:2019-01-02 23:25:49

标签: r dataframe subset variable-assignment user-defined-functions

我想编写一个函数,该函数根据数据框中其他三个列的值替换目标列的值。

data <-data.frame("exists" = c(1,0,1,0,1,0,0,0,1,1), 
                  "specific" = c("yes", NA, "no", NA, NA, NA, NA, NA, NA, "no"), 
                  "general" = c(NA, "yes", NA, "yes", "yes", NA, "no", NA, "no", NA), 
                  "therefore" = 0) 

目标列为therefore,默认值为0。我可以通过三行子集(或嵌套的therefore语句)将值手动分配给ifelse,但是我已经看到建议避免使用ifelse分配值)。

data[data["exists"] == 0, "therefore"] <- NA

data[grepl("yes", data[["specific"]], ignore.case=T), "therefore"] <- 1       

data[data["exists"] == 1 & grepl("yes", data[["general"]], ignore.case=T), 
"therefore"] <- 1

这将提供正确的输出:

> data["therefore"]
   therefore
1          1
2         NA
3          0
4         NA
5          1
6         NA
7         NA
8         NA
9          0
10         0

我尝试将代码作为函数编写,因此我可以更轻松地将其应用于各种列:

fun <- function (doesitapply, string, speccol, gencol, target) {   

  data[data[doesitapply] == 0, target] <- NA

  data[grepl(string, data[[speccol]], ignore.case=T), target] <- 1

  data[data[doesitapply] == 1 & grepl(string, data[[gencol]], 
  ignore.case=T), target] <- 1    

}

当我使用新功能fun()时,没有引发任何错误,但是therefore与其默认值保持不变。

fun(doesitapply = "exists", string = "yes", speccol = "specific", gencol = 
"general", target = "therefore")

> data["therefore"]
   therefore
1          0
2          0
3          0
4          0
5          0
6          0
7          0
8          0
9          0
10         0

这与在用户定义的函数中使用列名进行子设置有关吗?我已尝试对函数中的所有子集实例使用[[]]而不是[],但是...

 Error in `[[<-.data.frame`(`*tmp*`, data[[doesitapply]] == 0, target,  : 
  only a single element should be replaced 

我看过this post,但发现很难将其答案应用于我的案子。指导或建议表示赞赏!

1 个答案:

答案 0 :(得分:1)

当函数中的代码在函数之外运行(设置了所有使用的变量之后)时,它会按预期工作:

doesitapply <- "exists"
string <- "yes"
speccol <- "specific"
gencol <- "general"
target <- "therefore"

data[data[doesitapply] == 0, target] <- NA
data[grepl(string, data[[speccol]], ignore.case=T), target] <- 1
data[data[doesitapply] == 1 & grepl(string, data[[gencol]], ignore.case=T), target] <- 1 

这将提供与原始(未参数化)代码相同的输出。但是,这在函数中不起作用,因为它尝试更新data的本地版本。

您可以修改函数,以在所有3行中将<-更改为<<-<-运算符始终在本地范围内分配,而<<-运算符在父环境中搜索具有该名称的现有变量。

与其分配3个以上的语句,不如使用ifelse(在这里可以接受),或者使用case_when https://www.rdocumentation.org/packages/dplyr/versions/0.7.8/topics/case_when中的dplyr函数来避免需要使用嵌套。

根据您期望的值,也可能简化测试(例如避免使用grepl)。