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

Posted

技术标签:

【中文标题】用户定义函数中的子集以将值分配给目标列【英文标题】:Subsetting within user-defined function to assign values to target column 【发布时间】:2019-01-02 23:25:48 【问题描述】:

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

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。我可以使用三行子集(或嵌套ifelse 语句,手动将值分配给therefore,但我看到了避免@987654326 的建议@ 用于赋值)。

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,但我发现很难将其答案应用于我的案例。感谢指导或建议!

【问题讨论】:

在 R 中编写具有(持久)副作用的函数通常被认为是不好的做法。尝试重写函数以将data 作为参数之一,并让它以您想要的状态返回data.frame。这样您就不必担心环境、范围和所有这些东西。 @AkselA 感谢您的反馈。以后我会避免在我的函数中硬编码参数,比如data 【参考方案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 行中的 &lt;- 更改为 &lt;&lt;-&lt;- 运算符始终在本地范围内分配,而 &lt;&lt;- 运算符搜索父环境以查找具有该名称的现有变量。

与其分配超过 3 个语句,不如使用 ifelse 可能更典型,这在此处可以接受,或者可能是来自 dplyr https://www.rdocumentation.org/packages/dplyr/versions/0.7.8/topics/case_when 的 case_when 函数,它避免了使用嵌套的需要。

也可以根据您期望的值来简化测试(例如避免grepl)。

【讨论】:

&lt;&lt;- 而不是 &lt;- 是解决方法,谢谢!它也适用于我在第二段中提到的嵌套 'ifelse' 语句。

以上是关于用户定义函数中的子集以将值分配给目标列的主要内容,如果未能解决你的问题,请参考以下文章

为啥我在实现委托以将值从子 swift 类传递给父目标 C 类时出错?

TypeScript“字符串文字”子集不能分配给重载函数中的完整集

启动对话框以获取结果以将值返回给主要活动

用于在 R 中创建和求和子集的用户定义函数

MySQL:无法更新 JSON 列以将值从浮点数转换为整数

将值从数据库传递到 vue 组件并将其分配给变量