矢量化每行的"日期范围扩展",在 R 的 dplyr 中



我在 R 的 tibble 中有一个数据集,如下所示:

# A tibble: 50,045 x 5
ref_key start_date end_date  
<chr>   <date>     <date>    
1 123     2010-01-08 2010-01-13
2 123     2010-01-21 2010-01-23
3 123     2010-03-10 2010-04-14

我需要创建另一个每行只存储一个日期的 tibble,如下所示:

ref_key date      
<chr>   <date>    
1 123     2010-01-08
2 123     2010-01-09
3 123     2010-01-10
4 123     2010-01-11
5 123     2010-01-12
6 123     2010-01-13
7 123     2010-01-21
8 123     2010-01-22
9 123     2010-01-23

目前,我正在为此编写一个显式循环,如下所示:

for (loop in (1:nrow(input.df))) {
if (loop%%100==0) {
print(paste(loop,'/',nrow(input.df)))
}
temp.df.st00 <- input.df[loop,] %>% data.frame
temp.df.st01 <- tibble(ref_key=temp.df.st00[,'ref_key'],
date=seq(temp.df.st00[,'start_date'],
temp.df.st00[,'end_date'],1))
if (loop==1) {
output.df <- temp.df.st01
} else {
output.df <- output.df %>%
bind_rows(temp.df.st01)
}
}

它正在工作,但速度很慢,因为我有>50k 行要处理,完成循环需要几分钟。

我想知道这一步是否可以矢量化,是否与dplyr中的row_wise有关?

我们创建一个行名列(rownames_to_column),然后nest"rn"和"ref_key",mutatemap中获取"start_date"和"end_date"的序列,并在select掉不需要的列后unnest

library(tidyverse)
res <- df1 %>%
rownames_to_column('rn') %>% 
nest(-rn, -ref_key) %>%
mutate(date = map(data, ~ seq(.x$start_date, .x$end_date, by = "1 day"))) %>%
select(-data, -rn) %>%
unnest
head(res, 9)
#  ref_key       date
#1     123 2010-01-08
#2     123 2010-01-09
#3     123 2010-01-10
#4     123 2010-01-11
#5     123 2010-01-12
#6     123 2010-01-13
#7     123 2010-01-21
#8     123 2010-01-22
#9     123 2010-01-23

一种解决方案是使用tidyr::complete来扩展行。由于行扩展基于行的start-dateend_date,因此group_byrow_number将有助于生成start-dateend_date之间的Date序列。

library(dplyr)
library(tidyr)
df %>% #mutate(rnum = row_number()) %>%
group_by(row_number()) %>%
complete(start_date = seq.Date(max(start_date), max(end_date), by="day")) %>%
fill(ref_key) %>%
ungroup() %>%
select(ref_key, date = start_date)

# # A tibble: 45 x 2
# ref_key date      
# <int> <date>    
# 1     123 2010-01-08
# 2     123 2010-01-09
# 3     123 2010-01-10
# 4     123 2010-01-11
# 5     123 2010-01-12
# 6     123 2010-01-13
# 7     123 2010-01-21
# 8     123 2010-01-22
# 9     123 2010-01-23
# 10     123 2010-03-10
# # ... with 35 more rows

数据

df <- read.table(text = "ref_key start_date end_date  
123     2010-01-08 2010-01-13
123     2010-01-21 2010-01-23
123     2010-03-10 2010-04-14", header = TRUE, stringsAsFactor = FALSE)
df$start_date <- as.Date(df$start_date)
df$end_date <- as.Date(df$end_date)

最新更新