当前位置: 首页 > 知识库问答 >
问题:

为什么R中只有‘case _ when’给出不同的结果?

蓟辰沛
2023-03-14

当我在本文中使用<code>dplyr::case_when<code>而不是<code>if<code>时,我注意到了下面的这种行为。如果第二个分支的输出是一个显式字符串,它将按预期工作,但如果指定了<code>x</code>本身,结果将发生变化。

为什么只有< code>case_when给出不同的结果?

x <- character(0)

dplyr::case_when(rlang::is_empty(x) ~ "Empty", !rlang::is_empty(x) ~ "Not empty")
#> [1] "Empty"
dplyr::case_when(rlang::is_empty(x) ~ "Empty", !rlang::is_empty(x) ~ x)
#> character(0)

if (rlang::is_empty(x)) "Empty" else if (!rlang::is_empty(x)) "Not empty"
#> [1] "Empty"
if (rlang::is_empty(x)) "Empty" else if (!rlang::is_empty(x)) x
#> [1] "Empty"

ifelse(rlang::is_empty(x), "Empty", "Not empty")
#> [1] "Empty"
ifelse(rlang::is_empty(x), "Empty", x)
#> [1] "Empty"

由reprex软件包(v2.0.1)于2022年8月16日创建

共有3个答案

薛烨霖
2023-03-14

这是因为< code>case_when试图将所有公式循环到一个共同的长度。文件上说:

LHS 和 RHS 的长度可能相同,为 1 或 n。n 的值在所有情况下都必须一致。n == 0 的情况被视为 n != 1 的变体。

在你的第二个例子中,你提供了一个长度为0的向量作为其中一个公式的RHS,这就是返回长度为0的向量的原因;只是巧合,返回值和false case的RHS一样。

在考虑以下示例时,此行为似乎不那么令人惊讶:

library(dplyr, warn.conflicts = FALSE)

case_when(TRUE ~ 1, FALSE ~ numeric(0))
#> numeric(0)

case_when(TRUE ~ 1, FALSE ~ numeric(1))
#> [1] 1

case_when(TRUE ~ 1, FALSE ~ numeric(2))
#> [1] 1 1

case_when(TRUE ~ 1, FALSE ~ numeric(3))
#> [1] 1 1 1
季博
2023-03-14

case_whenifelse之间的一个很大区别是,case_when将右侧参数强制为相同的类型。从 ?case_when

RHS 不需要是合乎逻辑的,但所有 RHS 的计算结果都必须为相同类型的向量。

它还可能引发错误。

dplyr::case_when(rlang::is_empty(x) ~ 1, !rlang::is_empty(x) ~ "2")
# Error in names(message) <- `*vtmp*` : 
#   'names' attribute [1] must be the same length as the vector [0]
dplyr::case_when(rlang::is_empty(x) ~ "1", !rlang::is_empty(x) ~ "2")
[1] "1"

这些信息肯定可以更具体。

如前所述,这并不能解释我认为case_when源代码中的错误,特别是validate_case_when_length

m <- dplyr:::validate_case_when_length(query, value, fs, error_call = error_call)

在这种情况下会错误地返回0。

mycase_when <- function (...) 
{
  fs <- dplyr:::compact_null(rlang:::list2(...))
  n <- length(fs)
  error_call <- current_env()
  if (n == 0) {
    abort("No cases provided.", call = error_call)
  }
  query <- vector("list", n)
  value <- vector("list", n)
  default_env <- caller_env()
  quos_pairs <- dplyr:::map2(fs, seq_along(fs), dplyr:::validate_formula, default_env = default_env, 
                     dots_env = current_env(), error_call = error_call)
  for (i in seq_len(n)) {
    pair <- quos_pairs[[i]]
    query[[i]] <- eval_tidy(pair$lhs, env = default_env)
    value[[i]] <- eval_tidy(pair$rhs, env = default_env)
    if (!is.logical(query[[i]])) {
      abort_case_when_logical(pair$lhs, i, query[[i]], 
                              error_call = error_call)
    }
  }
  m <- dplyr:::validate_case_when_length(query, value, fs, error_call = error_call)
  
  print(paste('m',m))
  
  print(paste('value', value))
  print(paste('value[[1]][1]', value[[1]][1]))
  out <- value[[1]][rep(NA_integer_, m)]
  replaced <- rep(FALSE, m)
  print(paste('out 1',out))
  for (i in seq_len(n)) {
    out <- dplyr:::replace_with(out, query[[i]] & !replaced, value[[i]], 
                        NULL, error_call = error_call)
    replaced <- replaced | (query[[i]] & !is.na(query[[i]]))
  }
#  print(paste('out 2',out))
  out
}

mycase_when(rlang::is_empty(x) ~ "Empty", !rlang::is_empty(x) ~ "Not empty")
mycase_when(rlang::is_empty(x) ~ "Empty", !rlang::is_empty(x) ~ x)
mycase_when(rlang::is_empty(x) ~ "Empty", !rlang::is_empty(x) ~ "Not empty")
[1] "m 1"
[1] "value Empty"     "value Not empty"
[1] "value[[1]][1] Empty"
[1] "out 1 NA"
[1] "Empty"
mycase_when(rlang::is_empty(x) ~ "Empty", !rlang::is_empty(x) ~ x)
[1] "m 0"
[1] "value Empty"        "value character(0)"
[1] "value[[1]][1] Empty"
[1] "out 1 "
character(0)
终安和
2023-03-14

这可能是case_when或其内部助手函数中的错误。我们可以在case_when的源代码中放置浏览器,以查看在这两种情况下会发生什么。某些内部函数必须通过调用。

f <- function (...) {
    browser()
    fs <- dplyr:::compact_null(rlang::list2(...))
    n <- length(fs)
    error_call <- rlang::current_env()
    if (n == 0) {
        abort("No cases provided.", call = error_call)
    }
    query <- vector("list", n)
    value <- vector("list", n)
    default_env <- rlang::caller_env()
    quos_pairs <- purrr::map2(fs, seq_along(fs), dplyr:::validate_formula, default_env = default_env, 
                                                        dots_env = rlang::current_env(), error_call = error_call)
    for (i in seq_len(n)) {
        pair <- quos_pairs[[i]]
        query[[i]] <- rlang::eval_tidy(pair$lhs, env = default_env)
        value[[i]] <- rlang::eval_tidy(pair$rhs, env = default_env)
        if (!is.logical(query[[i]])) {
            dplyr:::abort_case_when_logical(pair$lhs, i, query[[i]], 
                                                                            error_call = error_call)
        }
    }
    m <- dplyr:::validate_case_when_length(query, value, fs, error_call = error_call)
    out <- value[[1]][rep(NA_integer_, m)]
    replaced <- rep(FALSE, m)
    for (i in seq_len(n)) {
        out <- dplyr:::replace_with(out, query[[i]] & !replaced, value[[i]], 
                                                NULL, error_call = error_call)
        replaced <- replaced | (query[[i]] & !is.na(query[[i]]))
    }
    out
}

dplyr 中的助手内部replace_with

replacer <- function (x, i, val, name, reason = NULL, error_call = rlang::caller_env()) {
    if (is.null(val)) {
        return(x)
    }
    dplyr:::check_length(val, x, name, reason, error_call = error_call)
    dplyr:::check_type(val, x, name, error_call = error_call)
    dplyr:::check_class(val, x, name, error_call = error_call)
    i[is.na(i)] <- FALSE
    if (length(val) == 1L) {
        x[i] <- val
    }
    else {
        x[i] <- val[i]
    }
    x
}

然后通过

x <- character(0)
f(rlang::is_empty(x) ~ "Empty", !rlang::is_empty(x) ~ "x")
f(rlang::is_empty(x) ~ "Empty", !rlang::is_empty(x) ~ x)

关键在于值m,即在工作情况下导致1L的值,在故障情况下是0L<代码>输出变为字符(0),而不是初始化为NA,长度为1。

已替换应为逻辑向量,指示值是否已替换。在故障情况下,rep(FALSE,0L)逻辑(0),稍后通过查询!已替换<代码>错误

当传递给替换程序时会给出一个特殊的子集操作字符(0)[logical(0)],即给出角色(0)

 类似资料:
  • 问题内容: 我尝试将matlab代码转换为numpy,并发现numpy与std函数的结果不同。 在matlab中 在numpy中 这正常吗?我应该如何处理呢? 问题答案: NumPy函数采用一个可选参数:“自由度增量”。默认情况下是。对其进行设置以获取MATLAB结果: 要添加更多上下文,在计算方差(标准偏差为平方根)时,通常将其除以我们拥有的值的数量。 但是,如果我们从较大的分布中选择元素的随机

  • 这是一个优化问题,我正试图用我使用的opl代码来解决(稍微有点扭曲)。 opl代码为我提供了两种解决方案,即:{Product12,Product31} 当我使用docplex将此代码翻译为python语言时,我使用以下代码: 我明白了: ***问题没有解决方案 我不明白为什么我有不同的结果,有人能帮我吗? 先谢谢你。 当做

  • 问题内容: 情况一: 输出: 2005年7月8日星期五00:00:00 GMT-0700(PST) 案例二: 输出: Thu Jul 07 2005 17:00:00 GMT-0700(PST) 为什么第二次解析不正确? 问题答案: 在第5版规范发布之前,该Date.parse方法完全依赖于实现(除后者返回数字而不是a之外,其他方法new Date(string)等效)。在第5版规范中,添加了该要

  • 问题内容: 查看以下代码,并请解释为什么该方法和函数给出两个不同的输出。 输出: 问题答案: 计算子字符串的非重叠出现次数: 返回substring sub 的不重叠出现的次数。 在字符串中恰好有一个这样的子字符串出现的位置:就在开头。因此计数 应该 返回。 一般来说,空字符串将匹配给定字符串中的 所有位置 ,包括开始和结束处的正确 位置 ,因此计数应 始终 为长度加1: 这是因为空字符串被认为存

  • Java 中的字节长度为 8 位。一个

  • 当我跑的时候。使用CPLEX的NET 4应用程序,我在不同的机器上得到不同的输出。在我的开发机器上,CPLEX输出一个结果(异常并卡在某个大值上),在所有其他机器上,结果都可以。 首先,我认为它与操作系统有关,因为我的开发机器上同时有视窗7 x64和视窗8 x64,所以我尝试在两个系统上运行应用程序。结果是一样的——有缺陷。 然后我试着在两台不同的台式机上运行,效果很好。我甚至在虚拟机内部进行了尝