首页 > 解决方案 > 在调用 mutate() 时引用上一列/下一列

问题描述

我的数据如下所示:

set.seed(1234)
library(tidyverse)

df <- data.frame(Time = c(1,1,2,2,3,3),
                 Region = c("A", "B", "A", "B", "A", "B"),
                 Age_1 = round(rnorm(6, mean = 10),0),
                 Age_2 = round(rnorm(6, mean = 10),0),
                 Age_3 = round(rnorm(6, mean = 10),0),
                 Age_4 = round(rnorm(6, mean = 10),0),
                 Age_5 = round(rnorm(6, mean = 10),0))

我需要为每个区域和时间点生成人口变化比率。例如,Ratio_2forTime == 2将是Age_2(at Time == 2) / Age_1(at Time == 1),按 分组Region。我可以通过键入以下内容手动执行此操作:

df %>% 
  group_by(Region) %>%
  mutate(Ratio_2 = Age_2 / dplyr::lag(Age_1, order_by = Time),
         Ratio_3 = Age_3 / dplyr::lag(Age_2, order_by = Time),
         Ratio_4 = Age_4 / dplyr::lag(Age_3, order_by = Time),
         Ratio_5 = Age_5 / dplyr::lag(Age_4, order_by = Time))

df
# A tibble: 6 x 11
# Groups:   Region [2]
   Time Region Age_1 Age_2 Age_3 Age_4 Age_5 Ratio_2 Ratio_3 Ratio_4 Ratio_5
  <dbl> <chr>  <dbl> <dbl> <dbl> <dbl> <dbl>   <dbl>   <dbl>   <dbl>   <dbl>
1     1 A         11     8     9     9    10  NA       NA     NA       NA   
2     1 B         10     9    10    10    11  NA       NA     NA       NA   
3     2 A          9    10     9     8    12   0.909    1.12   0.889    1.33
4     2 B          9    10     9     9     9   1        1      0.9      0.9 
5     3 A          8    11     9     9    12   1.22     0.9    1        1.5 
6     3 B          9     9     9     9     9   1        0.9    1        1   

由于我的原始数据有很多年龄组,因此此过程涉及大量手动编码。在我看来,程序化解决方案可能如下所示:

df %>%
   group_by(Region) %>%
   mutate(across(4:7, ~ . / dplyr::lag(.[?], order_by = Time), .names="Ratio_{.col}"))

包含的部分dplyr::lag(.[?])需要引用数据框中相对于的上一列,.但我还没有找到这样做的方法。

注意:这个问题与昨天的一篇文章有​​关,我在这篇文章中试图解决手头的问题,数据是长格式的。不过,以宽格式做是一个不同的问题,这就是我打开这个问题的原因。

标签: rtidyverse

解决方案


这是一个选项across

library(dplyr)
library(stringr)
df %>% 
    group_by(Region) %>% 
    mutate(across(matches('^Age_[2-5]$'),
       ~ ./lag(get(str_replace(cur_column(), '\\d+',
       as.character(readr::parse_number(cur_column())-1))), order_by = Time ),
       .names = "Ratio_{.col}" )) %>%
    ungroup

或者可以以简化的方式完成

library(purrr)
df[str_c('Region_', 2:5)] <-  map2(df[4:7], df[3:6], 
          ~ .x/lag(.y, order_by = df$Time))

推荐阅读