如何解决在R中计算两次之间的营业时间
我正在尝试计算两次(消息创建日期和消息打开日期)之间的小时数。我已使用以下代码创建函数
biz_hrs <- Vectorize(function(CreateDate,OpenedDate,starting_time = '8:00',ending_time = '17:00',holidays = NULL){
if(OpenedDate < CreateDate){
return(NA)
} else {
start_datetime <- as.POSIXct(paste0(substr(start,1,11),starting_time,':00'))
end_datetime <- as.POSIXct(paste0(substr(end,ending_time,':00'))
if(as.Date(CreateDate) == as.Date(OpenedDate) & !as.Date(CreateDate) %in% holidays & !format(as.Date(CreateDate),"%u") %in% c(6,7)){ #if starting time stamp is on same day as ending time stamp and if day is not a holiday or weekend
if(CreateDate > start_datetime & OpenedDate < end_datetime){ #if starting time stamp is later than start business hour and ending time stamp is earlier then ending business hour.
return(as.numeric(difftime(OpenedDate,CreateDate),units = 'hours'))
} else if(CreateDate > start_datetime & OpenedDate > end_datetime & CreateDate < end_datetime){ #if starting time stamp is later than end business hour and ending time stamp is earlier then ending business hour.
return(as.numeric(difftime(as.POSIXct(paste0(substr(start,':00')),units = 'hours'))
} else if(CreateDate < start_datetime & OpenedDate < end_datetime & OpenedDate > start_datetime){ #if starting time stamp is earlier than end business hour and ending time stamp is later than starting business hour.
return(as.numeric(difftime(OpenedDate,start_datetime),units = 'hours'))
} else if(CreateDate > end_datetime & OpenedDate > end_datetime){ #if starting time stamp is later than end business hour and ending time stamp is later than ending business hour.
return(0)
} else if(CreateDate < start_datetime & OpenedDate < start_datetime){ #if starting time stamp is earlier than start business hour and ending time stamp is earlier than starting business hour.
return(0)
} else {
return(as.numeric(difftime(end_datetime,units = 'hours'))
}
} else { #if starting time stamp and ending time stamp occured on a different day.
business_hrs <- as.numeric(difftime(as.POSIXct(paste0('2017-01-01',as.POSIXct(paste0('2017-01-01',':00')) #calculate business hours range by specified parameters
),units = 'hours')
start_day_hrs <- ifelse(CreateDate < as.POSIXct(paste0(substr(start,':00')) & !as.Date(CreateDate) %in% holidays & !format(as.Date(CreateDate),7),#if start time stamp is earlier than specified ending time
as.numeric(difftime(as.POSIXct(paste0(substr(start,units = 'hours'),#calculate time between time stamp and specified ending time
0 #else set zero
) #calculate amount of time on starting day
start_day_hrs <- pmin(start_day_hrs,business_hrs) #cap the maximum amount of hours dertermined by the specified business hours
start_day_hrs
end_day_hrs <- ifelse(OpenedDate > as.POSIXct(paste0(substr(end,':00')) & !as.Date(OpenedDate) %in% holidays & !format(as.Date(OpenedDate),#if end time stamp is later than specified starting time
as.numeric(difftime(end,as.POSIXct(paste0(substr(end,':00'))),#calculate time between time stamp and specified starting time
0) #calculate amount of time on ending day
end_day_hrs <- pmin(end_day_hrs,business_hrs) #cap the maximum amount of hours dertermined by the specified business hours
days_between <- seq(as.Date(CreateDate),as.Date(OpenedDate),by = 1) #create a vector of dates (from and up to including) the starting time stamp and ending time stamp
business_days <- days_between[!days_between %in% c(as.Date(CreateDate),as.Date(OpenedDate)) & !days_between %in% holidays & !format(as.Date(days_between),7)] #remove weekends and holidays from vector of dates
return(as.numeric(((length(business_days) * business_hrs) + start_day_hrs + end_day_hrs))) #multiply the remaining number of days in the vector (business days) by the amount of business hours and add hours from the starting and end day. Return the result
}
}
})
然后输入我的数据
Weekly_Final$ResponseTime <- biz_hrs(Weekly_Final$CreateDate,Weekly_Final$OpenedDate,'8:00','17:00')
但是出现以下错误:
Error in as.character(x) :
cannot coerce type 'closure' to vector of type 'character'
解决方法
重命名变量时,您有时会犯错。调用substr(start,1,11)
时出现错误,因为没有名为start
的变量。 R认为您是指称为start
的功能。我假设CreateDate
曾经被称为start
,而OpenedDate
曾经被称为end
。如果我们只是更新这些,则该函数将返回输出
biz_hrs <- Vectorize(function(CreateDate,OpenedDate,starting_time = '8:00',ending_time = '17:00',holidays = NULL){
if(OpenedDate < CreateDate){
return(NA)
} else {
start_datetime <- as.POSIXct(paste0(substr(CreateDate,11),starting_time,':00'))
end_datetime <- as.POSIXct(paste0(substr(OpenedDate,ending_time,':00'))
if(as.Date(CreateDate) == as.Date(OpenedDate) & !as.Date(CreateDate) %in% holidays & !format(as.Date(CreateDate),"%u") %in% c(6,7)){ #if starting time stamp is on same day as ending time stamp and if day is not a holiday or weekend
if(CreateDate > start_datetime & OpenedDate < end_datetime){ #if starting time stamp is later than start business hour and ending time stamp is earlier then ending business hour.
return(as.numeric(difftime(OpenedDate,CreateDate),units = 'hours'))
} else if(CreateDate > start_datetime & OpenedDate > end_datetime & CreateDate < end_datetime){ #if starting time stamp is later than end business hour and ending time stamp is earlier then ending business hour.
return(as.numeric(difftime(as.POSIXct(paste0(substr(start,':00')),units = 'hours'))
} else if(CreateDate < start_datetime & OpenedDate < end_datetime & OpenedDate > start_datetime){ #if starting time stamp is earlier than end business hour and ending time stamp is later than starting business hour.
return(as.numeric(difftime(OpenedDate,start_datetime),units = 'hours'))
} else if(CreateDate > end_datetime & OpenedDate > end_datetime){ #if starting time stamp is later than end business hour and ending time stamp is later than ending business hour.
return(0)
} else if(CreateDate < start_datetime & OpenedDate < start_datetime){ #if starting time stamp is earlier than start business hour and ending time stamp is earlier than starting business hour.
return(0)
} else {
return(as.numeric(difftime(end_datetime,units = 'hours'))
}
} else { #if starting time stamp and ending time stamp occured on a different day.
business_hrs <- as.numeric(difftime(as.POSIXct(paste0('2017-01-01',as.POSIXct(paste0('2017-01-01',':00')) #calculate business hours range by specified parameters
),units = 'hours')
start_day_hrs <- ifelse(CreateDate < as.POSIXct(paste0(substr(CreateDate,':00')) & !as.Date(CreateDate) %in% holidays & !format(as.Date(CreateDate),7),#if start time stamp is earlier than specified ending time
as.numeric(difftime(as.POSIXct(paste0(substr(CreateDate,units = 'hours'),#calculate time between time stamp and specified ending time
0 #else set zero
) #calculate amount of time on starting day
start_day_hrs <- pmin(start_day_hrs,business_hrs) #cap the maximum amount of hours dertermined by the specified business hours
start_day_hrs
end_day_hrs <- ifelse(OpenedDate > as.POSIXct(paste0(substr(OpenedDate,':00')) & !as.Date(OpenedDate) %in% holidays & !format(as.Date(OpenedDate),#if end time stamp is later than specified starting time
as.numeric(difftime(end,as.POSIXct(paste0(substr(OpenedDate,':00'))),#calculate time between time stamp and specified starting time
0) #calculate amount of time on ending day
end_day_hrs <- pmin(end_day_hrs,business_hrs) #cap the maximum amount of hours dertermined by the specified business hours
days_between <- seq(as.Date(CreateDate),as.Date(OpenedDate),by = 1) #create a vector of dates (from and up to including) the starting time stamp and ending time stamp
business_days <- days_between[!days_between %in% c(as.Date(CreateDate),as.Date(OpenedDate)) & !days_between %in% holidays & !format(as.Date(days_between),7)] #remove weekends and holidays from vector of dates
return(as.numeric(((length(business_days) * business_hrs) + start_day_hrs + end_day_hrs))) #multiply the remaining number of days in the vector (business days) by the amount of business hours and add hours from the starting and end day. Return the result
}
}
})
因此:
biz_hrs("2001-05-07","2001-05-08")
#> 2001-05-07
#> 9
我不知道这是否符合您的期望。
顺便说一句,您应该签出lubridate
软件包,这将使您简化和缩短代码。这将使调试更加容易。
我认为以下简单功能可以很好地代替biz_hrs
并保留界面
library(lubridate)
biz_hrs <- function(CreateDate,start_time = '8:00',end_time = '17:00',holidays = NULL)
{
begin <- as.Date(CreateDate)
end <- as.Date(OpenedDate)
hours <- as.numeric(strsplit(end_time,":")[[1]][1]) -
as.numeric(strsplit(start_time,":")[[1]][1])
sapply(seq_along(begin),function(i) {
if(begin[i] > end[i]) NA
else {
all_days <- seq(begin[i],end[i],"1 day")
sum(hours * (wday(all_days) %in% 2:6 & is.na(match(all_days,holidays))))
}})
}
,
建立在艾伦的建议之上。该功能非常笨拙。我们可以结合使用lubridate
和bizdays
软件包来对函数进行矢量化处理。问题实际上是找到两个日期之间的工作日数,并根据营业时间和营业时间进行调整。首先,我们必须提前几天,以确保它们在营业时间和营业时间之内。
使用lubridate的hms
(时分秒)类有点摆弄,我们可以创建一个移动日期的函数。
library(lubridate)
library(bizdays)
cal <- create.calendar('mycal',weekdays = c('saturday','sunday'))
open <- hms('08:00:00')
close <- hms('17:00:00')
start <- as_datetime('2017-05-10 18:00:00') # After closing time,so real date = '2017-05-11 08:00:00'
end <- as_datetime('2017-05-11 12:00:00') # 4 hours after open
fix_biz_date <- function(date,open,close){
if(!is.POSIXct(date))
date <- lubridate::as_datetime(date)
time <- hms(strftime(date,'%H:%M:%S'))
# Fix dates
ind <- which(time >= close)
if(length(ind) > 0)
date[ind] <- date[ind] + days(1)
# Fix times
ind <- c(ind,which(time < open))
if(length(ind) > 0){
hour(date[ind]) <- hour(open)
minute(date[ind]) <- minute(open)
second(date[ind]) <- second(open)
}
date
}
fix_biz_date(start,close)
[1] "2017-05-11 08:00:00 UTC"
fix_biz_date(end,close)
[1] "2017-05-11 12:00:00 UTC"
现在我们已经适当地更改了日期,我们只需要一个函数来查找两者之间的工作日和工作时间。
#' @param start Starting date-time
#' @param end Ending date-time
#' @param cal calendar object (bizdays::create.calendar)
#' @param open hm(s) second object (lubridate::hms),specifying opening time of day
#' @param open hm(s) second object (lubridate::hms),specifying closing time of day
wh_diff <- function(start,end,cal,close){
if(length(start) != length(end))
stop('start and end needs to be of equal length!')
if(!is.period(open))
open <- hms(open)
if(!is.period(close))
close <- hms(close)
start <- fix_biz_date(start,close)
end <- fix_biz_date(end,close)
sTime <- strftime(start,'%H:%M:%S')
eTime <- strftime(end,'%H:%M:%S')
days_dif <- bizdays(start,cal)
days_dif * (as.numeric(close - open) / 3600) +
as.numeric(hms(eTime) - hms(sTime)) / 3600
}
wh_diff(start,close) # Expected 4 hours.
[1] 4
现在,此功能实际上已被适当地向量化,并且将采用等量的开始日期和结束日期,或者采用多个结束日期的单个开始(反之亦然)。
end <- offset(end,seq(0,180,length.out = 91),cal)
wh_diff(start,close) # something like.. seq(0,18 * 91,length.out = 91)
start <- offset(start,0:90,close) # Expect seq(9,9 * 91,length.out = 91)
此方法将比使用vectorize
快很多时间,后者将在幕后创建几个for-loops
来完成工作。但是,它确实需要一些想象力和一些处理日期的经验。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。