【问题标题】:Tidyverse: case_when() not returning correct valueTidyverse:case_when() 没有返回正确的值
【发布时间】:2020-06-17 11:53:02
【问题描述】:

对于一组列,我希望检测哪一列具有最大值。如果列之间的值相等,那么我想使用权重来决定选择哪一列。我尝试使用 case_when() 来实现它,但是它不起作用。我将在下面使用示例数据集展示这个问题...

假设我有一个数据集,其中包含这三列(A、B、C),它们对应于孩子每天吃的苹果、香蕉或胡萝卜的数量。

对于我想记录消耗最多的食物的行(即价值最高的列)。如果任何列之间的值相等(例如,1 个苹果和 1 个香蕉),则应用以下排名。苹果 > 香蕉 > 胡萝卜,如果一个孩子吃 1 个苹果和 1 个香蕉,日志会显示苹果。

我尝试在 R 中使用 if_else 语句对和 case_when() 来实现这一点。但是,它不会返回正确的结果。例如,最后一行应归类为 Apple,而不是胡萝卜。我不确定我做错了什么。请为此问题提供 Tidyverse 解决方案,如果可能,请解释为什么我的方法不起作用。

library(tidyverse)

A <- c(1,1,3,3)
B <- c(2,3,1,1)
C <- c(1,1,1,2)
df <- data.frame(A,B,C)

top_food <- df %>% 
  mutate(highest = case_when(
    C > B ~ "carrot", # if carrot > banana
    C > A ~ "carrot", # if carrot > apple 
    B > A ~ "banana", # if banana > apple 
    B >= A ~ "banana", # if banana >= carrot
    A >= B ~ "apple", # if apple  >= banana
    A >= C ~ "apple" # if apple >= carrot
  )) 

> | A | B | C | HIGHEST |  |
> | 1 | 2 | 1 | banana  |  |
> | 1 | 3 | 1 | banana  |  |
> | 3 | 1 | 1 | apple   |  |
>   3 | 1 | 2 | carrot  |  |

注意事项: - 这是一个示例数据集。 - 我对具有不同功能的解决方案持开放态度,但请提供 Tidyverse 答案,因为这是我学习 R 的方式。如果可能,请解释为什么我使用 case_when() 的方法不起作用,以便我可以学习。 - 保持数据集的形状/布局很重要,以便它可以在 R 之外的软件中使用,所以请不要转换为长格式。

【问题讨论】:

  • case_when 返回第一个匹配项。在您的示例中,“胡萝卜”是第一个匹配项。 C &gt; B 返回 [1] FALSE FALSE FALSE TRUE。所以,“胡萝卜”是正确的。
  • 回复:case_when 不工作。我懂了!这是否意味着无法使用 case_when 跨 3 列/变量(例如 A、B 和 C)应用逻辑条件?
  • 我认为这是可能的。这不是case_when的问题,而是你的逻辑之一
  • 您可以执行以下操作:您可以执行以下操作: top_food % dplyr::mutate(highest = dplyr::case_when( C > A & C > B ~ "carrot" , B > A & B >= C ~ "香蕉", TRUE ~ "苹果"))

标签: r if-statement dplyr tidyverse logical-operators


【解决方案1】:

快速修复可以包含 dplyr::rowise 和 which.max()。这种方法的缺点是速度很慢。此外,我假设您的列的顺序反映了水果的排名(如果有平局,which.max 将返回第一个值)。

A <- c(1,1,3,3,1,1)
B <- c(2,3,1,1,1,2)
C <- c(1,1,1,2,1,2)

df <- data.frame(A,B,C)
labels <- c("apple","banana","carrot")

df %>%
    dplyr::rowwise() %>%
    dplyr::mutate(top=labels[which.max(c(A,B,C))]) %>%
    dplyr::ungroup()

另一种(可能更好)的方法是使用 max.col()

df <- data.frame(A,B,C)
labels <- c("apple","banana","carrot")
df %>%
    dplyr::mutate(top=labels[max.col(df,ties="first")])

还有另一个独立于您的列顺序的解决方案(现在我不在了:D):

df <- data.frame(A,B,C)

top_food <- df %>% 
    dplyr::mutate(highest = dplyr::case_when(
        C > A & C > B ~ "carrot",
        B > A & B >= C ~ "banana",
        TRUE ~ "apple"))

您使用 case_when 的方法不起作用,因为 case_when 会在找到真实条件后立即停止检查条件。由于您的第一个条件是 C>B ~“carriot”,这对于您的最后一行 (2>1) 是正确的,因此 case_when 返回“carrot”并且没有检查其他条件。

【讨论】:

    【解决方案2】:

    解决您的问题的一种方法(我稍微修改了数据以表明如果所有值都相同,它可以工作):

    library(tidyverse)
    df %>% 
      rowid_to_column() %>% 
      pivot_longer(-rowid) %>% 
      group_by(rowid) %>% 
      mutate(highest = name[value == max(value)],
             highest = case_when(var(value) == 0 & value == 1 ~ "apple",
                             var(value) == 0 & value == 2 ~ "banana",
                             var(value) == 0 & value == 3 ~ "carrot",
                             highest == "A" ~ "apple",
                             highest == "B"~ "banana",
                             highest == "C" ~ "carrot")) %>% 
      pivot_wider(names_from = name, values_from = value)
    
    # A tibble: 4 x 5
    # Groups:   rowid [4]
      rowid highest     A     B     C
      <int> <chr>   <dbl> <dbl> <dbl>
    1     1 apple       1     1     1
    2     2 banana      1     3     1
    3     3 apple       3     1     1
    4     4 apple       3     1     2
    

    数据

    A <- c(1,1,3,3)
    B <- c(1,3,1,1)
    C <- c(1,1,1,2)
    df <- data.frame(A,B,C)
    df
    

    【讨论】:

    • 这似乎是一个完美的答案。是否可以注释代码的“mutate”/“case_when”行,以便我了解正在发生的事情并因此在需要时进行任何调整?
    猜你喜欢
    • 2014-11-26
    • 2019-03-21
    • 2014-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多