Pergunta sobre datetime, r – Tempo de arredondamento para o quarto de hora mais próximo

24

Eu tenho um vetor de valores POSIXct e gostaria de arredondá-los para o quarto de hora mais próximo. Eu não me importo com o dia. Como faço para converter os valores para horas e minutos?

Por exemplo, eu gostaria do valor

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

ser estar

"20:45"

Sua resposta

7   a resposta
20

Você pode usarround. O truque é dividir por 900 segundos (15 minutos * 60 segundos) antes de arredondar e multiplicar por 900 depois:

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"

Para obter apenas uma hora e um minuto, basta usar o formato

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

as.character(format(b,"%H:%M"))
[1] "20:45"
Eu não acho que precisamos de um duplo, e a origem aceita uma string, tão ligeiramente simplificada:b <-as.POSIXlt(round(as.numeric(a)/(15*60))*(15*60),origin='1970-01-01') Mark Rajcok
2

UsandoIDate eITime aulas dedata.table e umIPeriod classe (acabei de desenvolver) consegui obter uma solução mais escalável.
Somenteshhhhimhuntingrabbits ePLapointe responder a pergunta em termos demais próximo. xts solução só arredonda usandoteto, minhaIPeriod solução permite especificarteto ouchão.
Para obter o melhor desempenho, você precisa manter seus dadosIDate eITime classes. Como visto no benchmark, é barato produzirPOSIXct deIDate/ITime/IPeriod. Abaixo do benchmark de algum timestamp 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 eITime escalas com sucesso não só nesta tarefa específica. Ambos os tipos, o mesmo queIPeriodsão baseadas em números inteiros. Eu diria que eles também vão escalar legal na junção ou agrupamento pordata hora Campos.
Manual online:https://jangorecki.github.io/drat/iperiod/

Abstração é valiosa imo shadowtalker
@ssdecontrol concorda, mas extrair apenas isso para um pacote separado não é tão necessário, pois data.table já é uma dependência leve. Pode ser importado apenas para IDateTime sem problemas. Esteja ciente de que há planejadas extensões para IDateTime classes data.tabledata.table # 1451, seria bom para IPeriod lidar com nanossegundos também. jangorecki
Ah, eu perdi essa linha. Eu tinha assumido que você teria um pacote separado chamado "periodize" ou "IPeriod" ou algo assim, ao contrário de um fork de data.table. Eu acho que é um pouco lamentável que oIDateTime o material é empacotado em data.table e não em um pacote separado shadowtalker
@ssdecontrol Veja o primeiro comando no bloco de códigos para instalar a partir do repositório publicado. Caso contrário, a maneira mais confiável é obter oiunidade ramo, você pode adicioná-lo ao controle remoto e check-out para ramificar. Baseia-se em outubro de 2015 data.table. jangorecki
9

lubridate pacote lida com isso facilmente agora comfloor_date. Para cortar um vetor de objetos POSIXct em intervalos de 15 minutos, use assim.

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

EDIT: anotado pelo usuário @ user3297928, uselubridate::round_date(x, "15 minutes") para arredondar para os 15 minutos mais próximos. Os andares acima.

14

algo como

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

trabalharia

3

Tente isso, que combina as duas solicitações e se baseia em ver o queround.POSIXt() etrunc.POSIXt() Faz.

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
}

Isto dá:

> 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"
isso parece não ser bem vetorizado, tentestructure(c(1313331280, 1313334917, 1313334917, 1313340309, 1313340309, 1313340895, 1313340895, 1313341133, 1313341218, 1313341475), class = c("POSIXct", "POSIXt"), tzone = "UTC") jangorecki
12

De fato, uma velha pergunta com algumas respostas úteis até agora. A última girafa parece ser a mais elegante. no entanto, não é floor_date mas round_date que fará o truque:

lubridate::round_date(x, "15 minutes") 
Nice point out, irá adicionar. giraffehere
4

Você pode usar oalign.time função no pacote xts para lidar com o arredondamento, entãoformat para retornar uma string de "HH: MM":

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 dica de gorjeta para você gauden
Isso é elegante, mas parece apenas arredondar. Dominic
para arredondar para baixo:align.time(p - lubridate::minutes(15), n=60*15) Nicholas Hamilton
@Dominic: você está 100% correto.align.time apenas arredonda para cima e você queria arredondar para o quarto de hora mais próximo. Desculpas Joshua Ulrich

Perguntas relacionadas