在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
相关产品推荐
相关产品推荐

