#r #data.table #dplyr #lubridate
#r #data.table #dplyr #lubridate
Вопрос:
Рассмотрим следующий фрейм данных
time <-c('2016-04-13 23:07:45','2016-04-13 23:07:50','2016-04-13 23:08:45','2016-04-13 23:08:45'
,'2016-04-13 23:08:45','2016-04-13 23:07:50','2016-04-13 23:07:51')
group <-c('A','A','A','B','B','B','B')
value<- c(5,10,2,2,NA,1,4)
df<-data.frame(time,group,value)
> df
time group value
1 2016-04-13 23:07:45 A 5
2 2016-04-13 23:07:50 A 10
3 2016-04-13 23:08:45 A 2
4 2016-04-13 23:08:45 B 2
5 2016-04-13 23:08:45 B NA
6 2016-04-13 23:07:50 B 1
7 2016-04-13 23:07:51 B 4
Я хотел бы выполнить повторную выборку этого фрейма данных в 5 seconds level
— group level
и вычислить сумму value
для каждого time-interval
— group value
.
Интервал должен быть закрыт слева и открыт справа. Например, первая строка вывода должна быть
2016-04-13 23:07:45 A 5
поскольку первый 5-секундный интервал равен [2016-04-13 23:07:45, 2016-04-13 23:07:50[
Как я могу это сделать в любом dplyr
или data.table
? Нужно ли импортировать lubridate
временные метки?
Комментарии:
1. исправлены опечатки. большое спасибо!
2. Я думаю
foverlaps
data.table
, что в этом случае может быть полезно. Я посмотрю, смогу ли я выполнить asnwer3.спасибо, или, может быть, с помощью a
dplyr
group_by
? Понятия не имею4. @Noobie, просто для пояснения, я предполагаю, что интервал открыт справа … (вот почему вы не включаете
23:07:50
значение в сумму первой строки вывода)? Кроме того, что вы делаете с группами, если они разные, но происходят в один и тот же интервал времени (например7 2016-04-13 23:07:51 B 4
, и, допустим, следующая строка была8 2016-04-13 23:07:53 A 12
)? Мы игнорируем разницу в группах и просто суммируем 1, 4 и 12?5. Привет, Джозеф, да, закрыто слева и открыто справа. Для вторых точек агрегирование выполняется на уровне временной группы, поэтому две точные временные метки для двух разных групп никогда не будут перепутаны
Ответ №1:
С последней версией (1.9.8 ) data.table
:
library(data.table)
# convert to data.table, fix time, add future time
setDT(df)
df[, time := as.POSIXct(time)][, time.5s := time 5]
# use non-equi join to filter on the required intervals and sum
df[, newval := df[df, on = .(group, time < time.5s, time >= time),
sum(value, na.rm = T), by = .EACHI]$V1]
df
# time group value time.5s newval
#1: 2016-04-13 23:07:45 A 5 2016-04-13 23:07:50 5
#2: 2016-04-13 23:07:50 A 10 2016-04-13 23:07:55 10
#3: 2016-04-13 23:08:45 A 2 2016-04-13 23:08:50 2
#4: 2016-04-13 23:08:45 B 2 2016-04-13 23:08:50 2
#5: 2016-04-13 23:08:45 B NA 2016-04-13 23:08:50 2
#6: 2016-04-13 23:07:50 B 1 2016-04-13 23:07:55 5
#7: 2016-04-13 23:07:51 B 4 2016-04-13 23:07:56 4
Комментарии:
1. Просто измените неравенства
on
со строгих на нестрогие по мере необходимости.2. разве 6 и 7 не должны быть сгруппированы вместе? Они находятся в одном и том же 5-секундном интервале. Похоже, вы просто добавляете 5 секунд к каждому результату.
3. верно. 6 и 7 следует суммировать вместе. хороший улов @JosephWood
4. # 7 находится в будущем интервале # 6, но # 6 не находится в будущем интервале # 7 .. если вы ищете совпадение future past, добавьте
time.neg.5s := time - 5
столбец и сравните с ним5. У меня такое чувство, что время от времени.5s могут быть созданы в одном вызове, но я не уверен, что это в любом случае улучшит скорость
Ответ №2:
Как насчет этого:
library(dplyr)
Group5 <- function(myDf) {
myDf$time <- ymd_hms(myDf$time)
myDf$timeGroup <- floor_date(myDf$time, unit = "5 seconds")
summarise(myDf %>% group_by(group, timeGroup), sum(value, na.rm = TRUE))
}
Group5(df)
Source: local data frame [5 x 3]
Groups: group [?]
group timeGroup `sum(value, na.rm = TRUE)`
<fctr> <dttm> <dbl>
1 A 2016-04-13 23:07:45 5
2 A 2016-04-13 23:07:50 10
3 A 2016-04-13 23:08:45 2
4 B 2016-04-13 23:07:50 5
5 B 2016-04-13 23:08:45 2
Он использует преимущества floor_date
и ymd_hms
из lubridate
, чтобы поместить время каждой даты в правильное групповое время.
Вот более экзотический пример:
set.seed(500)
time <- ymd_hms('2016-04-13 23:07:45') sample(-10^3:10^3, 10^5, replace=TRUE)
group <- rep(LETTERS[1:20], each = 5000)
value <- rep(NA, 10^5)
value[sample(10^5, 95000)] <- sample(100, 95000, replace=TRUE)
df2 <- data.frame(time,group,value)
head(df2)
time group value
1 2016-04-13 23:18:53 A 53
2 2016-04-13 23:15:15 A NA
3 2016-04-13 23:23:36 A 40
4 2016-04-13 23:06:40 A 23
5 2016-04-13 23:18:10 A 74
6 2016-04-13 22:57:56 A 65
Вызывая его, мы имеем:
Group5(df2)
Source: local data frame [8,020 x 3]
Groups: group [?]
group timeGroup `sum(value, na.rm = TRUE)`
<fctr> <dttm> <int>
1 A 2016-04-13 22:51:05 379
2 A 2016-04-13 22:51:10 646
3 A 2016-04-13 22:51:15 391
4 A 2016-04-13 22:51:20 1118
5 A 2016-04-13 22:51:25 745
6 A 2016-04-13 22:51:30 546
7 A 2016-04-13 22:51:35 884
8 A 2016-04-13 22:51:40 711
9 A 2016-04-13 22:51:45 526
10 A 2016-04-13 22:51:50 484
# ... with 8,010 more rows
Комментарии:
1. Хорошая догадка из вопроса Op, не думал о округлении с интервалом в 5 секунд.
Ответ №3:
Если вы хотите иметь отдельные объекты данных для каждой группы, вы могли бы использовать xts
для решения вашей проблемы вместо data.table
, для каждого группового объекта. xts period.apply
автоматически обработает ваш интервал, если он закрыт с левой стороны, но открыт и справа (что действительно полезно для агрегирования финансовых тиковых данных с частотой баров. Вы не получите двойного подсчета тиков на краях интервала для последовательных баров / интервалов):
time <-c('2016-04-13 23:07:45','2016-04-13 23:07:55','2016-04-13 23:08:45','2016-04-13 23:08:45'
,'2016-04-13 23:08:45','2016-04-13 23:07:50','2016-04-13 23:07:51')
group <-c('A','A','A','B','B','B','B')
value<- c(5,10,2,2,NA,1,4)
df=data.frame(time,group,value)
library(quantmod)
library(lubridate)
df$time = ymd_hms(df$time)
# In this example, model group B object: (You can easily generalise this with a loop or lapply over multiple groups)
df_grp <- df[df$group == "B", ]
x.df_grp <- xts(df_grp$value, order.by = df_grp$time)
ep <- endpoints(x.df_grp, on = "seconds", k = 5)
# You can replace sum by any useful function. Pass in extra arguments to period.apply that correspond to FUN, here na.rm = T, to avoid having sum returning NA in your group B row:
x.df_grp_5sec <- period.apply(x.df_grp, ep, FUN = sum, na.rm = TRUE)
# Align timestamps to end of each 5 sec interval by default (helps avoid lookforward bias when merging time series data on different time frequencies):
x.df_grp_5sec <- align.time(x.df_grp_5sec, 5)
# Now record timestamps at start of each 5 sec interval:
.index(x.df_grp_5sec) <- .index(x.df_grp_5sec) - 5
#result:
> x.df_grp_5sec
[,1]
2016-04-13 23:07:50 5
2016-04-13 23:08:45 2
Ответ №4:
Лучшая идея, к которой я пришел data.table
:
library(data.table)
setDT(df)
df[, result:={lv=df$group==group; dt=difftime( df$time, time, units="sec"); print(dt); sum(df$value[lv amp; dt >= 0 amp; dt < 5],na.rm=TRUE)},by=1:nrow(df)]
Вывод:
time group value result
1: 2016-04-13 23:07:45 A 5 5
2: 2016-04-13 23:07:50 A 10 10
3: 2016-04-13 23:08:45 A 2 2
4: 2016-04-13 23:08:45 B 2 2
5: 2016-04-13 23:08:45 B NA 2
6: 2016-04-13 23:07:50 B 1 5
7: 2016-04-13 23:07:51 B 4 4
j
Часть в деталях:
lv=df$group==group # Create a logical vector to filter at end
dt=abs( difftime( df$time, time, units="sec")) # compute the time difference in seconds between current row and all others
sum(df$value[lv amp; dt >= 0 amp; dt < 5]) # Sum the values where in same group and the difference in seconds is between 0 and 5 secs, 0 included, 5 excluded
это result:={}
позволяет нам создавать результат в виде вызова функции. это by=1:nrow(df)
позволяет работать строка за строкой.
И отфильтровать полученный результат, чтобы получить только начальную точку:
> df[,.SD[!duplicated(result)],by=group]
group time value result
1: A 2016-04-13 23:07:45 5 5
2: A 2016-04-13 23:07:50 10 10
3: A 2016-04-13 23:08:45 2 2
4: B 2016-04-13 23:08:45 2 2
5: B 2016-04-13 23:07:50 1 5
6: B 2016-04-13 23:07:51 4 4
Комментарии:
1. Почему у вас
result
для строки 7 равно 5? Предыдущая строка не входит в диапазон[time, time 5)
.2. Строка @eddi 6 находится в диапазоне в пределах 5 секунд, если не требуется, просто удалите вызов abs в разное время
3. @Eddie, я экстраполировал вопрос Op о диапазоне, действительно, вызов abs является излишним для ответа в этом случае
4. @Tensibai, не уверен, вопрос это или предложение… должны ли мы удалять NAs при суммировании?
5. Кроме того, вам необходимо изменить
data.frame
наdata.table
.. мне потребовалось пару раз получать ошибки, прежде чем я понял, что мне нужноdata.table
.