我希望使用两个数据集都存在的“日期时间”列合并两个数据集(POSIXct 格式:dd/mm/yyyy hh:mm)。以下是来自两个数据集的示例数据:
# Dataset 1 (dts1)
datetime count period
1 30/03/2011 02:32 27 561
2 30/03/2011 02:42 3 600
3 30/03/2011 02:52 0 574
4 30/03/2011 03:02 1 550
5 30/03/2011 03:12 15 600
6 30/03/2011 03:22 0 597
# Dateset 2 (dts2)
datetime dist car satd alt
1 30/03/2011 01:59 23.9 1 3 1.76
2 30/03/2011 02:58 14.7 1 7 6.36
3 30/03/2011 03:55 10.4 2 9 -0.34
4 30/03/2011 04:53 35.4 1 3 3.55
5 30/03/2011 05:52 56.1 1 7 -0.91
6 30/03/2011 06:48 12.3 1 4 6.58
7 30/03/2011 07:48 10.7 1 5 4.18
如果这是合并来自两个帧的匹配行的简单情况,那么基本的 merge(data1, data2, by="datetime")
或 rbind()
函数可用于。
但是,我的问题比较复杂,因为两个数据集中的时间间隔不相等。 数据集 1
包含精确 10 分钟间隔的数据(每行包含有关在指定日期/时间结束的 10 分钟 block 的信息),而 数据集2
包含大约 1 小时间隔的数据(每行包含在指定日期/时间结束的 1 小时 block 中的信息)。
让事情变得更难的是,两个数据集中行的开始时间之间存在时间不匹配(即dts1
:01/03/2013 10: 00:00,dts2
:01/03/2012 09:58:12)。 dts2
间隔在整个数据集中也有所不同(± 1 小时左右的几分钟)。我想将数据集 1 中每 10 分钟的数据行与其在数据集 2 中适合的小时 block (以及来自 dts2 的所有关联列值)链接起来。将有一些行可以放入 2 个不同的小时 block (即 30/03/2011 03:02),但我只需要将这些行链接到其中一个小时 block 。
我想以这样的方式结束:
datetime_dts1 count period datetime2_dts2 dist car satd alt
1 30/03/2011 02:32 27 561 30/03/2011 02:58 14.7 1 7 6.36
2 30/03/2011 02:42 3 600 30/03/2011 02:58 14.7 1 7 6.36
3 30/03/2011 02:52 0 574 30/03/2011 02:58 14.7 1 7 6.36
4 30/03/2011 03:02 1 550 30/03/2011 02:58 14.7 1 7 6.36
5 30/03/2011 03:12 15 600 30/03/2011 03:55 10.4 2 9 -0.34
6 30/03/2011 03:22 0 597 30/03/2011 03:55 10.4 2 9 -0.34
我一直在寻找这个问题的答案,但一直没能解决,而且我的 R 能力也不高。如果有人能给我指示或提供解决方案,我将不胜感激。
最佳答案
首先将您的日期时间字符串转换为 POSIXt
类后,round
ing 和 trunc
ating 这些时间的一些组合应该会让您得到一些您想要的东西可以用作合并的基础。
首先读入您的数据,并创建相应的 POSIXt 日期时间:
dts1 <- structure(list(datetime = structure(1:6,
.Label = c("30/03/2011 02:32", "30/03/2011 02:42",
"30/03/2011 02:52", "30/03/2011 03:02", "30/03/2011 03:12",
"30/03/2011 03:22"), class = "factor"), count = c(27L, 3L,
0L, 1L, 15L, 0L), period = c(561L, 600L, 574L, 550L, 600L,
597L)), .Names = c("datetime", "count", "period"),
class = "data.frame", row.names = c(NA, -6L))
dts2 <- structure(list(datetime = structure(1:7,
.Label = c("30/03/2011 01:59", "30/03/2011 02:58",
"30/03/2011 03:55", "30/03/2011 04:53", "30/03/2011 05:52",
"30/03/2011 06:48", "30/03/2011 07:48"), class = "factor"),
dist = c(23.9, 14.7, 10.4, 35.4, 56.1, 12.3, 10.7), car =
c(1L, 1L, 2L, 1L, 1L, 1L, 1L), satd = c(3L, 7L, 9L, 3L, 7L,
4L, 5L), alt = c(1.76, 6.36, -0.34, 3.55, -0.91, 6.58,
4.18)), .Names = c("datetime", "dist", "car", "satd",
"alt"), class = "data.frame", row.names = c(NA, -7L))
# create corresponding POSIXlt vector
# (you could update the 'datetime' columns in-place if you prefer)
datetime1 <- strptime(dts1$datetime, format="%d/%m/%Y %H:%M")
datetime2 <- strptime(dts2$datetime, format="%d/%m/%Y %H:%M")
以下代码在所有情况下都基于最近的小时生成合并表。在合并内部,它只是在每个数据帧前添加一个带有舍入时间的列,基于该列进行合并(即列号 1),然后使用 -1
索引删除该列结束:
# merge based on nearest hour
merge(
cbind(round(datetime1, "hours"), dts1),
cbind(round(datetime2, "hours"), dts2),
by=1, suffixes=c("_dts1", "_dts2")
)[-1]
datetime_dts1 count period datetime_dts2 dist car satd alt
1 30/03/2011 02:32 27 561 30/03/2011 02:58 14.7 1 7 6.36
2 30/03/2011 02:42 3 600 30/03/2011 02:58 14.7 1 7 6.36
3 30/03/2011 02:52 0 574 30/03/2011 02:58 14.7 1 7 6.36
4 30/03/2011 03:02 1 550 30/03/2011 02:58 14.7 1 7 6.36
5 30/03/2011 03:12 15 600 30/03/2011 02:58 14.7 1 7 6.36
6 30/03/2011 03:22 0 597 30/03/2011 02:58 14.7 1 7 6.36
如上,但这次只是在小时上截断:
merge(
cbind(trunc(datetime1, "hours"), dts1),
cbind(trunc(datetime2, "hours"), dts2),
by=1, suffixes=c("_dts1", "_dts2")
)[-1]
datetime_dts1 count period datetime_dts2 dist car satd alt
1 30/03/2011 02:32 27 561 30/03/2011 02:58 14.7 1 7 6.36
2 30/03/2011 02:42 3 600 30/03/2011 02:58 14.7 1 7 6.36
3 30/03/2011 02:52 0 574 30/03/2011 02:58 14.7 1 7 6.36
4 30/03/2011 03:02 1 550 30/03/2011 03:55 10.4 2 9 -0.34
5 30/03/2011 03:12 15 600 30/03/2011 03:55 10.4 2 9 -0.34
6 30/03/2011 03:22 0 597 30/03/2011 03:55 10.4 2 9 -0.34
同上,但对于 dts1,通过在截断前减去 10*60 秒,将记录视为属于前一小时的记录,直到整点过后 10 分钟。这一个产生与您指定的相同的输出,但没有更多信息我不确定它是否是您想要的确切规则。
merge(
cbind(trunc(datetime1 - 10*60, "hours"), dts1),
cbind(trunc(datetime2, "hours"), dts2),
by=1, suffixes=c("_dts1", "_dts2")
)[-1]
datetime_dts1 count period datetime_dts2 dist car satd alt
1 30/03/2011 02:32 27 561 30/03/2011 02:58 14.7 1 7 6.36
2 30/03/2011 02:42 3 600 30/03/2011 02:58 14.7 1 7 6.36
3 30/03/2011 02:52 0 574 30/03/2011 02:58 14.7 1 7 6.36
4 30/03/2011 03:02 1 550 30/03/2011 02:58 14.7 1 7 6.36
5 30/03/2011 03:12 15 600 30/03/2011 03:55 10.4 2 9 -0.34
6 30/03/2011 03:22 0 597 30/03/2011 03:55 10.4 2 9 -0.34
您可以根据您的特定规则调整舍入、截断以及是否先减去/添加一些时间的细节。
编辑:
不是最优雅的,但这是一种不同的方法,可以适应您在评论中描述的更复杂的条件规则。这在很大程度上依赖于 zoo 包中的 na.locf
来首先确定每个 dts1 记录之前和之后的 dts2 时间。有了这些,只需应用规则来选择所需的 dts2 时间,匹配回原始 dts1 表,然后合并即可。
library(zoo)
# create ordered list of all datetimes, using names to keep
# track of which ones come from each data frame
alldts <- sort(c(
setNames(datetime1, rep("dts1", length(datetime1))),
setNames(datetime2, rep("dts2", length(datetime2)))))
is.dts1 <- names(alldts)=="dts1"
# for each dts1 record, get previous closest dts2 time
dts2.prev <- alldts
dts2.prev[is.dts1] <- NA
dts2.prev <- na.locf(dts2.prev, na.rm=FALSE)[is.dts1]
# for each dts1 record, get next closest dts2 time
dts2.next <- alldts
dts2.next[is.dts1] <- NA
dts2.next <- na.locf(dts2.next, na.rm=FALSE, fromLast=TRUE)[is.dts1]
# for each dts1 record, apply rule to choose dts2 time
use.prev <- !is.na(dts2.prev) & (alldts[is.dts1] - dts2.prev < 5)
dts2.to.use <- ifelse(use.prev, as.character(dts2.prev),
as.character(dts2.next))
# merge based on chosen dts2 times, prepended as character vector
# for the purpose of merging
merge(
cbind(.dt=dts2.to.use[match(datetime1, alldts[is.dts1])], dts1),
cbind(.dt=as.character(datetime2), dts2),
by=".dt", all.x=TRUE, suffixes=c("_dts1", "_dts2")
)[-1]
关于r - 如何通过日期/时间值不匹配的公共(public)列合并 r 中的两个数据框,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15200851/