You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

R语言重复测量长数据转换:按区块拆分指标列

问题描述

我有一份长格式重复测量数据集:每个被试完成9个区块任务,每个区块100个试次,测量指标包括反应时(reaction_time)、反应内容(response)、正确率(correct)等,display列标记所属区块。

需求:保留长格式(每个被试对应100行,每行代表一个试次),同时将各指标按区块拆分为独立列,列名格式如block1_trialnumber、block1_correct,最终生成5项指标×9个区块=45列。

附我生成的模拟数据集(欢迎提优化建议):

set.seed(2222)
Responses <- c('a','b','c','d')
right <- c('1', '0')

dat <- data.frame(ID = c("1", "1","1", "1", "1", "1","2","2","2", "2","2","2","3","3","3","3","3","3","4","4","4","4","4","4"),
                  condition = c("acq","acq","acq","acq","acq","acq","close","close","close","close","close","close","control","control","control","control","control","control","acq","acq","acq","acq","acq","acq"),
                  trial=rep(1:2),
                  reaction_time = runif(2),
                  response = sample(Responses, 4),
                  correct = sample(right, 2),
                  display = c("block 1","block 1", "block 2", "block 2","block 3","block 3","block 1","block 1", "block 2", "block 2","block 3","block 3","block 1","block 1", "block 2", "block 2","block 3","block 3","block 1","block 1", "block 2", "block 2","block 3","block 3" ))

该模拟数据中每个被试对应3个区块共6行,condition对每个被试固定,其他列随行变化。期望转换后每个被试2行(对应2个试次),生成3区块×3指标=9列,目标结构示例:

clean_dat <- data.frame(ID = c("1","1","2","2","3","3", "4", "4"),
                        condition = c("acq","acq","close", "close", "control", "control", "acq", "acq"),
                        trial = rep(1:2),
                        reaction_time_block1 = c("0.367658177","0.181875909", "0.756887668","0.243691438","0.576413027","0.927882617", "0.886305536", "0.573818437"),
                        reaction_time_block2 = c("0.826176795","0.584974443", "0.678065433", "0.062752954", "0.870914138","0.390425880", "0.985821178", "0.009305823"),
                        reaction_time_block3 =c("0.958869835","0.101798940", "0.376347888","0.656183007","0.975945541","0.986688076", "0.178954507","0.764906755"),
                        response_block1 = c("a","b", "c","d","a","b","c","d"),
                        response_block2 = c("c","d","a","b","c","d","a","b"),
                        response_block3 =c("a","b", "c","d","a","b","c","d"), 
                        correct_block1 =c("0","1","0","1","0","1","0","1"),
                        correct_block2 =c("0","1","0","1","0","1","0","1"),
                        correct_block3 =c("0","1","0","1","0","1","0","1"))

我尝试用pivot_wider但效果不对:

dat_pivot <-dat %>% 
  pivot_wider(names_from = display, values_from = c(reaction_time, trial, response,correct))

生成了正确列数,但每个被试仅1行,列内为列表结构且报错,用unlist未解决,想找比循环更简便的方法。


解决方案

问题出在pivot_wider未指定id_cols,默认会把所有非values_from列当成分组依据,导致每个被试只保留1行。只需明确按ID、condition、trial分组,就能让每个试次对应一行:

library(tidyverse)

set.seed(2222)
# 先使用原模拟数据
Responses <- c('a','b','c','d')
right <- c('1', '0')

dat <- data.frame(ID = c("1", "1","1", "1", "1", "1","2","2","2", "2","2","2","3","3","3","3","3","3","4","4","4","4","4","4"),
                  condition = c("acq","acq","acq","acq","acq","acq","close","close","close","close","close","close","control","control","control","control","control","control","acq","acq","acq","acq","acq","acq"),
                  trial=rep(1:2),
                  reaction_time = runif(2),
                  response = sample(Responses, 4),
                  correct = sample(right, 2),
                  display = c("block 1","block 1", "block 2", "block 2","block 3","block 3","block 1","block 1", "block 2", "block 2","block 3","block 3","block 1","block 1", "block 2", "block 2","block 3","block 3","block 1","block 1", "block 2", "block 2","block 3","block 3" ))

# 修正后的pivot_wider用法
dat_clean <- dat %>%
  pivot_wider(
    id_cols = c(ID, condition, trial),  # 指定分组依据:被试、条件、试次
    names_from = display,
    values_from = c(reaction_time, response, correct),
    names_glue = "{.value}_{str_replace(display, ' ', '')}"  # 格式化列名,去掉空格变成block1
  )

# 查看转换结果
dat_clean

运行后会得到每个被试对应2行(试次1和试次2),列名符合reaction_time_block1、response_block2的格式,完全匹配目标结构。


模拟数据优化建议

原模拟数据生成方式繁琐,且存在重复值问题(比如reaction_time仅生成2个值重复给所有行),用tidyverse函数可高效生成更贴近真实情况的数据集:

set.seed(2222)
Responses <- c('a','b','c','d')
right <- c('1', '0')

# 生成基础组合:被试、条件、区块、试次
dat_optimized <- crossing(
  ID = as.character(1:4),
  condition = c("acq", "close", "control", "acq"),  # 对应原数据的被试条件
  display = paste0("block ", 1:3),
  trial = 1:2
) %>%
  # 为每行生成独立的随机指标数据
  mutate(
    reaction_time = runif(n()),
    response = sample(Responses, n(), replace = TRUE),
    correct = sample(right, n(), replace = TRUE)
  )

此代码生成的数据集每个单元格都是独立随机值,更符合真实实验数据特征,且代码简洁易维护。

内容的提问来源于stack exchange,提问作者captain broccoli heart

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 16:08:09