【问题标题】:Applying if else statements involving two dataframe columns in R在 R 中应用涉及两个数据框列的 if else 语句
【发布时间】:2020-12-17 17:10:18
【问题描述】:

我正在尝试修改包含两列的数据框,以添加第三个,根据其他列的内容(即每个列的内容是正数还是负数)返回四个可能的表达式。

我尝试了几种方法,dplyr 和 sapply 中的 'mutate' 函数。不幸的是,当我收到错误“条件的长度> 1并且只使用第一个元素”时,我似乎遗漏了一些东西。因此,只有第一次迭代会应用于新列中的每一行。

一个可重现的例子(我尝试过的变异方法)如下:

Costs <- c(2, -5, -7, 3, 12)
Outcomes <- c(-2, 5, -7, 3, -2)

results <- as.data.frame(cbind(Costs, Outcomes))
results

quadrant <- function(cost,outcome) {
        if (costs < 0 &
            outcomes < 0) {
                "SW Quadrant"
        }
        else if (costs<0 & outcomes>0){
                "Dominant"
        } 
        else if (costs>0 & outcomes<0){
                "Dominated"
        }
        else{""}
}


results <- mutate(results,Quadrant = quadrant(Costs,Outcomes)
        )

完整的警告信息是:

警告信息: 1:mutate() 输入问题Quadrant。 i 条件的长度 > 1 并且仅使用第一个元素 i 输入Quadrantquadrant(results$Costs, results$Outcomes)。 2:如果(成本 1 并且只使用第一个元素 3:mutate() 输入 Quadrant 有问题。 i 条件的长度 > 1 并且仅使用第一个元素 i 输入Quadrantquadrant(results$Costs, results$Outcomes)。 4:如果(成本 0){ : 条件的长度 > 1 并且只使用第一个元素 5:mutate() 输入 Quadrant 有问题。 i 条件的长度 > 1 并且仅使用第一个元素 i 输入Quadrantquadrant(results$Costs, results$Outcomes)。 6: 如果 (成本 > 0 & 结果 1 并且只使用第一个元素

我对 sapply 函数的尝试:

results <- sapply(results$Quadrant,quadrant(results$Costs,results$Outcomes))

导致以下错误,并与 mutate 方法一致的警告消息。

get(as.character(FUN), mode = "function", envir = envir) 中的错误: 找不到模式“功能”的对象“支配”

我确定我在这里遗漏了一些明显的东西。感谢您的任何建议。

【问题讨论】:

  • 我在下面添加了一个答案,但我认为主要问题是 if() 没有矢量化,因此评估发生在第一个元素上,而不是像 ifelse() 函数那样逐元素。

标签: r if-statement dplyr sapply


【解决方案1】:

这个函数有两个问题。

  1. 您使用cost 定义函数,但使用costs(结果相同);
  2. 您使用 if,它严格要求长度为 1 的逻辑条件,并且有两个错误:您使用 &amp;,它几乎不应该像这样暴露在 if 语句中, em> 你正在传递向量,所以cost &lt; 0 将返回一个与cost 长度相同的逻辑向量(这里大于1)。

建议:

quadrant_sgl <- function(cost, outcome) {
  if (cost < 0 && outcome < 0) return("SW Quadrant")
  if (cost < 0 && outcome > 0) return("Dominant")
  if (cost > 0 && outcome < 0) return("Dominated")
  return("")
}

quadrant_vec1 <- function(cost, outcome) {
  ifelse(cost < 0 & outcome < 0, "SW Quadrant",
         ifelse(cost < 0 & outcome > 0, "Dominant",
                ifelse(cost > 0 & outcome < 0, "Dominated",
                       "")))
}

quadrant_vec2 <- function(cost, outcome) {
  ifelse(cost < 0,
         ifelse(outcome < 0, "SW Quadrant", "Dominant"),
         ifelse(outcome < 0, "Dominated", ""))
}

quadrant_vec3 <- function(cost, outcome) {
  dplyr::case_when(
    cost < 0 & outcome < 0 ~ "SW Quadrant",
    cost < 0 & outcome > 0 ~ "Dominant",
    cost > 0 & outcome < 0 ~ "Dominated",
    TRUE ~ ""
  )
}

quadrant_vec4 <- function(cost, outcome) {
  data.table::fcase(
    cost < 0 & outcome < 0, "SW Quadrant",
    cost < 0 & outcome > 0, "Dominant",
    cost > 0 & outcome < 0, "Dominated",
    rep(TRUE, length(cost)), ""
  )
}

第一个函数 (quadrant_sgl) 将保持单操作(未向量化)的函数转换为向量化函数。如果您不熟悉向量化的概念,请知道 (1) R 做得很好,(2) R 更喜欢它,以及 (3) 这不是详细讨论这个问题的最佳场所。搜索“R vectorization”,你应该会找到很多关于这方面的资料。

因此,第一个只是演示当函数(由于时间、编程技巧或其他原因)无法转换为矢量化友好函数时该怎么做。使用Vectorize

其他功能都比较等价。

如果您正在使用dplyr 和朋友,那么我强烈推荐使用quadrant_vec3,因为它比嵌套的ifelses 更容易阅读和维护(IMO)。 (顺便说一句:如果您必须使用嵌套的ifelse,那么至少使用嵌套的dplyr::if_elses,因为它们通常比基本R 的ifelse 更安全。)

如果你正在冒险进入data.table 的世界,那么quadrant_vec4 相当于使用data.table 自己的fcase 函数,与case_when 基本相同。

演示:

Vectorize(quadrant_sgl, vectorize.args = c("cost", "outcome"))(results$Costs, results$Outcomes)
# [1] "Dominated"   "Dominant"    "SW Quadrant" ""            "Dominated"  
quadrant_vec1(results$Costs, results$Outcomes)
# [1] "Dominated"   "Dominant"    "SW Quadrant" ""            "Dominated"  
quadrant_vec2(results$Costs, results$Outcomes)
# [1] "Dominated"   "Dominant"    "SW Quadrant" ""            "Dominated"  
quadrant_vec3(results$Costs, results$Outcomes)
# [1] "Dominated"   "Dominant"    "SW Quadrant" ""            "Dominated"  

【讨论】:

  • 感谢您的回答(并为成本/成本的错字道歉,昨天漫长的一天)。我发现多个不同的示例和解释确实有助于让我了解矢量化。非常感谢!
【解决方案2】:

你可能想要更多类似的东西:

costs <- c(2, -5, -7, 3, 12)
outcomes <- c(-2, 5, -7, 3, -2)

results <- as.data.frame(cbind(costs, outcomes))

results <- results %>% mutate(Quadrant = case_when(
  outcomes < 0 & costs < 0 ~ "SW Quadrant", 
  costs < 0 & outcomes > 0 ~ "Dominant", 
  costs > 0 & outcomes < 0 ~ "Dominated", 
  TRUE ~ ""))

results
#   costs outcomes    Quadrant
# 1     2       -2   Dominated
# 2    -5        5    Dominant
# 3    -7       -7 SW Quadrant
# 4     3        3            
# 5    12       -2   Dominated

【讨论】:

  • 感谢您的回答,它运行良好,我之前没有听说过 case_when。非常感谢!
猜你喜欢
  • 1970-01-01
  • 2015-01-07
  • 2015-12-16
  • 1970-01-01
  • 2015-09-03
  • 1970-01-01
  • 2017-02-14
  • 2021-05-30
  • 1970-01-01
相关资源
最近更新 更多