Я рассчитываю получить самое близкое предыдущее значение для каждого 15-минутного интервала (то есть 12:00:00, 12:15:00, 12:30:00 AM) для произвольного числа показаний между интервалами.
Например, я ищу 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
. Я думаю, что nalocf
функция из zoo
и dplyr
или data.table
может привести меня туда-сюда. Открыто для других пакетов.
3 ответа
Это может быть хорошим приложением для data.table
переходящих объединений с опцией «ближайший».
Первым шагом является получение данных в объект типа data.table
с правильно отформатированной отметкой времени POSIXct
.
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
может быть хорошим местом для начала, если вы вообще не использовали data.table
перед.
Выполнение help("data.table")
даст вам краткую рецензию, но есть один хороший пример некоторых возможностей, написанных Беном Горманом в его блоге - Анализ Гормана: R - переходящие объединения таблиц Data.Table и другие работы Робера Норберга в его блоге bRogramming: Понимание data.table Rolling Joins, которое может помочь лучше понять.
Обновление. Похоже, что вы, возможно, захотите переносить только наблюдения, вместо того, чтобы обязательно указывать «самое близкое» значение - в этом случае вариант будет следующим:
(Используя тот же 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
Это может немного отличаться от вашего примера. Я не уверен, что ваш пример вывод на 100% правильно. например, как насчет данных от 12/8?
Библиотека lubridate имеет много полезных функций даты и времени. Это преобразует символ в дату и округляет до ближайшего периода. (Есть также функции floor_date
и ceiling_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
В зависимости от структуры входных данных и ожидаемого результата у ОП есть несколько вариантов.
Из вопроса и примера набора данных не совсем понятно, как должен выглядеть ожидаемый результат, если входные данные содержат промежутки , то есть интервалы, превышающие 15 минут, когда данные не были записаны. Как ОП хочет, чтобы пропуски во входных данных были отражены в результате?
РЕДАКТИРОВАТЬ: ОП предоставляет два немного разных набора данных. Оба используются ниже, чтобы продемонстрировать влияние входных данных на результат.
Варианты ниже будут использовать lubridate
и data.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"))]
EDIT: С другим набором данных (без усеченных секунд) мы получаем
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
.
EDIT: С другим набором данных (без усеченных секунд) мы получаем
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
Скользящее соединение (пробелы, заполненные данными в результате)
Это упрощенная версия подхода Мэтта
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
Здесь пробел заполняется данными, которые были скопированы из последней доступной величины. Из анализа результата больше не видно, что во входных данных был пробел.
EDIT: С другим набором данных (без усеченных секунд) мы получаем
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
Данные
ОП предоставил данные в виде 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))
РЕДАКТИРОВАТЬ: ОП предоставляет два немного разных набора данных:
- как
dput()
с усеченными секундами (df
в этом ответе) - как напечатано
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)]
Похожие вопросы
Новые вопросы
r
R - это бесплатный язык программирования с открытым исходным кодом и программная среда для статистических вычислений, биоинформатики, визуализации и общих вычислений. Пожалуйста, предоставьте минимальные и воспроизводимые примеры вместе с желаемым результатом. Используйте dput () для данных и укажите все небазовые пакеты с вызовами library (). Не вставляйте изображения для данных или кода, вместо этого используйте блоки кода с отступом. По вопросам, связанным со статистикой, используйте https://stats.stackexchange.com.