Вопрос по r – Время округления до ближайшей четверти часа

24

У меня есть вектор значений POSIXct, и я хотел бы округлить их до ближайшей четверти часа. Я не забочусь о дне. Как я могу преобразовать значения в часы и минуты?

Например, я хотел бы значение

"2012-05-30 20:41:21 UTC"

быть

"20:45"

Ваш Ответ

7   ответов
14

format(strptime("1970-01-01", "%Y-%m-%d", tz="UTC") + round(as.numeric(your.time)/900)*900,"%H:%M")

должно сработать

12

старый вопрос с некоторыми полезными ответами до сих пор. Последний, созданный жирафом, кажется самым элегантным. однако, это будет не floor_date, а round_date, что и поможет:

lubridate::round_date(x, "15 minutes") 
Приятно отметить, добавлю.
3

round.POSIXt() а такжеtrunc.POSIXt() делать.

myRound <- function (x, convert = TRUE)  {
    x <- as.POSIXlt(x)
    mins <- x$min
    mult <- mins %/% 15
    remain <- mins %% 15
    if(remain > 7L || (remain == 7L && x$sec > 29))
        mult <- mult + 1
    if(mult > 3) {
        x$min <- 0
        x <- x + 3600
    } else {
        x$min <- 15 * mult
    }
    x <- trunc.POSIXt(x, units = "mins")
    if(convert) {
        x <- format(x, format = "%H:%M")
    }
    x
}

Это дает:

> tmp <- as.POSIXct("2012-05-30 20:41:21 UTC")
> myRound(tmp)
[1] "20:45"
> myRound(tmp, convert = FALSE)
[1] "2012-05-30 20:45:00 BST"
> tmp2 <- as.POSIXct("2012-05-30 20:55:21 UTC")
> myRound(tmp2)
[1] "21:00"
> myRound(tmp2, convert = FALSE)
[1] "2012-05-30 21:00:00 BST"
это, кажется, не векторизовано хорошо, попробуйтеstructure(c(1313331280, 1313334917, 1313334917, 1313340309, 1313340309, 1313340895, 1313340895, 1313341133, 1313341218, 1313341475), class = c("POSIXct", "POSIXt"), tzone = "UTC")
9

Старый вопрос, но хотелось бы отметить, чтоlubridate пакет обрабатывает это легко сейчас сfloor_date, Чтобы вырезать вектор объектов POSIXct с 15-минутными интервалами, используйте вот так.

x <- lubridate::floor_date(x, "15 minutes")

РЕДАКТИРОВАТЬ: Отмечено пользователем @ user3297928, использоватьlubridate::round_date(x, "15 minutes") для округления до ближайших 15 минут. Выше этажей это.

4

Вы можете использоватьalign.time функция в пакете xts для обработки округления, затемformat чтобы вернуть строку "ЧЧ: ММ":

R> library(xts)
R> p <- as.POSIXct("2012-05-30 20:41:21", tz="UTC")
R> a <- align.time(p, n=60*15)  # n is in seconds
R> format(a, "%H:%M")
[1] "20:45"
+1 шляпа подсказка тебе
округлить вниз:align.time(p - lubridate::minutes(15), n=60*15)
@ Доминик: ты на 100% прав.align.time только округляется, и вы хотели округлить до ближайшей четверти часа. Извиняюсь.
Это элегантно, но, кажется, только округлить. Dominic
20

Ты можешь использоватьround, Хитрость заключается в том, чтобы разделить на 900 секунд (15 минут * 60 секунд) перед округлением и затем умножить на 900:

a <-as.POSIXlt("2012-05-30 20:41:21 UTC")
b <-as.POSIXlt(round(as.double(a)/(15*60))*(15*60),origin=(as.POSIXlt('1970-01-01')))
b
[1] "2012-05-30 20:45:00 EDT"

Чтобы получить только час и минуту, просто используйте формат

format(b,"%H:%M")
[1] "20:45"

as.character(format(b,"%H:%M"))
[1] "20:45"
Я не думаю, что нам нужно удваивать, а origin принимает строку, так что немного упрощено:b <-as.POSIXlt(round(as.numeric(a)/(15*60))*(15*60),origin='1970-01-01')
2

С помощьюIDate а такжеITime классы отdata.table иIPeriod класс (только что разработанный) мне удалось получить более масштабируемое решение.
Толькоshhhhimhuntingrabbits а такжеPLapointe ответить на вопрос с точки зренияnearest. xts Решение только раунды с использованиемceilingмойIPeriod Решение позволяет указатьceiling или жеfloor.
Чтобы добиться максимальной производительности, вам нужно хранить свои данные вIDate а такжеITime классы. Как видно на бенчмарке, производить дешевоPOSIXct отIDate/ITime/IPeriod, Ниже отметки времени около 22M:

# install only if you don't have
install.packages(c("microbenchmarkCore","data.table"),
                 repos = c("https://olafmersmann.github.io/drat",
                           "https://jangorecki.github.io/drat/iperiod"))
library(microbenchmarkCore)
library(data.table) # iunit branch
library(xts)
Sys.setenv(TZ="UTC")

## some source data: download and unzip csv
# "http://api.bitcoincharts.com/v1/csv/btceUSD.csv.gz"
# below benchmark on btceUSD.csv.gz 11-Oct-2015 11:35 133664801

system.nanotime(dt <- fread(".btceUSD.csv"))
# Read 21931266 rows and 3 (of 3) columns from 0.878 GB file in 00:00:10
#     user   system  elapsed 
#       NA       NA 9.048991

# take the timestamp only
x = as.POSIXct(dt[[1L]], tz="UTC", origin="1970-01-01")

# functions
shhhhi <- function(your.time){
    strptime("1970-01-01", "%Y-%m-%d", tz="UTC") + round(as.numeric(your.time)/900)*900
}

PLapointe <- function(a){
    as.POSIXlt(round(as.double(a)/(15*60))*(15*60),origin=(as.POSIXlt('1970-01-01')))
}

# myRound - not vectorized

# compare results
all.equal(
    format(shhhhi(x),"%H:%M"),
    format(PLapointe(x),"%H:%M")
)
# [1] TRUE
all.equal(
    format(align.time(x, n = 60*15),"%H:%M"),
    format(periodize(x, "mins", 15),"%H:%M")
)
# [1] TRUE

# IPeriod native input are IDate and ITime - will be tested too
idt <- IDateTime(x)
idate <- idt$idate
itime <- idt$itime
microbenchmark(times = 10L,
               shhhhi(x),
               PLapointe(x),
               xts = align.time(x, 15*60),
               posix_ip_posix = as.POSIXct(periodize(x, "mins", 15), tz="UTC"),
               posix_ip = periodize(x, "mins", 15),
               ip_posix = as.POSIXct(periodize(idate, itime, "mins", 15), tz="UTC"),
               ip = periodize(idate, itime, "mins", 15))
# Unit: microseconds
#            expr         min          lq         mean       median          uq         max neval
#       shhhhi(x)  960819.810  984970.363 1127272.6812 1167512.2765 1201770.895 1243706.235    10
#    PLapointe(x) 2322929.313 2440263.122 2617210.4264 2597772.9825 2792936.774 2981499.356    10
#             xts  453409.222  525738.163  581139.6768  546300.9395  677077.650  767609.155    10
#  posix_ip_posix 3314609.993 3499220.920 3641219.0876 3586822.9150 3654548.885 4457614.174    10
#        posix_ip 3010316.462 3066736.299 3157777.2361 3133693.0655 3234307.549 3401388.800    10
#        ip_posix     335.741     380.696     513.7420     543.3425     630.020     663.385    10
#              ip      98.031     151.471     207.7404     231.8200     262.037     278.789    10

IDate а такжеITime успешно масштабируется не только в этой конкретной задаче. Оба типа, так же, какIPeriod, являются целочисленными. Я предполагаю, что они также будут хорошо масштабироваться при объединении или группировании поdatetime поля.
Электронное руководство:https://jangorecki.github.io/drat/iperiod/

Спасибо за пост, но можете ли вы объяснить, как на самом делеinstall этот пакет? Это нигде не ясно из документации.
Абстракция ценна имо
@ssdecontrol См. первую команду в блоке кода для установки из опубликованного репо. В противном случае самый надежный способ получитьiunit филиал, вы можете добавить его в удаленный и оформить заказ в филиал. Он основан на октябрь 2015 г. data.table.
@ssdecontrol, но класс IPeriod просто числовой, любой пакет может справиться с этим только%/% У оператора, имеющего периоды в жестком коде, нет даже необходимости хранить какие-либо атрибуты, только число, без черной магии
Ах, я пропустил эту строчку. Я предполагал, что у вас будет отдельный пакет под названием «periodize» или «IPeriod»; или что-то, в отличие от форка data.table. Я думаю, что несколько прискорбно, чтоIDateTime материал упакован в data.table, а не в отдельный пакет

Похожие вопросы