Pregunta sobre datetime, r – Tiempo de redondeo al cuarto de hora más cercano

24

Tengo un vector de valores POSIXct y me gustaría redondearlos al cuarto de hora más cercano. No me importa el día. ¿Cómo convierto los valores a horas y minutos?

Por ejemplo, me gustaría el valor.

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

ser

"20:45"

Tu respuesta

7   la respuesta
12

una vieja pregunta con algunas respuestas útiles hasta ahora. El último por jirafa parece ser el más elegante. sin embargo, no es floor_date sino round_date lo que hará el truco:

lubridate::round_date(x, "15 minutes") 
Buen punto, agregará. giraffehere
9

lubridate paquete maneja esto fácilmente ahora confloor_date. Para cortar un vector de objetos POSIXct en intervalos de 15 minutos, úselo así.

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

EDITAR: anotado por el usuario @ user3297928, uselubridate::round_date(x, "15 minutes") Para redondear a los 15 minutos más cercanos. Los pisos anteriores lo.

2

IDate yITime clases dedata.table y unIPeriod clase (recién desarrollada) pude obtener una solución más escalable.
Solamenteshhhhimhuntingrabbits yPLapointe responder a la pregunta en términos demás cercano. xts La solución solo redondea utilizandotechomiIPeriod solución permite especificartecho opiso.
Para obtener el mejor rendimiento necesitaría mantener sus datos enIDate yITime clases Como se ve en el punto de referencia es barato producirPOSIXct desdeIDate/ITime/IPeriod. Por debajo del punto de referencia de unos 22M de marca de tiempo:

# 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 yITime Escala con éxito no solo en esta tarea en particular. Ambos tipos, igual queIPeriod, son basados ​​en enteros. Supongo que también escalarán bien al unirse o agruparse porfecha y hora campos.
Manual en línea:https://jangorecki.github.io/drat/iperiod/

@ssdecontrol está de acuerdo, pero extraer eso en un paquete separado no es tan necesario ya que data.table ya es una dependencia ligera. Se puede importar solo para IDateTime sin problemas. Tenga en cuenta que se planea una extensión para las clases de datos IDateTime.tabla de datos # 1451, sería bueno para IPeriod manejar nanosegundos también. jangorecki
Ah, me perdí esa línea. Yo había asumido que usted tendría un paquete separado llamado "periodize" o "IPeriod" o algo así, a diferencia de una tabla de datos. Creo que es algo desafortunado que elIDateTime Las cosas se incluyen en data.table y no en un paquete separado. shadowtalker
@ssdecontrol pero la clase IPeriod es solo un número, cualquier paquete puede manejarlo solo con%/% Operador, teniendo los períodos codificados, no es necesario almacenar ningún atributo, solo un número, no hay magia negra. jangorecki
@ssdecontrol Vea el primer comando en el bloque de código para instalar desde el repositorio publicado. De lo contrario, la forma más confiable es obtener eliunit rama, puede agregarlo a remoto y pago y envío a rama. Se basa en octubre de 2015 data.table. jangorecki
4

Puedes usar elalign.time Funciona en el paquete xts para manejar el redondeo, luegoformat para devolver una cadena 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 punta de sombrero para ti gauden
Esto es elegante, pero parece que sólo se redondea. Dominic
@Dominic: eres 100% correcto.align.time solo se redondea y desea redondear al cuarto de hora más cercano. Disculpas Joshua Ulrich
para redondear hacia abajo:align.time(p - lubridate::minutes(15), n=60*15) Nicholas Hamilton
14

algo como

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

trabajaría

20

Puedes usarround. El truco es dividir por 900 segundos (15 minutos * 60 segundos) antes de redondear y multiplicar por 900 después:

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 obtener solo hora y minuto, solo usa el formato

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

as.character(format(b,"%H:%M"))
[1] "20:45"
No creo que necesitemos un doble, y el origen acepta una cadena, tan ligeramente simplificada:b <-as.POSIXlt(round(as.numeric(a)/(15*60))*(15*60),origin='1970-01-01') Mark Rajcok
3

que combina ambas solicitudes y se basa en analizar quéround.POSIXt() ytrunc.POSIXt() hacer.

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
}

Esto da:

> 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"
Esto parece no estar bien vectorizado, intentestructure(c(1313331280, 1313334917, 1313334917, 1313340309, 1313340309, 1313340895, 1313340895, 1313341133, 1313341218, 1313341475), class = c("POSIXct", "POSIXt"), tzone = "UTC") jangorecki

Preguntas relacionadas