format a time difference automatically - r

Inside a function a need to convert some number, in general in range of 20 to 200, in to difftime and show via format as expected time needed to finish.
as.difftime has got a useful units="auto" so it will use "sec" say for 20 secs and "mins" for 60+ secs...
But it says also
> as.difftime(100, units="auto")
Error in as.difftime(100, units = "auto") :
need explicit units for numeric conversion
How can I avoid that?
EDIT: Current workaround
> (Sys.time()+100)-Sys.time()
Time difference of 1.666667 mins

Is lubridate an alternative?
library(lubridate)
new_difftime(second = 20)
# Time difference of 20 secs
new_difftime(second = 60)
# Time difference of 1 mins
new_difftime(second = 240)
# Time difference of 4 mins
new_difftime(second = 1000000)
# Time difference of 11.57407 days
# new_difftime creates an object of same class as as.difftime does.
class(as.difftime(20, units = "secs"))
# [1] "difftime"
class(new_difftime(second = 20))
# [1] "difftime"
It is also possible to specify input values of several units. E.g. from ?new_difftime
new_difftime(second = 3, minute = 1.5, hour = 2, day = 6, week = 1)
# Time difference of 13.08441 days

Related

Create n different dates in consecutive months from a starting year-month

I have a starting time specified as a year-month character, e.g. "2020-12". From the start, for each of T consecutive months, I need to generate n different dates (year-month-day), where the day is random.
Any help will be useful!
The data I'm working on:
data <- data.frame(
data = sample(seq(as.Date('2000/01/01'), as.Date('2020/01/01'), by="day"), 500),
price = round(runif(500, min = 10, max = 20),2),
quantity = round(rnorm(500,30),0)
)
func <- function(start, months, n) {
startdate <- as.Date(paste0(start, "-01"))
enddate <- seq(startdate, by = "month", length.out = months)
months <- seq_len(months)
enddate_lt <- as.POSIXlt(enddate)
enddate_lt$mon <- enddate_lt$mon + 1
enddate_lt$mday <- enddate_lt$mday - 1
days_per_month <- as.integer(format(enddate_lt, format = "%d"))
days <- lapply(days_per_month, sample, size = n)
dates <- Map(`+`, enddate, days)
do.call(c, dates)
}
set.seed(2021)
func("2020-12", 4, 3)
# [1] "2020-12-08" "2020-12-07" "2020-12-15" "2021-01-27" "2021-01-08" "2021-01-13" "2021-02-21" "2021-02-07" "2021-02-28"
# [10] "2021-03-28" "2021-03-07" "2021-03-15"
func("2020-12", 5, 2)
# [1] "2020-12-06" "2020-12-16" "2021-01-08" "2021-01-10" "2021-02-24" "2021-02-13" "2021-03-20" "2021-03-29" "2021-04-19"
# [10] "2021-04-28"
func("2020-12", 2, 10)
# [1] "2020-12-29" "2020-12-30" "2020-12-04" "2020-12-15" "2020-12-09" "2020-12-27" "2020-12-05" "2020-12-06" "2020-12-23"
# [10] "2020-12-17" "2021-01-03" "2021-01-20" "2021-01-05" "2021-01-22" "2021-01-23" "2021-01-06" "2021-01-10" "2021-01-07"
# [19] "2021-01-19" "2021-01-12"
Most of the dancing with POSIXlt objects is because it gives us clean (base R) access to the number of days in a month, which makes sampleing the days in a month rather simple. It can also be done (code-golf shorter) using the lubridate package, but I don't know that that is any more correct than this code is.
This just dumps out a sequence of random dates, with n days per month. It does not sort within each month, though it does output the months in order. (That's not a difficult extension, there just wasn't a requirement for it.) It doesn't put out a frame, you can easily extend this to fit in a frame or call data.frame(date = do.call(c, dates)) on the last line, depending on what you need to do with the output.
You could convert the start time to a class for monthly data, zoo::yearmon. Then use as.Date.yearmon and its frac argument ("a number between 0 and 1 inclusive that indicates the fraction of the way through the period that the result represents") with random values from runif (uniform between 0 and 1) to convert to a random date within each year-month.
start = "2020-12"
T = 3
n = 2
library(zoo)
set.seed(1)
as.Date(as.yearmon(start) + rep((1:T)/12, each = n), frac = runif(T * n))
# [1] "2021-01-08" "2021-01-12" "2021-02-16" "2021-02-25" "2021-03-07" "2021-03-27"

how to calculate time interval and divide it by integer in r?

i have time string as "08:00","06:00"
and i wanna calculate difference between them
and divide it by 15 mins.
then results should be 8 in integer
i don't how to code in R
anybody can help me ?
Something like this using difftime?
difftime(
as.POSIXct("08:00", format = "%H:%M"),
as.POSIXct("06:00", format = "%H:%M"),
units = "mins") / 15
#Time difference of 8 mins
Or to convert to numeric
as.numeric(
difftime(as.POSIXct("08:00", format = "%H:%M"),
as.POSIXct("06:00", format = "%H:%M"),
units = "mins") / 15)
#[1] 8
It would be easy with lubridate, where we convert the strings in hm format and divide by 15 minutes.
library(lubridate)
(hm(a) - hm(b))/minutes(15)
#[1] 8
data
a <- "08:00"
b <- "06:00"

Converting a number into time (0,5 of an hour = 00:30:00)

I am trying to convert a number into time format.
For example:
I calculate how long has to be charged an electric car at the charging station of 11 kWh.
Energy demand - 2,8 kWh
Charging time = 2,8 kWh/11 kWh = 0,257 h
0,257 h = 15 min 25 sec. = 00:15:25
How can I convert 0,257 h into 00:15:25 in R?
Based on the example, we will assume that the input is less than 24 (but if that is not the case these could be modified to handle that depending on the definition of what such an input should produce).
1) chron::times Use chron times like this. times measures times in fractions of a day so divide the hours (.257) by 24 to give the fraction of a day that it represents.
library(chron)
times(.257 / 24)
## [1] 00:15:25
This gives a chron "times" class object. If x is such an object use format(x) to convert it to a character string, if desired.
2) POSIXct This uses no packages although it is longer. It returns the time as a character string. POSIXct measures time in seconds and so multiply the hours (.257) by 3600 as there are 3600 seconds in an hour.
format(as.POSIXct("1970-01-01") + 3600 * .257, "%H:%M:%S")
## [1] "00:15:25"
2a) This variation would also work. It is longer but it involves no conversion factors. It returns a character string.
format(as.POSIXct("1970-01-01") + as.difftime(.257, units = "hours"), "%H:%M:%S")
## [1] "00:15:25"
Updates: Added (2). Also added (2a) and improved (2).
The answer by #GGrothendieck seems to be the way to go here. But if you had to do this in base R, you could just compute the hour, minute, and second components and build the time string manually:
x <- 2.257 # number of hours
total <- round(x*60*60, digits=0) # the total number of seconds
hours <- trunc(total / (60*60))
minutes <- trunc((x - hours) * 60)
seconds <- total %% 60
ts <- paste0(formatC(hours, width=2, flag="0"), ":",
formatC(minutes, width=2, flag="0"), ":",
formatC(seconds, width=2, flag="0"))
ts
[1] "02:15:25"
Demo
The tidyverse solution would use the hms package:
hms::hms(0.257 * 60^2)
#> 00:15:25.2
Gives you an object of classes hms and difftime. If you want a string:
format(hms::hms(0.257 * 60^2))
#> [1] "00:15:25.2"

Time difference with R. When day, month and year absent

How to get date difference with R (in term of minutes) when day, month and year were not provided.
For instance minutes betweeen "23:14:01" and "00:02:01".
You can use difftime:
a <- strptime("23:14:01",format = "%H:%M:%S")
b <- strptime("00:02:01",format = "%H:%M:%S")
difftime(a,b, units = "mins")
# Time difference of 1392 mins
difftime_res_2 <- 1440 - difftime_res # In case the times are from following days
difftime_res_2
# Time difference of 48 mins

Round from milliseconds to minutes

I have time data in hours:minutes:seconds:milliseconds. I also have a descriptor for what occurs at each timepoint. An example of my dataset is as follows:
StartTime <- c("00:00:00:00", "00:00:14:04", "00:01:51:06", "00:03:30:02")
Events <- c("Start whistle", "Pass", "Shot", "Pass")
RawData <- data.frame(StartTime, Events)
I now wish to create a new column that rounds according to minutes played, from the StartTime column. My anticipated output would be:
MinutesPlayed <- c(0, 0, 2, 3)
I have tried to round using the following code, however it includes the date (unnecessary for my work) and still keeps time in H:M:S format.
RawData$MinutesPlayed <- strptime(RawData$StartTime, "%H:%M:%S")
Where am I please going wrong?
If you're rounding up, then the last element should go up to 4 (since 30.02 seconds rounds to 1 minute). Here's an idea using strptime(), rounding the minutes.
## replace the last colon with a decimal point
st <- sub("(.*):(.*)", "\\1.\\2", StartTime)
## convert to POSIXlt and grab the rounded minutes
round(strptime(st, "%H:%M:%OS"), "mins")$min
# [1] 0 0 2 4
sapply(strsplit(as.character(RawData$StartTime),":"), function(x)
#Use 'ceiling' or 'round' instead of 'floor' as needed
floor(as.numeric(x[1])*60 + #hours to minutes
as.numeric(x[2]) + #minutes
as.numeric(x[3])/60 + #seconds to minutes
as.numeric(x[4])/60000)) #milliseconds to minutes
#[1] 0 0 1 3

Resources