You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在dplyr的mutate中使用case_when:为何计算时而用行值时而用整列?

问题:dplyr中按行取两变量最小值的问题

我需要在两个变量满足条件时创建新变量,取另外两个变量的每行较小值(实际是日期,这里用数值简化示例)。但用first(sort(c(var1,var2)))时,得到的是整个数据框中这两个变量的全局最小值,而非每行的最小值;但var1 * var2却能按行计算。

测试代码如下:

library(dplyr)
starwars %>% 
  mutate(
    new_var = 
      case_when(
       hair_color == "brown" & eye_color == "brown" ~ first(sort(c(birth_year, mass)))
      ),
    new_var2 = 
      case_when(
        hair_color == "brown" & eye_color == "brown" ~ birth_year * mass 
      )
   ) %>% 
  select ( name, hair_color, eye_color, mass, birth_year, new_var, new_var2) %>% 
  filter(hair_color == "brown" & eye_color == "brown")

简化输出:

# A tibble: 9 × 7
  name                  hair_color eye_color  mass birth_year new_var new_var2
  <chr>                 <chr>      <chr>     <dbl>      <dbl>   <dbl>    <dbl>
1 Leia Organa           brown      brown        49         19       8      931
2 Han Solo              brown      brown        80         29       8     2320
3 Arvel Crynyd          brown      brown        NA         NA       8       NA
4 Wicket Systri Warrick brown      brown        20          8       8      160

原因解释

  • c(birth_year, mass)会把两列的所有值合并成一个全局向量,而非按行配对。之后sort()对这个全局向量排序,first()取排序后的第一个值,也就是整个数据框中birth_year和mass的全局最小值(此处为8),因此所有符合条件的行都得到同一个值。
  • birth_year * mass属于dplyr支持的向量化逐元素运算:每一行的birth_year和mass对应相乘,自然得到每行的结果。

实现按行取最小值的方法

方法1:使用pmin()函数(推荐)

pmin()是R内置的向量化函数,专门用于按行取多个向量的最小值,默认保留NA,可通过na.rm=TRUE调整NA处理逻辑。

修改后的代码:

library(dplyr)
starwars %>% 
  mutate(
    new_var = 
      case_when(
       hair_color == "brown" & eye_color == "brown" ~ pmin(birth_year, mass, na.rm = FALSE)
      ),
    new_var2 = 
      case_when(
        hair_color == "brown" & eye_color == "brown" ~ birth_year * mass 
      )
   ) %>% 
  select(name, hair_color, eye_color, mass, birth_year, new_var, new_var2) %>% 
  filter(hair_color == "brown" & eye_color == "brown")

运行后new_var将得到每行对应值的较小值:

  • Leia Organa:min(19,49)=19
  • Han Solo:min(29,80)=29
  • Arvel Crynyd:NA(两列均为NA)
  • Wicket Systri Warrick:min(8,20)=8

方法2:使用rowwise()(适合复杂行操作)

如果需要执行更复杂的行级运算,可以用rowwise()让dplyr按行处理数据,注意处理后要取消分组:

library(dplyr)
starwars %>% 
  rowwise() %>%
  mutate(
    new_var = 
      case_when(
       hair_color == "brown" & eye_color == "brown" ~ first(sort(c(birth_year, mass)))
      ),
    new_var2 = 
      case_when(
        hair_color == "brown" & eye_color == "brown" ~ birth_year * mass 
      )
   ) %>% 
  ungroup() %>% # 务必取消按行分组,避免后续运算异常
  select(name, hair_color, eye_color, mass, birth_year, new_var, new_var2) %>% 
  filter(hair_color == "brown" & eye_color == "brown")

注意:rowwise()会降低运算效率,数据量大时优先使用pmin()这类向量化函数。


内容的提问来源于stack exchange,提问作者PStaus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 02:05:32