使用dplyr按组内匹配列填充缺失值(NA)
嘿,这个需求其实不用把数据展开就能优雅解决,完全能保持原有的整洁结构!我给你分享两种常用工具的实现方案,分别是R的tidyverse和Python的pandas:
R 方案(基于tidyverse)
核心思路是按date分组,在每个日期组内,用另一组的有效值填充当前组的NA。我们可以用dplyr的分组操作结合coalesce函数来实现:
首先构造示例数据:
library(tidyverse) # 模拟你的数据结构 df <- tibble( date = rep(as.Date(c("2023-01-01", "2023-01-02", "2023-01-03")), 2), groupID = rep(c("A", "B"), each = 3), value = c(1, NA, 3, NA, 2, NA) )
然后执行填充操作:
df_filled <- df %>% group_by(date) %>% mutate( # 提取同日期下另一组的数值 other_group_val = case_when( groupID == "A" ~ value[groupID == "B"], groupID == "B" ~ value[groupID == "A"] ), # 用另一组的值填充NA,优先保留原有效值 value = coalesce(value, other_group_val) ) %>% ungroup()
如果不需要保留中间列other_group_val,直接把逻辑整合到coalesce里也可以。这个方法全程保持原数据的行结构,不会展开。
Python 方案(基于pandas)
同样按date分组,利用分组后的变换操作来实现跨组填充。这里有两种写法,一种是直观的自定义函数,另一种是更简洁的lambda表达式:
先构造示例数据:
import pandas as pd import numpy as np df = pd.DataFrame({ "date": pd.date_range("2023-01-01", periods=3).repeat(2), "groupID": ["A", "B"]*3, "value": [1, np.nan, np.nan, 2, 3, np.nan] })
写法一:直观的自定义分组函数
def fill_cross_group_na(group): # 分别获取当前日期组内A、B的有效值 a_val = group[group["groupID"] == "A"]["value"].dropna().values b_val = group[group["groupID"] == "B"]["value"].dropna().values # 填充NA:A的NA用B的值,B的NA用A的值 group["value"] = np.where( group["groupID"] == "A", group["value"].fillna(b_val[0] if len(b_val) > 0 else np.nan), group["value"].fillna(a_val[0] if len(a_val) > 0 else np.nan) ) return group df_filled = df.groupby("date").apply(fill_cross_group_na).reset_index(drop=True)
写法二:简洁的lambda变换
因为你的场景只有两组,每个日期组内刚好两行,我们可以直接反转取另一行的值来填充:
df["value"] = df.groupby("date")["value"].transform( lambda x: x.fillna(x.iloc[::-1].values[0]) )
这个写法非常简洁,核心是x.iloc[::-1]反转当前组的行,取第一个元素就是另一组的值,完美适配两组的跨填充需求。
不管用哪种工具,最终处理后的数据都会保持原有的行结构,完全符合你“保持数据整洁”的要求~
内容的提问来源于stack exchange,提问作者jknappe
相关产品推荐
相关产品推荐

