如何在R语言For Loop中存储ID与名称并生成对应命名的Excel文件
解决方案
问题根源
你的代码核心问题在于遍历逻辑错误:cust_list是一个2行2列的数据框,1:length(cust_list)返回的是列数(1:2),虽然循环次数刚好等于目标行数,但索引数据的方式不够严谨,且容易在数据结构变化时出错。
修正后的For循环版本
以下代码修复了遍历逻辑,同时优化了代码可读性:
library(tidyverse) library(janitor) library(writexl) df.1 <- tribble( ~customer_id, ~cust_nm ,~period, ~cost1, ~cost2 , 'cust1', 'pizzaco', '202201', 5, 10, 'cust2', 'sodaco','202202', 5, 10, 'cust1', 'pizzaco', '202203', 5, 10, 'cust2', 'sodaco', '202204', 5, 10, ) # 用distinct()替代unique(),更符合tidyverse编码习惯 cust_list <- df.1 %>% select(customer_id, cust_nm) %>% distinct() # 按数据框的行数遍历,而非列数 for (i in seq(nrow(cust_list))) { # 直接提取当前行的客户ID和名称,索引更清晰 current_cust_id <- cust_list$customer_id[i] current_cust_nm <- cust_list$cust_nm[i] test <- df.1 %>% filter(customer_id == current_cust_id) %>% group_by(customer_id, cust_nm, period) %>% summarise( cost1 = sum(cost1, na.rm = TRUE), cost2 = sum(cost2, na.rm = TRUE), total = cost1 + cost2, .groups = "drop" # 显式取消分组,避免后续警告 ) %>% adorn_totals(where = 'row') write_xlsx(test, paste0("test_", current_cust_nm, ".xlsx")) }
更简洁的Tidyverse风格实现
如果想摆脱手动循环,可使用group_split + map2组合,无需单独创建cust_list:
library(tidyverse) library(janitor) library(writexl) df.1 <- tribble( ~customer_id, ~cust_nm ,~period, ~cost1, ~cost2 , 'cust1', 'pizzaco', '202201', 5, 10, 'cust2', 'sodaco','202202', 5, 10, 'cust1', 'pizzaco', '202203', 5, 10, 'cust2', 'sodaco', '202204', 5, 10, ) df.1 %>% # 按客户分组后拆分为单个数据框列表 group_by(customer_id, cust_nm) %>% group_split() %>% # 同时遍历数据框和对应的客户名称 map2(., map(., ~ unique(.$cust_nm)), function(data, cust_name) { data %>% group_by(period) %>% summarise( cost1 = sum(cost1, na.rm = TRUE), cost2 = sum(cost2, na.rm = TRUE), total = cost1 + cost2, .groups = "drop" ) %>% # 添加总计行 add_row( customer_id = unique(data$customer_id), cust_nm = cust_name, period = "Total", cost1 = sum(.$cost1), cost2 = sum(.$cost2), total = sum(.$total) ) %>% # 写入Excel write_xlsx(paste0("test_", cust_name, ".xlsx")) })
关键优化点
- 用
seq(nrow(cust_list))替代1:length(cust_list),确保遍历的是数据框的行数而非列数 - 直接用
$索引提取客户信息,避免复杂的矩阵式索引,提升代码可读性 - 在
summarise中添加.groups = "drop",显式取消分组,避免后续操作的潜在警告 - 用
distinct()替代unique(),更贴合tidyverse的编码规范
内容的提问来源于stack exchange,提问作者cowboy
相关产品推荐
相关产品推荐

