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

如何合并多个DataFrame并筛选参与全部测试的唯一参与者

问题描述

现有4个行数不同的DataFrame,需合并它们,仅保留参与了所有测试的参与者,且每个参与者对应一行,避免重复列名与NA值。

变量定义

  • class1Time1:2021年参加VarA和VarB测试的班级1数据
  • class2Time1:2021年参加VarA和VarB测试的班级2数据
  • class1Time2:2022年参加VarA和VarB测试的班级1数据
  • class2Time2:2022年参加VarA和VarB测试的班级2数据

注:"Var_Year"代表对应年份的科目成绩,部分参与者未完成全部测试。

模拟代码

创建模拟数据框:

names1 <- c("Mary","John","Kate", "Bea", "Harry", "Hermione", "Rony", "Dobby")
names2 <- c("Harry", "Hermione", "Rony", "Dobby", "Dumbledore", "Snape", "Sirius")

class1Time1 <- data.frame(ID = names1[3:8], VarA_21 = sample(1:20, 6), VarB_21 = sample(1:20, 6))
class2Time1 <- data.frame(ID = names2[1:7], VarA_21 = sample(1:20, 7), VarB_21 = sample(1:20, 7))
class1Time2 <- data.frame(ID = names1[2:8], VarA_22 = sample(1:20, 7), VarB_22 = sample(1:20, 7))
class2Time2 <- data.frame(ID = names2[1:4], VarA_22 = sample(1:20, 4), VarB_22 = sample(1:20, 4))

仅Harry、Hermione、Rony、Dobby完成了全部测试

将环境中所有含"class"的DataFrame存入列表:

together <- grep("class",names(.GlobalEnv), value=TRUE)
my_list <- do.call("list", mget(together))

已尝试方法

使用Reduce结合full_join、merge、rbind等,但未达预期。使用test <- Reduce(function(...) merge(..., all = TRUE, by="ID"), my_list)时出现列名重复问题。

待解决问题

  1. 如何合并所有DataFrame,得到仅包含参与全部测试的参与者,且每个参与者一行的结果?
  2. 若解决问题1,是否可通过filter结合complete.cases筛选无NA的记录?

解决方案

问题1:合并并保留全测试参与者

核心逻辑是先按ID合并所有数据框,合并后处理重复列(取非NA值整合),最后筛选出所有测试都完成的参与者。以下提供两种实现方式:

方法1:dplyr + purrr 实现

library(dplyr)
library(purrr)

# 逐步合并数据框,处理重复列
merged_data <- my_list %>%
  reduce(full_join, by = "ID") %>%
  # 整合重复的年份科目列,取非NA值
  mutate(
    VarA_21 = coalesce(VarA_21.x, VarA_21.y),
    VarB_21 = coalesce(VarB_21.x, VarB_21.y),
    .keep = "unused" # 删除原重复列
  ) %>%
  # 筛选所有成绩列无NA的参与者(即完成全部测试)
  filter(complete.cases(.))

方法2:base R 实现

# 合并所有数据框
merged_base <- Reduce(function(x, y) merge(x, y, by = "ID", all = TRUE), my_list)

# 遍历整合重复的年份科目列
for (var in c("VarA", "VarB")) {
  for (year in c("21", "22")) {
    target_cols <- grep(paste0(var, "_", year), colnames(merged_base), value = TRUE)
    # 取每行非NA值(同一参与者同科目同年份只会有一个有效成绩)
    merged_base[[paste0(var, "_", year)]] <- apply(merged_base[target_cols], 1, function(x) x[!is.na(x)][1])
    # 删除多余的重复列
    merged_base <- merged_base[, !colnames(merged_base) %in% target_cols[-1]]
  }
}

# 筛选完成全部测试的参与者
merged_base <- merged_base[complete.cases(merged_base), ]

两种方法最终都会得到仅包含Harry、Hermione、Rony、Dobby的结果,每个参与者一行,无重复列和NA值。

问题2:关于filter结合complete.cases

完全可以。filter(complete.cases(.))(dplyr语法)或data[complete.cases(data), ](base R语法)是筛选无NA记录的标准方式,也是上面两种方法中筛选全测试参与者的核心步骤——完成所有测试的参与者所有成绩列都不会有NA,因此通过complete.cases可以精准定位目标群体。

内容的提问来源于stack exchange,提问作者Larissa Cury

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 23:40:31