如何在R中按分组提取首个实例并将列表垂直展开为列?
问题描述
原始数据结构如下:
age_group age_division total_estimate zipcode single_age <chr> <dbl> <dbl> <dbl> <list> 1 0-4 2 10 51557 list(C(0, 0, 1, 1, 2, 2, 3, 3, 4, 4)) 2 0-4 2 10 51557 list(C(0, 0, 1, 1, 2, 2, 3, 3, 4, 4)) 3 0-4 2 10 51557 list(C(0, 0, 1, 1, 2, 2, 3, 3, 4, 4)) 4 0-4 2 10 51557 list(C(0, 0, 1, 1, 2, 2, 3, 3, 4, 4)) 5 0-4 2 10 51557 list(C(0, 0, 1, 1, 2, 2, 3, 3, 4, 4)) 6 0-4 2 10 51557 list(C(0, 0, 1, 1, 2, 2, 3, 3, 4, 4)) 7 0-4 2 10 51557 list(C(0, 0, 1, 1, 2, 2, 3, 3, 4, 4)) 8 0-4 2 10 51557 list(C(0, 0, 1, 1, 2, 2, 3, 3, 4, 4)) 9 0-4 2 10 51557 list(C(0, 0, 1, 1, 2, 2, 3, 3, 4, 4)) 10 0-4 2 10 51557 list(C(0, 0, 1, 1, 2, 2, 3, 3, 4, 4)) 11 0-4 3 15 51558 list(C(0, 0 ,0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)) 12 0-4 3 15 51558 list(C(0, 0 ,0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)) 13 0-4 3 15 51558 list(C(0, 0 ,0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)) 14 0-4 3 15 51558 list(C(0, 0 ,0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)) 15 0-4 3 15 51558 list(C(0, 0 ,0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)) 16 0-4 3 15 51558 list(C(0, 0 ,0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)) 17 0-4 3 15 51558 list(C(0, 0 ,0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)) 18 0-4 3 15 51558 list(C(0, 0 ,0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)) 19 0-4 3 15 51558 list(C(0, 0 ,0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)) 20 0-4 3 15 51558 list(C(0, 0 ,0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)) 21 0-4 3 15 51558 list(C(0, 0 ,0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)) 22 0-4 3 15 51558 list(C(0, 0 ,0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)) 23 0-4 3 15 51558 list(C(0, 0 ,0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)) 24 0-4 3 15 51558 list(C(0, 0 ,0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)) 25 0-4 3 15 51558 list(C(0, 0 ,0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)) 26 85-90 1 8 51559 list(C(0, 1, 2, 3, 4)) 27 85-90 1 8 51559 list(C(0, 1, 2, 3, 4)) 28 85-90 1 8 51559 list(C(0, 1, 2, 3, 4)) 29 85-90 1 8 51559 list(C(0, 1, 2, 3, 4)) 30 85-90 1 8 51559 list(C(0, 1, 2, 3, 4)) 31 85-90 1 8 51559 list(C(0, 1, 2, 3, 4))
需求:针对每个唯一的zipcode和age_group,将对应的首个single_age列表垂直展开为数值列(而非列表类型),同时保留其他列的对应值,最终得到每行对应一个single_age数值的结构。
当前编写的代码生成的single_age仍为列表类型:
# Function to expand age groups expand_age <- function(age_group, age_division) { age <- rep(seq(as.numeric(strsplit(age_group, "-")[[1]][1]), as.numeric(strsplit(age_group, "-")[[1]][2])), each = age_division) return(list(age)) } # Apply the function to each row of the dataframe using mutate df <- df_1 %>% group_by(zipcode) %>% mutate(single_age = list(expand_age(first(age_group), first(age_division))))
期望输出格式:
age_group age_division total_estimate zipcode single_age <chr> <dbl> <dbl> <dbl> <dbl> 1 0-4 2 10 51557 0 2 0-4 2 10 51557 0 3 0-4 2 10 51557 1 4 0-4 2 10 51557 1 5 0-4 2 10 51557 2 6 0-4 2 10 51557 2 7 0-4 2 10 51557 3 8 0-4 2 10 51557 3 9 0-4 2 10 51557 4 10 0-4 2 10 51557 4 11 0-4 3 15 51558 0 12 0-4 3 15 51558 0 13 0-4 3 15 51558 0 14 0-4 3 15 51558 1 15 0-4 3 15 51558 1 16 0-4 3 15 51558 1 17 0-4 3 15 51558 2 18 0-4 3 15 51558 2 19 0-4 3 15 51558 2 20 0-4 3 15 51558 3 21 0-4 3 15 51558 3 22 0-4 3 15 51558 3 23 0-4 3 15 51558 4 24 0-4 3 15 51558 4 25 0-4 3 15 51558 4 26 85-90 1 8 51559 85 27 85-90 1 8 51559 86 28 85-90 1 8 51559 87 29 85-90 1 8 51559 88 30 85-90 1 8 51559 89 31 85-90 1 8 51559 90
解决方案
问题分析
- 原函数
expand_age返回列表,导致生成的single_age仍为列表类型; - 分组仅按
zipcode,但实际需要按zipcode+age_group分组(不同年龄组的生成规则不同); - 仅用
mutate无法将列表展开为多行,需要配合unnest扩展行。
修改后的代码
方法一:基于原函数调整
# 修改expand_age函数,直接返回向量而非列表 expand_age <- function(age_group, age_division) { age_range <- as.numeric(strsplit(age_group, "-")[[1]]) rep(seq(age_range[1], age_range[2]), each = age_division) } # 按zipcode和age_group分组,生成single_age向量后展开行 df <- df_1 %>% group_by(zipcode, age_group) %>% # 提取每组的唯一属性值(同组内这些值一致) summarise( age_division = first(age_division), total_estimate = first(total_estimate), single_age = list(expand_age(age_group, age_division)) ) %>% # 将列表列展开为多行 unnest(single_age) %>% ungroup()
方法二:更简洁的tidyverse写法(无需单独函数)
df <- df_1 %>% # 保留每组的唯一属性记录 distinct(zipcode, age_group, age_division, total_estimate) %>% rowwise() %>% mutate( age_range = as.numeric(strsplit(age_group, "-")[[1]]), single_age = list(rep(seq(age_range[1], age_range[2]), each = age_division)) ) %>% # 展开列表为多行 unnest(single_age) %>% ungroup() %>% # 调整列顺序与期望输出一致 select(age_group, age_division, total_estimate, zipcode, single_age)
说明
- 两种方法都会先保留每组的唯一属性,避免重复处理;
- 通过
list()包装生成的年龄向量,再用unnest()将其展开为多行,最终single_age会是数值类型; - 针对
85-90这类年龄组,代码会自动根据age_group的范围生成正确的连续数值。
内容的提问来源于stack exchange,提问作者Sai Paritala
相关产品推荐
相关产品推荐

