我的数据如下:
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))
我需要为每个地区和时间点生成人口变化的比率。例如,Time == 2
的Ratio_2
将是由Region
分组的Age_2
(在Time == 2
处(/Age_1
(在Time == 1
处(。我可以手动输入:
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(.[?])
的部分需要引用数据帧中相对于.
的前一列,但我还没有找到这样做的方法
注意:这个问题与昨天的一篇帖子有关,我在帖子中试图解决手头的问题,因为数据是长格式的。不过,以广泛的形式进行是一个不同的问题,这就是我打开这个问题的原因。
这里有一个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))