R语言按列名模式批量计算净迁移率新列的方法咨询
问题描述
现有按年份、国家维度统计,且按性别、年龄拆分的人口数、出生人口、死亡人口数据集,需要计算每一个年份-国家-性别-年龄组合对应的净迁移率,数据样例如下:
遵循数据集现有列命名规则,净迁移率计算公式为:2001_netmigration = 2001_pop - 2000_deaths + 2000_births - 2000_pop
需要对2001年至2020年的所有年份批量执行该计算,生成全部对应新列。
最初编写的实现代码如下,直接运行会报错,但手动写死实际列名运行时能得到符合预期的结果:
n <- 2001 while(n <= 2020){ aux <- aux %>% mutate(., paste0(n,"_netmigr") = paste0(n,"_pop") - paste0((n-1),"_deaths") + paste0((n-1),"_births") - paste0((n-1),"_pop"), .after = paste0(n,"_pop")) }
需要找到动态迭代指定、识别列名的正确实现方法。
解决方案
原代码失效有三个核心原因:
mutate中直接用=赋值时,等号左侧必须是固定列名,不支持字符串动态生成列名paste0()生成的是字符串值,不能直接作为列引用参与算术运算- 原循环缺少
n的自增步骤,运行后会陷入无限循环
方法1:保留宽表结构,使用dplyr动态编程语法修正循环
使用:=运算符支持动态列名赋值,通过.data[[列名字符串]]的方式引用动态列名,修正后代码:
library(dplyr) n <- 2001 while(n <= 2020){ # 提前拼接所需列名,提升代码可读性 new_col <- paste0(n, "_netmigr") curr_pop_col <- paste0(n, "_pop") prev_death_col <- paste0(n-1, "_deaths") prev_birth_col <- paste0(n-1, "_births") prev_pop_col <- paste0(n-1, "_pop") aux <- aux %>% mutate( !!new_col := .data[[curr_pop_col]] - .data[[prev_death_col]] + .data[[prev_birth_col]] - .data[[prev_pop_col]], .after = all_of(curr_pop_col) ) n <- n + 1 # 补上循环自增,避免死循环 }
方法2:转换为长表计算(更推荐,减少循环出错概率)
宽表循环容易出现列名拼接错误,可先将数据转换为长格式,按维度分组后通过偏移函数直接计算,再转回原宽表结构,代码逻辑更清晰:
library(dplyr) library(tidyr) aux_result <- aux %>% # 宽转长,拆分年份和指标名 pivot_longer( cols = matches("^\\d{4}_(pop|births|deaths)$"), names_to = c("year", "metric"), names_sep = "_", values_to = "val" ) %>% mutate(year = as.integer(year)) %>% pivot_wider(names_from = metric, values_from = val) %>% # 按非年份维度分组,排序后取上一年数据计算 group_by(country, sex, age) %>% arrange(year, .by_group = TRUE) %>% mutate(netmigr = pop - lag(deaths) + lag(births) - lag(pop)) %>% ungroup() %>% # 长转回宽,匹配原数据结构 pivot_longer(cols = c(pop, births, deaths, netmigr), names_to = "metric", values_to = "val") %>% mutate(col_name = paste0(year, "_", metric)) %>% select(-year, -metric) %>% pivot_wider(names_from = col_name, values_from = val)
内容的提问来源于stack exchange,提问作者Peter Kamal
相关产品推荐
相关产品推荐

