如何在R中基于'total_'前缀列高效透视数据框?
解决方案
我们可以通过列分组标记+正则匹配的方式,用tidyr::pivot_longer一次性完成多组数据的透视,避免多次调用的繁琐。具体步骤如下:
1. 加载必要包
首先确保加载dplyr、tidyr和stringr包:
library(dplyr) library(tidyr) library(stringr)
2. 自动生成列分组标识
先给每个非index的列分配所属的分组(对应收入、年龄、语言三类),核心是利用cumsum对total_开头的列进行累积计数,以此区分不同组:
# 提取所有total_开头的列对应的类别(去掉total_前缀) group_categories <- str_remove(names(df)[grepl("^total_", names(df))], "^total_") names(group_categories) <- paste0("group_", seq_along(group_categories))
3. 一次性透视多组数据
先给列名添加分组前缀,再通过两次透视(长转宽+宽转长)完成结构重塑,同时自动填充每组的总计值:
result <- df %>% # 给非index列添加分组前缀(如group_1_total_income) rename_with(function(col) { group_idx <- cumsum(grepl("^total_", names(df)))[which(names(df) == col)] paste0("group_", group_idx, "_", col) }, -index) %>% # 转成长格式,拆分分组和原列名 pivot_longer( cols = -index, names_to = c("group", "variable"), names_sep = "_", extra = "merge" # 处理列名含下划线的情况(如ten_twenty_k) ) %>% # 把分组映射为对应的类别(收入/年龄/语言) mutate(category = group_categories[group]) %>% # 区分总计列和分项列,去掉总计列的total_前缀 mutate( is_total = str_starts(variable, "total_"), variable = str_remove(variable, "^total_") ) %>% # 转成宽格式,让总计值和分项值同属一行 pivot_wider( id_cols = c(index, category, variable), names_from = is_total, values_from = value, names_glue = "{ifelse(is_total, 'total_value', 'item_value')}" ) %>% # 为每组的所有行填充总计值 group_by(index, category) %>% fill(total_value, .direction = "downup") %>% ungroup() %>% # 过滤掉无分项值的行(原总计行) filter(!is.na(item_value)) %>% # 重命名列并调整顺序 rename(item = variable) %>% select(index, category, item, item_value, total_value)
处理后的result结构示例(前5行):
# A tibble: 9 × 5 index category item item_value total_value <int> <chr> <chr> <dbl> <dbl> 1 1 income ten_twenty_k 90 100 2 1 income twenty_one_thirty_k 10 100 3 1 age age_15_lower 10 1000 4 1 age age_16_30 900 1000 5 1 age age_31_50 90 1000
4. 拆分数据框并去重
按category拆分数据框,并用distinct()去除重复行:
# 按类别拆分数据框 split_dfs <- result %>% group_split(category, .keep = TRUE) # 给拆分后的列表命名 names(split_dfs) <- unique(result$category) # 对每个数据框去重 split_dfs <- lapply(split_dfs, function(df) { df %>% distinct() })
拆分后可通过split_dfs$income、split_dfs$age、split_dfs$language分别访问对应的数据框。
内容的提问来源于stack exchange,提问作者johnny_gillette
相关产品推荐
相关产品推荐

