我有一个看起来像这样的桌子;
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
我还有另一个桌子,该桌子在2018年每周都有。
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
... ... ...
假设week_start是星期一,week_end是星期日。
我想做两件事。我首先想将week_id加入到第一个表中,然后再为每个时间戳分配一天。我的输出看起来像这样:
user_id timestamp week_id day_of_week
aa 2018-01-01 12:01 UTC 1 Monday
ab 2018-01-02 05:01 UTC 1 Tuesday
bb 2018-01-13 09:01 UTC 2 Friday
bc 2018-01-28 23:01 UTC 4 Friday
cc 2018-01-06 11:01 UTC 1 Saturday
在Excel中,我可以使用vlookup
轻松地做到这一点。我的主要兴趣是学习在这种情况下如何联接表。因此,我不会接受使用weekday
函数的答案。
这两个表的格式都更易于访问。
user_id <- c("aa", "ab", "bb", "bc", "cc")
timestamp <- c("2018-01-01 12:01", "2018-01-01 05:01", "2018-06-01 09:01", "2018-03-03 23:01", "2018-01-02 11:01")
week_id <- seq(1,52)
week_start <- seq(as.Date("2018-01-01"), as.Date("2018-12-31"), 7)
week_end <- week_start + 6
week_start <- week_start[1:52]
week_end <- week_end[1:52]
table1 <- data.frame(user_id, timestamp)
table2 <- data.frame(week_id, week_start, week_end)
答案 0 :(得分:1)
使用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不支持复杂的联接,因此无法直接完成,但是一种解决方法是对两个数据帧进行完全交叉联接,从而产生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)
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)
答案 1 :(得分:0)
fuzzyjoin
包就是这种情况。使用match_fun
-参数,我们可以为每一列指定条件。在这种情况下,table1$date >= table2$week_start
和table1$date <= table2$week_end
。
library(fuzzyjoin)
library(lubridate)
table1$date <- as.Date(table1$timestamp)
fuzzy_left_join(table1, table2,
by = c("date" = "week_start", "date" = "week_end"),
match_fun = list(`>=`, `<=`)) %>%
mutate(day_of_week = wday(date, label = TRUE)) %>%
select(user_id, timestamp, week_id, day_of_week)
user_id timestamp week_id day_of_week
1 aa 2018-01-01 12:01 1 Mo
2 ab 2018-01-01 05:01 1 Mo
3 bb 2018-06-01 09:01 22 Fr
4 bc 2018-03-03 23:01 9 Sa
5 cc 2018-01-02 11:01 1 Di
我也是一个聪明人,因为我没有使用weekday
函数,而是使用lubridate
软件包中的wday。