根据另一个数据帧在R数据帧中创建变量

时间:2015-01-03 16:57:54

标签: r

在浪费了将近一天后,我正在寻求帮助。我有一个大数据帧(bdf)和一个小数据帧(sdf)。我想将变量z添加到bdf,具体取决于sdf $ y的值(它随时间变量而变化)。

这是一个可重复的例子:

bdf <- data.frame(tb = seq(as.POSIXct("2013-05-19 17:11:22 GMT", tz="GMT"), by=5624*24, length.out=10))

bdf
                tb
1  2013-05-19 17:11:22
2  2013-05-21 06:40:58
3  2013-05-22 20:10:34
4  2013-05-24 09:40:10
5  2013-05-25 23:09:46
6  2013-05-27 12:39:22
7  2013-05-29 02:08:58
8  2013-05-30 15:38:34
9  2013-06-01 05:08:10
10 2013-06-02 18:37:46


sdf <- data.frame(ts = as.POSIXct(c("2013-05-22", "2013-05-25", "2013-05-30"), tz="GMT"), y = c(0.2, -0.1, 0.3))

> sdf
      ts    y
1 2013-05-22  0.2
2 2013-05-25 -0.1
3 2013-05-30  0.3

我想在bdf中使用以下sdf $ y值创建变量z:

  • 0.2对于行,其中bdf $ tb的范围从第一个bdf $ tb到sdf $ ts的第一个和第二个值之间的中间值。在这个简单的例子中,这是dbf的第1行到第3行的情况,其时间bdf $ tb低于“2013-05-23 12:00:00 GMT”。

  • -0.1对于行,其中bdf $ tb的范围从sdf $ ts的第1和第2值之间的中间到sdf $ ts的第2和第3值之间的中间。在这个简单的例子中,这是dbf的第4行和第5行的情况,在“2013-05-23 12:00:00 GMT”和“2013-05-27 12:00:00 GMT”之间的时间为bdf $ tb

  • 对于所有行,其中bdf $ tb的范围是从sdf $ ts的第2和第3值到bdf $ tb的最后一个值的中间值。在这个简单的例子中,这是dbf的第1到6到10行,其时间大于“2013-05-23 12:00:00 GMT”。

因此,最后,大数据帧bdf应如下所示:

                 tb    z
1  2013-05-19 17:11:22  0.2
2  2013-05-21 06:40:58  0.2
3  2013-05-22 20:10:34  0.2
4  2013-05-24 09:40:10 -0.1
5  2013-05-25 23:09:46 -0.1
6  2013-05-27 12:39:22  0.3
7  2013-05-29 02:08:58  0.3
8  2013-05-30 15:38:34  0.3
9  2013-06-01 05:08:10  0.3
10 2013-06-02 18:37:46  0.3

我无法成功使用dplyr :: mutate并且无处使用循环...任何帮助都将非常感激。我希望我清楚地将这个问题描述为遵守礼仪(这是我的第一个问题)。

4 个答案:

答案 0 :(得分:7)

以下是使用data.table&#39> 滚动加入的解决方案:

require(data.table)
setkey(setDT(sdf), ts)
sdf[bdf, roll = "nearest"]
#                      ts    y
#  1: 2013-05-19 17:11:22  0.2
#  2: 2013-05-21 06:40:58  0.2
#  3: 2013-05-22 20:10:34  0.2
#  4: 2013-05-24 09:40:10 -0.1
#  5: 2013-05-25 23:09:46 -0.1
#  6: 2013-05-27 12:39:22  0.3
#  7: 2013-05-29 02:08:58  0.3
#  8: 2013-05-30 15:38:34  0.3
#  9: 2013-06-01 05:08:10  0.3
# 10: 2013-06-02 18:37:46  0.3
  • setDT通过引用将data.frame转换为data.table

  • setkey按提供的列按递增顺序对data.table 按引用排序,并将这些列标记为键列(以便我们可以稍后加入这些关键专栏。

  • 在data.table中,x[i]i是data.table时执行连接。如果您还不熟悉,我会推荐您this answer来了解data.table联接。

  • x[i]执行 equi-join 。也就是说,它在x中为i中的每一行找到匹配的行索引,然后从x中提取这些行以返回连接结果以及来自i的相应行。如果i中的某行未在x中找到匹配的行索引,则默认情况下该行NA将为x

    但是,x[i, roll = .]执行滚动加入。当没有匹配时,最后一个观察结果是前进(roll = TRUE-Inf),或者下一个观察结果可以向后进行(roll = Inf),或者滚动到最近的值(roll = "nearest")。在这种情况下,您需要roll = "nearest" IIUC。

HTH

答案 1 :(得分:3)

这是我的方法:

library(zoo)
m <- c(rollmean(as.POSIXct(sdf$ts), 2), Inf)
transform(bdf, z = sdf$y[sapply(tb, function(x) which.max(x < m))])
#                    tb    z
#1  2013-05-19 17:11:22  0.2
#2  2013-05-21 06:40:58  0.2
#3  2013-05-22 20:10:34  0.2
#4  2013-05-24 09:40:10 -0.1
#5  2013-05-25 23:09:46 -0.1
#6  2013-05-27 12:39:22  0.3
#7  2013-05-29 02:08:58  0.3
#8  2013-05-30 15:38:34  0.3
#9  2013-06-01 05:08:10  0.3
#10 2013-06-02 18:37:46  0.3

更新:已删除转换为数字(不需要)

简要说明:

  • as.POSIXct(sdf$ts)将日期转换为POSIXct样式的日期时间
  • rollmean(as.POSIXct(sdf$ts), 2)计算每两个连续行的滚动平均值。这恰好是您想要用于分离观察的时间。 rollmean来自包zoo。计算rollmean(..,2)意味着与输入向量相比,输出向量缩短了1。
  • 这就是我将rollmean的结果包装在c(.., Inf)中的原因,这意味着将无穷大值作为最后一个值添加到rollmean向量中。这样可以确保返回zsdf的最后一个条目(具体示例中为0.3)。
  • 我使用transformz列添加到bdf
  • sapply(tb, function(x) which.max(x < m))循环遍历bdf$tb中的条目,并为每个条目计算bdf$tb小于(早于)m(保存向量)的最大索引rollmean条目)。每个bdf$tb条目仅返回最大(最新)索引。
  • sdf$y[sapply(tb, function(x) which.max(x < m))]中使用索引向量来提取sdf$y的相应元素,然后将其存储/复制到z中的新bdf

希望有所帮助

答案 2 :(得分:3)

编辑注释:我最初得到的结果与我现在认为与我对R difftime对象缺乏了解有关的结果略有不同。 POSIXt对象中的时区对我来说仍然是一个谜,但我现在看到,当我强迫一个&#39; difftime&#39;反对&#39;数字&#39;我在&#34;天&#34;中获得了价值。

findInterval函数作为索引创建函数非常有用,该函数映射值 - 向量,其中一个值具有多个相邻的非重叠区间。你真的只有两个时间点分成三个区间。

bdf$z <- c(0.2,-0.1,0.3)[findInterval(bdf$tb, 
                c(-Inf, 
  sdf$ts[2] - 0.5*as.numeric(difftime(sdf$ts[2], sdf$ts[1], units="secs")), 
  sdf$ts[3] - 0.5*as.numeric(difftime(sdf$ts[3], sdf$ts[2],units="sec")), 
                 Inf))]

> bdf
                    tb    z
1  2013-05-19 17:11:22  0.2
2  2013-05-21 06:40:58  0.2
3  2013-05-22 20:10:34  0.2
4  2013-05-24 09:40:10 -0.1
5  2013-05-25 23:09:46 -0.1
6  2013-05-27 12:39:22  0.3
7  2013-05-29 02:08:58  0.3
8  2013-05-30 15:38:34  0.3
9  2013-06-01 05:08:10  0.3
10 2013-06-02 18:37:46  0.3

我还检查了我的结果是否会受到findIntervals中的间隔是否在右边而不是左边(默认值)关闭的影响,并且没有看到差异。

答案 3 :(得分:3)

这似乎现在绝对没必要,但在基地R

bdf$z <- numeric(nrow(bdf))
for(i in seq_along(bdf$z)){
  ind <- which.min(abs(bdf$tb[i] - sdf$ts))
  bdf$z[i] <- sdf$y[ind]
}

虽然有点笨拙,但它具有清晰度的优势,可以轻松适应dplyr

library(dplyr)
bdf %>% rowwise() %>% 
  mutate(z= sdf$y[which.min(abs(as.numeric(tb)-as.numeric(sdf$ts)))])

#Source: local data frame [10 x 2]
#Groups: <by row>

#                    tb    z
#1  2013-05-19 17:11:22  0.2
#2  2013-05-21 06:40:58  0.2
#3  2013-05-22 20:10:34  0.2
#4  2013-05-24 09:40:10 -0.1
#5  2013-05-25 23:09:46 -0.1
#6  2013-05-27 12:39:22  0.3
#7  2013-05-29 02:08:58  0.3
#8  2013-05-30 15:38:34  0.3
#9  2013-06-01 05:08:10  0.3
#10 2013-06-02 18:37:46  0.3