多列匹配条件下将df2数据映射到df1的R语言实现问题
基于多列条件将df2数据匹配至df1并保留所有观测
需求说明
需要为df1新增列NewData.copied,规则如下:
- 当
df1$Name与df2$Name匹配,且df1$Date > df2$Date时,填入对应的df2$NewData值 - 必须保留
df1的所有原始观测 - 此前尝试
%in%、match+mapply时,出现错误:longer argument not a multiple of length of shorter
示例数据
df1 = data.frame("Name" = c("John", "Jake", "John", "Jake"), "Date" = c("2020-01-03", "2020-01-02", "2020-01-03", "2020-01-04")) df2 = data.frame("Name" = c("John", "Jake", "John", "Jake"), "Date" = c("2020-01-03", "2020-01-01", "2020-01-01", "2020-01-04"), "NewData" = c("Stuff1", "Stuff2", "Stuff3", "Stuff4"))
前置处理:转换日期格式
首先要把字符型的Date转为日期类型,否则无法正确比较大小:
df1$Date <- as.Date(df1$Date) df2$Date <- as.Date(df2$Date)
解决方案
方法1:使用dplyr(tidyverse)
通过连接+分组筛选的方式实现,确保保留所有df1观测:
library(dplyr) df1_result <- df1 %>% # 按Name左连接df2,区分两个表的Date列 left_join(df2, by = "Name", suffix = c(".df1", ".df2")) %>% # 筛选符合Date条件的记录 filter(Date.df1 > Date.df2) %>% # 按Name和df1的Date分组,取df2中最新日期对应的NewData(和示例结果一致) group_by(Name, Date.df1) %>% slice_max(Date.df2) %>% ungroup() %>% # 整理列名 select(Name, Date = Date.df1, NewData.copied = NewData) %>% # 右连接回原始df1,补全未匹配的观测为NA right_join(df1, by = c("Name", "Date")) %>% select(Name, Date, NewData.copied)
方法2:使用Base R
通过逐行筛选匹配记录实现:
df1$NewData.copied <- sapply(1:nrow(df1), function(i) { # 筛选df2中Name匹配且Date小于当前df1行Date的记录 match_rows <- df2[df2$Name == df1$Name[i] & df2$Date < df1$Date[i], ] if (nrow(match_rows) == 0) { # 无匹配时返回NA NA_character_ } else { # 取匹配记录中最新日期对应的NewData match_rows$NewData[which.max(match_rows$Date)] } })
错误原因解释
使用%in%或match+mapply出现长度不匹配错误,是因为df1和df2的匹配关系并非一对一:每个Name对应的Date记录数不一致,简单的向量匹配无法处理这种多对多/一对多的条件匹配场景,必须针对每行做针对性筛选或通过连接后分组处理。
预期结果
运行上述任意方法后,df1(或df1_result)的结果如下:
Name Date NewData.copied 1 John 2020-01-03 Stuff3 2 Jake 2020-01-02 Stuff2 3 John 2020-01-03 Stuff3 4 Jake 2020-01-04 <NA>
内容的提问来源于stack exchange,提问作者user17661126
相关产品推荐
相关产品推荐

