R语言中为数据框生成前次试验分数列时循环报错的解决方案
问题描述
我有如下结构的dataframe:
| subjectID(受试者ID) | Trial(试验次数) | Score(分数) |
|---|---|---|
| 1 | 1 | 16 |
| 1 | 1 | 16 |
| 1 | 1 | 16 |
| 1 | 2 | 8 |
| 1 | 2 | 8 |
| 1 | 2 | 8 |
| 1 | 3 | 12 |
| 1 | 3 | 12 |
| 1 | 3 | 12 |
| 2 | 1 | 9 |
| 2 | 1 | 9 |
| 2 | 1 | 9 |
| 2 | 2 | 10 |
| 2 | 2 | 10 |
| 2 | 2 | 10 |
需要新增一列Previous_Trial_Score,存储每个受试者上一次试验的分数,效果如下:
| subjectID(受试者ID) | Trial(试验次数) | Score(分数) | Previous_Trial_Score(前次试验分数) |
|---|---|---|---|
| 1 | 1 | 16 | NA |
| 1 | 1 | 16 | NA |
| 1 | 1 | 16 | NA |
| 1 | 2 | 8 | 16 |
| 1 | 2 | 8 | 16 |
| 1 | 2 | 8 | 16 |
| 1 | 3 | 12 | 8 |
| 1 | 3 | 12 | 8 |
| 1 | 3 | 12 | 8 |
| 2 | 1 | 9 | NA |
| 2 | 1 | 9 | NA |
| 2 | 1 | 9 | NA |
| 2 | 2 | 10 | 9 |
| 2 | 2 | 10 | 9 |
| 2 | 2 | 10 | 9 |
每个受试者的Trial 1对应的Previous_Trial_Score为NA。我写了如下循环代码:
for (myperson in unique(data$subjectID)){ for (mytrial in unique(data$Trial[data$Trial>1])){ #Specify the trial and person Prev_Score=as.numeric(unique(data[data$subjectID==myperson & data$Trial==mytrial-1, "Score"])) #Save it to the dataframe data[data$subjectID==myperson & data$Trial==mytrial,"Prev_Score"]=Prev_Score } }
运行时出现错误:
Error: Assigned data `value` must be compatible with existing data. i Error occurred for column `Prev_Score`. x Can't convert from <double> to <logical> due to loss of precision. * Locations: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 3...
解决方案
修复原有循环的方法
错误原因是赋值前未初始化Prev_Score列,R会默认将其创建为逻辑型,后续赋值数值型数据时就会报错。只需提前初始化该列为数值型即可:
# 先初始化列为数值型,默认填充NA data$Prev_Score <- NA_real_ for (myperson in unique(data$subjectID)){ # 仅遍历当前受试者存在的、大于1的试验次数 mytrials <- unique(data$Trial[data$subjectID == myperson & data$Trial > 1]) for (mytrial in mytrials){ # 获取前一次试验的分数 Prev_Score <- as.numeric(unique(data[data$subjectID == myperson & data$Trial == mytrial - 1, "Score"])) # 赋值到对应行 data[data$subjectID == myperson & data$Trial == mytrial, "Prev_Score"] <- Prev_Score } }
tidyverse/dplyr 替代方案
用dplyr的分组和窗口函数更简洁高效,无需手动写循环:
library(dplyr) data <- data %>% # 按受试者分组 group_by(subjectID) %>% # 按试验次数排序,确保顺序正确 arrange(Trial, .by_group = TRUE) %>% # 提取每个试验组的首个分数作为基准,向前偏移得到前次试验分数后填充同组所有行 mutate( trial_score = first(Score), Previous_Trial_Score = lag(trial_score) ) %>% # 移除临时列 select(-trial_score) %>% ungroup()
也可以先提取受试者-试验的分数映射表,再合并回原数据:
library(dplyr) # 创建每个受试者-试验的分数映射表,生成前次试验分数 trial_scores <- data %>% distinct(subjectID, Trial, .keep_all = TRUE) %>% group_by(subjectID) %>% mutate(Previous_Trial_Score = lag(Score)) %>% ungroup() # 合并回原数据框 data <- data %>% left_join(trial_scores, by = c("subjectID", "Trial", "Score"))
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

