基于tidyverse的心理测试数据处理:替换特定位置数值
心理测试数据处理方案(tidyverse实现)
需求说明
- 处理一组包含10道难度递增题目的心理测试数据,得分分为3档:0(错误)、1(接近正确)、2(正确)
- 被试可能未完成全部题目,需按以下规则清洗数据:
- 将第一个值为2的列之前的所有列替换为2(默认被试具备答对这些题的能力)
- 将最后一个值为0的列之后的所有列替换为0(默认被试无法答对这些题)
- 要求使用tidyverse工具集实现,优先使用
mutate函数
原始数据框
df = structure(list(x1_2 = c(NA, 2, NA, NA, NA, NA), x2_2 = c(NA, 2, 2, 2, 2, 2), x3_2 = c(2, 2, 1, 1, 2, 2), x4_2 = c(2, 1, 0, 2, 0, 0), x5_2 = c(2, 1, NA, 2, NA, NA)), row.names = c(NA, -6L), class = c("tbl_df", "tbl", "data.frame")) df
目标数据框
df_final = structure(list(x1_2 = c(2, 2, 2, 2, 2, 2), x2_2 = c(2, 2, 2, 2, 2, 2), x3_2 = c(2, 2, 1, 1, 2, 2), x4_2 = c(2, 1, 0, 2, 0, 0), x5_2 = c(2, 1, 0, 2, 0, 0)), row.names = c(NA, -6L), class = c("tbl_df", "tbl", "data.frame")) df_final
之前的尝试代码
initialNA <- function(x) { index <- cumsum(is.na(x)) >= seq_along(x) x[index] <- 2 x } df <- data.frame(t(apply(df, 1, initialNA))) df
解决方案(tidyverse实现)
结合rowwise()、c_across()和mutate()逐行处理数据,同时满足两个规则:
library(tidyverse) df_processed <- df %>% rowwise() %>% mutate( # 提取当前行所有列值为向量 row_vals = list(c_across(everything())), # 规则1:找到第一个2的位置,之前的列替换为2 first_2_pos = which(row_vals == 2)[1], after_rule1 = ifelse(is.na(first_2_pos), row_vals, replace(row_vals, seq_len(first_2_pos - 1), 2)), # 规则2:找到最后一个0的位置,之后的列替换为0 last_0_pos = tail(which(after_rule1 == 0), 1), final_vals = ifelse(is.na(last_0_pos), after_rule1, replace(after_rule1, seq(last_0_pos + 1, length(after_rule1)), 0)), # 将处理后的向量拆分回原列 !!!set_names(as.list(final_vals), colnames(df)) ) %>% # 移除中间辅助列 select(-row_vals, -first_2_pos, -after_rule1, -last_0_pos, -final_vals) %>% ungroup() # 查看结果 df_processed
代码说明
rowwise():将数据框按行分组,实现逐行独立处理c_across(everything()):提取当前行的所有列值,转为便于操作的向量- 规则1处理:定位第一个值为2的位置,将该位置之前的所有值替换为2;若行内无2则保持原数据
- 规则2处理:定位规则1处理后最后一个值为0的位置,将该位置之后的所有值替换为0;若行内无0则保持规则1的结果
!!!set_names(...):将处理后的向量重新映射为原列名对应的列- 最后清理辅助列并取消行分组
运行后结果与目标数据框df_final完全一致。
内容的提问来源于stack exchange,提问作者Luis
相关产品推荐
相关产品推荐

