如何在R语言中重构调查数据的数据框,将位置列转换为项目评分列?
如何在R中重塑李克特量表调查数据的结构
很高兴帮你解决这个数据重塑的问题!你的需求是把记录答题顺序和评分的宽格式数据,转换成以每个项目为列的结构化数据,这在R里有几种常用的实现方式,下面我给你详细演示:
方法一:使用tidyverse工具包(推荐,代码更直观)
tidyverse里的tidyr包专门处理数据重塑,配合dplyr的管道操作,步骤清晰易读:
步骤1:加载包并准备原始数据
# 安装并加载tidyverse(如果还没安装的话) # install.packages("tidyverse") library(tidyverse) # 你的原始数据框 df <- data.frame(userid = c(1, 2, 3), pos1 = c("itemA_1", "itemB_1", "itemA_2"), pos2 = c("itemB_1", "itemC_2", "itemC_1"), pos3 = c("itemC_5", "itemA_4", "itemB_3") )
步骤2:将宽格式转为长格式
把pos1、pos2、pos3这些列合并成一列,方便后续拆分:
df_long <- df %>% pivot_longer( cols = starts_with("pos"), # 选择所有以pos开头的列 names_to = "position", # 原列名存到position列 values_to = "item_score" # 原列值存到item_score列 )
步骤3:拆分项目与评分
把item_score列里的itemX_Y拆分成项目名(item)和评分(score),并把评分转为数值型:
df_split <- df_long %>% separate( item_score, into = c("item", "score"), # 拆分后生成两列 sep = "_" # 按下划线拆分 ) %>% mutate(score = as.integer(score)) # 把评分转成整数
步骤4:转回目标宽格式
把每个项目作为单独的列,每个用户的评分对应到相应列:
df_final <- df_split %>% pivot_wider( names_from = item, # 用item列的值作为新列名 values_from = score # 用score列的值填充新列 )
运行后得到的结果:
> df_final # A tibble: 3 × 4 userid itemA itemB itemC <dbl> <int> <int> <int> 1 1 1 1 5 2 2 4 1 2 3 3 2 3 1
注:你提供的示例结果里部分评分和原始数据不符(比如用户1的itemB评分应为1而非3),上面是基于你给出的原始数据生成的正确结果。
方法二:使用base R(无需额外安装包)
如果你不想安装第三方包,也可以用base R的reshape函数和字符串拆分功能实现:
# 原始数据 df <- data.frame(userid = c(1, 2, 3), pos1 = c("itemA_1", "itemB_1", "itemA_2"), pos2 = c("itemB_1", "itemC_2", "itemC_1"), pos3 = c("itemC_5", "itemA_4", "itemB_3") ) # 1. 转成长格式 df_long_base <- reshape( df, direction = "long", varying = list(names(df)[-1]), # 指定要转换的列(除了userid) v.names = "item_score", idvar = "userid", timevar = "position" ) # 2. 拆分项目和评分 split_cols <- do.call(rbind, strsplit(as.character(df_long_base$item_score), "_")) df_split_base <- cbind(df_long_base["userid"], split_cols) names(df_split_base) <- c("userid", "item", "score") df_split_base$score <- as.integer(df_split_base$score) # 3. 转回宽格式 df_final_base <- reshape( df_split_base, direction = "wide", idvar = "userid", timevar = "item", v.names = "score" ) # 去掉列名里的"score."前缀 names(df_final_base) <- gsub("score\\.", "", names(df_final_base))
运行后得到的结果和tidyverse方法完全一致:
> df_final_base userid itemA itemB itemC 1 1 1 1 5 2 2 4 1 2 3 3 2 3 1
内容的提问来源于stack exchange,提问作者D. Studer
相关产品推荐
相关产品推荐

