R语言按起止日期按天展开值并转换为宽表的实现方法
日期区间拆分转宽表实现方案
需求说明
现有存储日期区间的数据框结构如下:
# 示例数据 df <- data.frame( start_date = as.Date(c("2020-01-01", "2020-01-05")), end_date = as.Date(c("2020-01-06", "2020-01-07")), name = c("x", "y"), value = c(2, 4) )
需要将每行的日期区间按天拆分为单日记录,把name列的取值展开为独立列,对应填充value字段,最终得到连续日期粒度的宽表,无匹配值的位置填NA,预期输出如下:
date x y 1 2020-01-01 2 NA 2 2020-01-02 2 NA 3 2020-01-03 2 NA 4 2020-01-04 2 NA 5 2020-01-05 2 4 6 2020-01-06 2 4 7 2020-01-07 NA 4
方案1:Base R 原生实现
无第三方包依赖,核心通过基础函数遍历生成日期序列、转换表结构、补全日期:
# 遍历每行生成区间内所有单日记录,拼接为长表 long_data <- do.call(rbind, lapply(seq_len(nrow(df)), function(row_idx) { date_seq <- seq(df$start_date[row_idx], df$end_date[row_idx], by = "day") data.frame( date = date_seq, name = df$name[row_idx], value = df$value[row_idx] ) })) # 长表转宽表,按date分组,name值作为列名 wide_data <- reshape( long_data, idvar = "date", timevar = "name", direction = "wide" ) # 清理自动生成的列名前缀 colnames(wide_data) <- gsub("^value\\.", "", colnames(wide_data)) # 生成全量连续日期序列,左连补全断点 full_dates <- data.frame(date = seq(min(df$start_date), max(df$end_date), by = "day")) result_base <- merge(full_dates, wide_data, by = "date", all.x = TRUE)
运行后result_base与预期输出完全一致。
方案2:dplyr + lubridate 实现
代码可读性更高,搭配tidyr完成表结构转换,适合tidyverse工作流:
依赖包安装加载:首次使用需运行
install.packages(c("dplyr","lubridate","tidyr"))安装
library(dplyr) library(lubridate) library(tidyr) result_tidy <- df %>% # 按行生成当前记录日期区间内的所有单日,存储为列表列 rowwise() %>% mutate(date = list(seq(start_date, end_date, by = "day"))) %>% # 展开列表列,得到单日粒度长表 unnest_longer(date) %>% # 长表转宽表,name的取值作为新列,对应填充value pivot_wider( id_cols = date, names_from = name, values_from = value ) %>% # 补全所有连续日期,缺失值自动填充为NA complete(date = seq(min(df$start_date), max(df$end_date), by = "day")) %>% # 按日期升序排列 arrange(date)
注意:如果原始数据存在同一name、同一日期对应多个value的重叠情况,pivot_wider会生成列表列并提示警告,可提前根据业务规则做聚合、去重处理。
内容的提问来源于stack exchange,提问作者ravinglooper
相关产品推荐
相关产品推荐

