Используя SQL, можно объединить две таблицы в таком диапазоне. Это кажется наиболее элегантным решением, выражающим наши намерения напрямую, но мы также предлагаем несколько альтернатив ниже.
library(sqldf)
DF1$date <- as.Date(DF1$timestamp)
sqldf("select *
from DF1 a
left join DF2 b on date between week_start and week_end")
дает:
user_id timestamp date week_id week_start week_end
1 aa 2018-01-01 12:01:00 2018-01-01 1 2018-01-01 2018-01-07
2 ab 2018-01-01 05:01:00 2018-01-01 1 2018-01-01 2018-01-07
3 bb 2018-06-01 09:01:00 2018-06-01 NA <NA> <NA>
4 bc 2018-03-03 23:01:00 2018-03-04 NA <NA> <NA>
5 cc 2018-01-02 11:01:00 2018-01-02 1 2018-01-01 2018-01-07
dplyr
В комментарии автор спросил, можно ли это сделать в dplyr. Это не может быть сделано напрямую, поскольку dplyr не поддерживает сложные объединения, но обходной путь может заключаться в полном перекрестном объединении двух фреймов данных, что приводит к промежуточному результату nrow(DF1) * nrow(DF2)
, а затем отфильтровывает его. dplyr напрямую не поддерживает перекрестные объединения, но мы можем смоделировать их, выполнив полное объединение с идентичным столбцом фиктивной константы, который добавляется к обоим фреймам данных при полном объединении. Так как нам здесь действительно нужно правильное соединение, чтобы добавить обратно несопоставленные строки, мы делаем последнее правое соединение с исходным фреймом данных DF1
. Очевидно, что это совершенно нецелесообразно для достаточно больших входных данных, но для небольших входных данных мы можем это сделать. Если бы было известно, что в DF2
есть соответствие каждой строке в DF1
, тогда right_join
в конце можно было бы опустить.
DF1 %>%
mutate(date = as.Date(timestamp), dummy = 1) %>%
full_join(DF2 %>% mutate(dummy = 1)) %>%
filter(date >= week_start & date <= week_end) %>%
select(-dummy) %>%
right_join(DF1)
R База
findix
находит индекс в DF2
, соответствующий дате d
. Затем мы sapply
пересекаем даты, соответствующие строкам DF1
, и складываем DF1
и соответствующую строку DF2
.
findix <- function(d) c(which(d >= DF2$week_start & d <= DF2$week_end), NA)[1]
cbind(DF1, DF2[sapply(as.Date(DF1$timestamp), findix), ])
Примечание
Используемые входные данные в воспроизводимой форме:
Lines1 <- "user_id timestamp
aa 2018-01-01 12:01 UTC
ab 2018-01-01 05:01 UTC
bb 2018-06-01 09:01 UTC
bc 2018-03-03 23:01 UTC
cc 2018-01-02 11:01 UTC"
DF1 <- read.csv(text = gsub(" +", ",", Lines1), strip.white = TRUE)
DF1$timestamp <- as.POSIXct(DF1$timestamp)
Lines2 <- "week_id week_start week_end
1 2018-01-01 2018-01-07
2 2018-01-08 2018-01-15
3 2018-01-16 2018-01-23
4 2018-01-23 2018-01-30"
DF2 <- read.table(text = Lines2, header = TRUE)
DF2$week_start <- as.Date(DF2$week_start)
DF2$week_end <- as.Date(DF2$week_end)