【问题标题】:Closest Value for each 15 min interval每 15 分钟间隔的最接近值
【发布时间】:2018-08-20 19:23:43
【问题描述】:

我希望每 15 分钟间隔(即上午 12:00:00、上午 12:15:00、上午 12:30:00)获得最接近的先前读数,以获取间隔之间任意数量的读数。

例如,我希望拥有df

Timestamp   Value (kW)
8/12/2018 23:00:06  51
8/13/2018 0:00:16   52
8/13/2018 0:10:26   53
8/13/2018 0:14:36   54
8/13/2018 0:15:00   55
8/13/2018 0:19:57   56
8/13/2018 0:29:09   57
8/13/2018 0:38:17   58
8/13/2018 0:44:59   59
8/13/2018 0:45:00   60
8/13/2018 0:58:47   61
8/13/2018 1:01:57   62


structure(list(Timestamp = c("8/12/2018 23:00:00", "8/13/2018 0:00:00", 
"8/13/2018 0:10:00", "8/13/2018 0:14:00", "8/13/2018 0:15:00", 
"8/13/2018 0:19:00", "8/13/2018 0:29:00", "8/13/2018 0:38:00", 
"8/13/2018 0:44:00", "8/13/2018 0:45:00", "8/13/2018 0:58:00", 
"8/13/2018 1:01:00"), Value..kW. = 51:62), .Names = c("Timestamp", 
"Value..kW."), class = "data.frame", row.names = c(NA, -12L))

看看更接近df2的东西:

Interval    Value
8/13/2018 0:00:00   51
8/13/2018 0:15:00   55
8/13/2018 0:30:00   57
8/13/2018 0:45:00   60
8/13/2018 1:00:00   61

请注意seconds。 我在想zoodplyrdata.table 中的nalocf 函数可以让我成功。对其他包开放。

【问题讨论】:

  • 您提供的结构不包括秒数
  • 已更新 - 谢谢!
  • 为什么8/12/2018 23:15:008/13/2018 01:15:00 不获取值?听起来你有一个隐含的区间,你想要它的值,但你没有提到它。
  • 另外,如果它是您想要的最接近的 previous 值(正如预期的输出和您的开场白所示),您也应该更新标题以反映这一点。您得到的两个答案都回答了最接近值的问题,而不是最接近的先前值。
  • 同样,structure(...) 中提供的数据与打印的df 不匹配。在structure(..) 中,秒数已被截断。

标签: r dplyr data.table zoo


【解决方案1】:

对于带有“最近”选项的data.table 滚动连接,这可能是一个很好的应用程序。

第一步是将数据放入具有正确格式的POSIXct时间戳的data.table类型对象中。

library(data.table)

DT <- structure(list(Timestamp = c("8/12/2018 23:00:00", "8/13/2018 0:00:00", 
                             "8/13/2018 0:10:00", "8/13/2018 0:14:00", "8/13/2018 0:15:00", 
                             "8/13/2018 0:19:00", "8/13/2018 0:29:00", "8/13/2018 0:38:00", 
                             "8/13/2018 0:44:00", "8/13/2018 0:45:00", "8/13/2018 0:58:00", 
                             "8/13/2018 1:01:00"), Value..kW. = 51:62), .Names = c("Timestamp", 
                                                                                   "Value..kW."), class = "data.frame", row.names = c(NA, -12L))
## Convert from data.frame to data.table
setDT(DT)

## Convert to POSIXct
DT[,Timestamp := as.POSIXct(Timestamp, format = "%m/%d/%Y %H:%M:%S", tz = "UTC")]

一旦你有了它,你就可以用你的 15 分钟间隔序列生成另一个表。

## Get Start and Ends
Start <- min(as.POSIXct(cut.POSIXt(DT[,Timestamp],breaks = c("15 min")), tz = "UTC"))
End <- max(as.POSIXct(cut.POSIXt(DT[,Timestamp],breaks = c("15 min")), tz = "UTC"))
## Generate data.table with a sequence
SummaryDT <- data.table(TimeStamp15 = seq.POSIXt(from = Start, to = End, by = "15 min"))

print(SummaryDT)
#            TimeStamp15
# 1: 2018-08-12 23:00:00
# 2: 2018-08-12 23:15:00
# 3: 2018-08-12 23:30:00
# 4: 2018-08-12 23:45:00
# 5: 2018-08-13 00:00:00
# 6: 2018-08-13 00:15:00
# 7: 2018-08-13 00:30:00
# 8: 2018-08-13 00:45:00
# 9: 2018-08-13 01:00:00

然后,您可以使用滚动连接更新设置键并获取最接近每 15 分钟时间的值。

## Set keys
setkey(SummaryDT,TimeStamp15)
setkey(DT,Timestamp)

## Create a new column in SummaryDT with the closest measurement
SummaryDT[DT, Closest_Value_kW := `i.Value..kW.` , roll = "nearest"]
print(SummaryDT)
#            TimeStamp15 Closest_Value_kW
# 1: 2018-08-12 23:00:00               51
# 2: 2018-08-12 23:15:00               NA
# 3: 2018-08-12 23:30:00               NA
# 4: 2018-08-12 23:45:00               NA
# 5: 2018-08-13 00:00:00               52
# 6: 2018-08-13 00:15:00               56
# 7: 2018-08-13 00:30:00               57
# 8: 2018-08-13 00:45:00               60
# 9: 2018-08-13 01:00:00               62

如果您是 data.table 的新手,这可能有点难以理解,此示例处于频谱的高级端——data.table 网站上的 Getting Started 页面可能是一个很好的地方如果您之前根本没有使用过data.table,请开始。

执行 help("data.table") 会给你一个简洁的文章,但是 Ben Gorman 在他的博客上写的一些功能的一个很好的例子 -- Gorman Analysis: R – Data.Table Rolling Joins 和另一个由 Rober Norberg 在他的博客上写的 @987654323 @ 这可能有助于更好地理解。

更新:看起来您可能只想继续观察而不是一定要做“最接近”的值——在这种情况下,选项如下:

(使用相同的DT作为起点)

## Get Start and Ends
Start <- min(as.POSIXct(cut.POSIXt(DT[,Timestamp],breaks = c("15 min")), tz = "UTC"))
End <- max(as.POSIXct(cut.POSIXt(DT[,Timestamp],breaks = c("15 min"),), tz = "UTC"))
## Generate data.table with a sequence
SummaryDT <-data.table(TimeStamp15 = seq.POSIXt(from = Start, to = End, by = "15 min"))

## Set keys
setkey(SummaryDT,TimeStamp15)
setkey(DT,Timestamp)
## Do a rolling join
FinalDT <- DT[SummaryDT, roll = +Inf]

print(FinalDT)
#              Timestamp Value..kW.
# 1: 2018-08-12 23:00:00         51
# 2: 2018-08-12 23:15:00         51
# 3: 2018-08-12 23:30:00         51
# 4: 2018-08-12 23:45:00         51
# 5: 2018-08-13 00:00:00         52
# 6: 2018-08-13 00:15:00         55
# 7: 2018-08-13 00:30:00         57
# 8: 2018-08-13 00:45:00         60
# 9: 2018-08-13 01:00:00         61

【讨论】:

  • (+1) 我喜欢data.table 滚动连接,在这里提及它们很好,但是FWIW 我也认为对于这样的问题,你实际上不需要这样做有点矫枉过正join 两个时间序列在一起,而不是你真的只想在一个时间序列中舍入值。不过,在真正的连接用例中,稍后可能会派上用场。
【解决方案2】:

这可能与您的示例结果略有不同。我不确定您的示例输出是否 100% 正确。例如 12/8 的数据呢?

library lubridate 具有许多有用的日期时间功能。这会将字符转换为日期并四舍五入到最接近的句点。 (还有floor_dateceiling_date 函数分别向下或向上取整)。

library(dplyr) 
library(lubridate)
df %>% 
  # ensure timestamp is a date type and round to the nearest fifteen minutes
  mutate(ts = mdy_hm(Timestamp),
         period = round_date(ts, unit = "15 minutes")) %>%
  # group into periods 
  group_by(period) %>%
  # grab the first row in each period, orderd by the timestamp (use -1 for last)
  top_n(-1, ts) %>%
  # order the reuslt
  arrange(period)

#   Timestamp       Value..kW. ts                  period             
#   <chr>                <int> <dttm>              <dttm>             
# 1 8/12/2018 23:00         51 2018-08-12 23:00:00 2018-08-12 23:00:00
# 2 8/13/2018 0:00          52 2018-08-13 00:00:00 2018-08-13 00:00:00
# 3 8/13/2018 0:10          53 2018-08-13 00:10:00 2018-08-13 00:15:00
# 4 8/13/2018 0:29          57 2018-08-13 00:29:00 2018-08-13 00:30:00
# 5 8/13/2018 0:38          58 2018-08-13 00:38:00 2018-08-13 00:45:00

【讨论】:

  • 尽管标题另有说明,但似乎最接近的 previous 值是这里的目标。所以你会想要ceiling_date() 而不是round_date()
  • @MikkoMarttila 获取以前的值,不是floor_date,而不是ceiling_date?天花板功能向上/进入未来
  • @camille 您希望将原始时间向上舍入,因为时间 00:01 的值应计入时间 00:15 的测量,而不是 00:00 尚未发生的测量。
【解决方案3】:

根据输入数据的结构和预期结果,OP 有多种选择。

从问题和示例数据集来看,如果输入数据包含间隙(即没有记录数据的时间间隔超过 15 分钟),预期结果应该是什么样子并不完全清楚. OP 希望输入数据中的差距如何反映在结果中?

编辑: OP 提供了两个略有不同的数据集。下面使用两者来演示输入数据对结果的影响。

以下变体将使用lubridatedata.table。假设df已经被Timesstamp订购。

准备

这是所有变体都需要的:

library(lubridate)
library(data.table)
setDT(df)[, Timestamp := mdy_hms(Timestamp)]

聚合到下一个 15 分钟间隔(结果有间隙)

最简单的解决方案是聚合到下一个 15 分钟间隔:

df[, .SD[.N], by = .(Interval = ceiling_date(Timestamp, "15 min"))]
              Interval Value..kW.
1: 2018-08-12 23:00:00         51
2: 2018-08-13 00:00:00         52
3: 2018-08-13 00:15:00         55
4: 2018-08-13 00:30:00         57
5: 2018-08-13 00:45:00         60
6: 2018-08-13 01:00:00         61
7: 2018-08-13 01:15:00         62

请注意,第 1 行和第 2 行之间有 1 小时的间隔,其中缺少 3 个间隔。

为了完整起见,这里有一个变体,它也适用于无序数据。

df[, .SD[which.max(Timestamp)], keyby = .(Interval = ceiling_date(Timestamp, "15 min"))]

编辑:我们得到另一个数据集(没有截断秒数)

df0[, .SD[.N], by = .(Interval = ceiling_date(Timestamp, "15 min"))]
1: 2018-08-12 23:15:00         51
2: 2018-08-13 00:15:00         55
3: 2018-08-13 00:30:00         57
4: 2018-08-13 00:45:00         60
5: 2018-08-13 01:00:00         61
6: 2018-08-13 01:15:00         62

请注意,在没有截断秒数的情况下,这些值将移动到下一个间隔。

汇总到下一个 15 分钟间隔,结果中没有间隙

step <- "15 min"
df[, .SD[.N], by = .(Interval = ceiling_date(Timestamp, step))][
  .(seq(min(Interval), max(Interval), step)), on = .(Interval = V1)]

这里我们加入一系列时间戳来完成缺失的间隔:

               Interval Value..kW.
 1: 2018-08-12 23:00:00         51
 2: 2018-08-12 23:15:00         NA
 3: 2018-08-12 23:30:00         NA
 4: 2018-08-12 23:45:00         NA
 5: 2018-08-13 00:00:00         52
 6: 2018-08-13 00:15:00         55
 7: 2018-08-13 00:30:00         57
 8: 2018-08-13 00:45:00         60
 9: 2018-08-13 01:00:00         61
10: 2018-08-13 01:15:00         62

现在,NA 值可以在结果中看到差距。

编辑:我们得到另一个数据集(没有截断秒数)

df0[, .SD[.N], by = .(Interval = ceiling_date(Timestamp, step))][
  .(seq(min(Interval), max(Interval), step)), on = .(Interval = V1)]
              Interval Value..kW.
1: 2018-08-12 23:15:00         51
2: 2018-08-12 23:30:00         NA
3: 2018-08-12 23:45:00         NA
4: 2018-08-13 00:00:00         NA
5: 2018-08-13 00:15:00         55
6: 2018-08-13 00:30:00         57
7: 2018-08-13 00:45:00         60
8: 2018-08-13 01:00:00         61
9: 2018-08-13 01:15:00         62

滚动连接(结果中的数据填充间隙)

这是Matt's approach的精简版

step = "15 min"
df[.(seq(floor_date(min(Timestamp), step), ceiling_date(max(Timestamp), step),by = step)), 
   on = .(Timestamp = V1), roll = TRUE]
              Timestamp Value..kW.
 1: 2018-08-12 23:00:00         51
 2: 2018-08-12 23:15:00         51
 3: 2018-08-12 23:30:00         51
 4: 2018-08-12 23:45:00         51
 5: 2018-08-13 00:00:00         52
 6: 2018-08-13 00:15:00         55
 7: 2018-08-13 00:30:00         57
 8: 2018-08-13 00:45:00         60
 9: 2018-08-13 01:00:00         61
10: 2018-08-13 01:15:00         62

在这里,空白填充了从最新可用值复制的数据。从结果来看,已经看不出输入数据有差距。

编辑:我们得到另一个数据集(没有截断秒数)

df0[.(seq(floor_date(min(Timestamp), step), ceiling_date(max(Timestamp), step),by = step)), 
   on = .(Timestamp = V1), roll = TRUE]
              Timestamp Value..kW.
 1: 2018-08-12 23:00:00         NA
 2: 2018-08-12 23:15:00         51
 3: 2018-08-12 23:30:00         51
 4: 2018-08-12 23:45:00         51
 5: 2018-08-13 00:00:00         51
 6: 2018-08-13 00:15:00         55
 7: 2018-08-13 00:30:00         57
 8: 2018-08-13 00:45:00         60
 9: 2018-08-13 01:00:00         61
10: 2018-08-13 01:15:00         62

在这里,我们在第一行有一个未填补的空白。这是由间隔序列的构造方式引起的。稍作修改即可避免

df0[.(seq(ceiling_date(min(Timestamp), step), ceiling_date(max(Timestamp), step),by = step)), 
    on = .(Timestamp = V1), roll = TRUE]

             Timestamp Value..kW.
1: 2018-08-12 23:15:00         51
2: 2018-08-12 23:30:00         51
3: 2018-08-12 23:45:00         51
4: 2018-08-13 00:00:00         51
5: 2018-08-13 00:15:00         55
6: 2018-08-13 00:30:00         57
7: 2018-08-13 00:45:00         60
8: 2018-08-13 01:00:00         61
9: 2018-08-13 01:15:00         62

数据

OP 提供的数据为dput()

df <-
structure(list(Timestamp = c("8/12/2018 23:00:00", "8/13/2018 0:00:00", 
"8/13/2018 0:10:00", "8/13/2018 0:14:00", "8/13/2018 0:15:00", 
"8/13/2018 0:19:00", "8/13/2018 0:29:00", "8/13/2018 0:38:00", 
"8/13/2018 0:44:00", "8/13/2018 0:45:00", "8/13/2018 0:58:00", 
"8/13/2018 1:01:00"), Value..kW. = 51:62), .Names = c("Timestamp", 
"Value..kW."), class = "data.frame", row.names = c(NA, -12L))

编辑: OP 提供了两个略有不同的数据集:

  1. dput() 截断秒数(此答案为df
  2. 在没有截断秒的问题中打印df(此答案中为df0

这种细微的差异会影响结果。所以,这里是打印出来的数据集:

df0 <- data.frame(
readr::read_table("        Timestamp   Value.(kW)
8/12/2018 23:00:06  51
8/13/2018 0:00:16   52
8/13/2018 0:10:26   53
8/13/2018 0:14:36   54
8/13/2018 0:15:00   55
8/13/2018 0:19:57   56
8/13/2018 0:29:09   57
8/13/2018 0:38:17   58
8/13/2018 0:44:59   59
8/13/2018 0:45:00   60
8/13/2018 0:58:47   61
8/13/2018 1:01:57   62
"))
# prepare
library(lubridate)
library(data.table)
setDT(df0)[, Timestamp := mdy_hms(Timestamp)]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-24
    • 2012-12-17
    • 1970-01-01
    • 1970-01-01
    • 2023-01-13
    • 1970-01-01
    • 2023-04-08
    相关资源
    最近更新 更多