I have a data frame of start and end dates for 40k~ different doctors (MPINs). We can create an illustrative data frame using the code below:
x <- seq(as.Date("2014-01-01"), as.Date("2015-10-31"), by = "days")
y <- c(1:150)
mpins <- c(1000000:9999999)
s = 40000
df <- data.frame(start_date = as.Date(sample(x, s, replace = TRUE)),
MPIN = sample(mpins, s, replace = FALSE))
df$end_date <- as.Date(df$start_date + sample(y, s, replace = TRUE))
head(df)
start_date MPIN end_date
1 2015-10-31 1093782 2016-03-27
2 2014-12-06 4932382 2015-04-30
3 2015-02-28 5577980 2015-03-29
4 2014-07-31 9824869 2014-11-17
5 2014-06-14 5845130 2014-06-22
6 2014-07-13 2773056 2014-10-17
What I'd like to now do is find a very quick way to create a new data frame that contains a field for each unique month (formatted "YYYY-mm") for each unique MPIN in a data frame that looks like this:
head(target_df)
months MPIN
1 2015-10 1093782
2 2015-11 1093782
3 2015-12 1093782
4 2016-01 1093782
5 2016-02 1093782
6 2016-03 1093782
I've come up with a looping function to do this, but I feel that it is very inefficient. With 40k~ MPINs, it takes about 2 minutes, and the list of MPINs is only going to get larger with time. Below is my current-state solution:
df2 <- function(x) {
tm1 <- df[x, ]
dates <- data.frame(seq(as.Date(tm1$start_date),
as.Date(tm1$end_date),
by = "days"))
colnames(dates) <- c("dates")
dates$months <- substr(as.character(dates$dates), 1, 7)
dates <- dates[which(!duplicated(dates$months)), ]
dates$MPIN <- tm1$MPIN
dates$dates <- NULL
print(dates)
}
a <- (1:nrow(df))
system.time(df3 <- do.call("rbind", lapply(a, function(x) df2(x))))
df3$unique <- paste0(df3$MPIN, "-", df3$months)
df3 <- df3[which(!duplicated(df3$unique)), ]
df3$unique <- NULL
head(df3)
months MPIN
1 2015-10 1093782
2 2015-11 1093782
32 2015-12 1093782
63 2016-01 1093782
94 2016-02 1093782
123 2016-03 1093782
Any suggestions to speed this process up would be greatly appreciated. Thanks!
Update
Slightly tweaking @Michele_Usuelli 's helpful recommendation, I was able to speed up the process by about 80%.
My original function's results:
user system elapsed
122.57 1.50 126.01
The results using the function below:
user system elapsed
25.52 0.15 26.06
library(data.table)
library(dplyr)
# for each record, create a sequence of dates
df <- data.table(df)
df4 <- df[, list(date = seq(start_date, end_date, by = "day"),
MPIN = MPIN),
by = 1:nrow(df)]
# determine the unique month-MPIN combinations
df5 <- df4 %>%
group_by(month = paste0(format(date, "%Y"), "-", format(date, "%m")), MPIN) %>%
summarise(n = n()) %>%
select(-n)