R语言如何编写数据结构操作函数,用pivot_longer完成多选项问卷宽表转长表及批量处理
宽转长操作失败的核心原因
- 此前的操作混淆了两类结构完全不同的列,没有建立列与列之间的映射关系:
- 4个被重命名为v1的列,本质是多选题每个选项(1-4号)的选中状态标识,每列固定对应一个选项
- 12个带后缀的关联题列(
o_v1_1、c_v1_1等),本质是每个选项跳转后对应的三道小题作答值,每列和对应编号的选项强绑定
- 之前直接将所有非ID列一次性传入
pivot_longer,既没有拆分两类列的结构,也没有通过选项编号建立两类列的关联,再加上重名列破坏了列名自带的映射信息,完全无法匹配目标输出的逻辑。
正确的pivot_longer参数配置逻辑
整个转换不需要复杂的参数,分三步拆分处理即可:
- 优先处理多选选项的选中状态列,过滤未选中的空值,提取每列对应的选项编号
# 注意:不要提前将4个选项列重命名为重复的"v1",保留原始的v1...3、v1...4这类带列序的列名 select_long <- raw_data %>% pivot_longer( cols = starts_with("v1..."), # 精准选中4个多选选项列 values_to = "answer_dir", # 存储受访者选中的选项编号 values_drop_na = TRUE # 直接过滤未选中的空值,减少后续冗余计算 ) %>% # 通过列名位置对应关系,标记每列绑定的选项号 mutate( option_num = case_when( name == "v1...3" ~ 1, name == "v1...4" ~ 2, name == "v1...5" ~ 3, name == "v1...6" ~ 4 ) ) %>% select(seance_id, respondent, answer_dir, option_num) - 处理每个选项关联的三道小题列,同样提取每列绑定的选项编号
related_long <- raw_data %>% pivot_longer( cols = matches("(o|c|f)_v1_\\d"), # 精准匹配所有关联小题列 names_to = "criteria", # 存储小题的完整列名 values_to = "criteria_answer" # 存储小题的作答值 ) %>% # 从列名中提取绑定的选项号:比如o_v1_1的后缀1,对应选项1 mutate( option_num = str_extract(criteria, "(?<=_v1_)\\d") ) - 基于「受访者唯一标识 + 选项编号」的共同字段,将两个长表关联,即可得到目标格式的输出
final_long <- select_long %>% left_join(related_long, by = c("seance_id", "respondent", "option_num")) %>% mutate(direction = "v1") %>% # 补上目标格式中固定的题目方向列 select(seance_id, respondent, direction, answer_dir, criteria, criteria_answer)
多数据框批量自动化实现
针对嵌套在Excel列表中的30余个结构相似的数据框,不需要逐表手动写代码,将上述流程封装成通用函数,配合批量循环即可全量处理:
- 封装的通用函数会自动识别符合命名规则的列,不需要根据每个表的列数调整参数
convert_survey <- function(df) { # 自动识别两类目标列 select_cols <- colnames(df)[str_detect(colnames(df), "^v1\\.\\.\\.")] related_cols <- colnames(df)[str_detect(colnames(df), "^(o|c|f)_v1_")] # 处理多选选中状态列 select_res <- df %>% pivot_longer(all_of(select_cols), values_to = "answer_dir", values_drop_na = TRUE) %>% mutate(option_num = str_extract(name, "\\d+$")) %>% # 从列名末尾提取数字作为选项号 select(seance_id, respondent, answer_dir, option_num) # 处理关联小题列 related_res <- df %>% pivot_longer(all_of(related_cols), names_to = "criteria", values_to = "criteria_answer") %>% mutate(option_num = str_extract(criteria, "(?<=_v1_)\\d")) # 关联输出 select_res %>% left_join(related_res, by = c("seance_id", "respondent", "option_num")) %>% mutate(direction = "v1") %>% select(seance_id, respondent, direction, answer_dir, criteria, criteria_answer) } # 批量处理列表中所有数据框,最终合并为一个总表 final_all <- excel_sheet_list %>% # 即从Excel读取的、嵌套所有sheet数据的列表对象 purrr::map(convert_survey) %>% list_rbind(names_to = "source_sheet") # 自动标记每一行数据来自哪个原始sheet
注意事项:
- 原流程中
mutate_if(is.numeric, as.character)的代码完全没必要保留,提前将所有数值列批量转为字符型,会破坏数值的计算属性,影响后续分析- 不要给不同的多选选项列重命名为重复的列名,列名是
pivot_longer识别映射关系的核心依据- 只要所有数据框的列名遵循统一的命名规则,同一个函数就可以兼容全量30余个数据表的转换需求,不需要逐表修改参数
内容的提问来源于stack exchange,提问作者rg4s
相关产品推荐
相关产品推荐

