【问题标题】:Calculate character string "days, hours, minutes, seconds" to numeric total days [duplicate]将字符串“天,小时,分钟,秒”计算为数字总天数[重复]
【发布时间】:2016-05-07 09:13:07
【问题描述】:

我看到了很多与格式化时间有关的问题,但在我所拥有的特定导入格式中没有一个问题:

Time <- c(
"22 hours 3 minutes 22 seconds", 
"170 hours 15 minutes 20 seconds", 
"39 seconds", 
"2 days 6 hours 44 minutes 17 seconds", 
"9 hours 54 minutes 36 seconds", 
"357 hours 23 minutes 28 seconds", 
"464 hours 30 minutes 7 seconds", 
"51 seconds", 
"31 hours 39 minutes 2 seconds", 
"355 hours 29 minutes 10 seconds")

有些时间只包含“秒”,而其他时间包含“分钟和秒”、“天、小时、分钟和秒”、“天和秒”等。还有我需要保留的 NA 值。如何获取此字符向量来计算(即添加天、小时、分钟、秒)数字总天数?

例如:

Time
8.10
19.3
0.68
2.28
48.1
0.00
0.70
0.1
3.2
13.9

谢谢!

编辑

老问题,但现在一个简单的lubridate 电话就可以解决问题:

(period_to_seconds(period(time)) / 86400) %>% round(2)

除了需要%&gt;% 以提高可读性之外,这也可以在没有任何包的情况下使用:

Time_vec <- mapply(function(tt, to_days) {
  ifelse(grepl(tt, Time), gsub(paste0("^.*?(\\d+) ", tt, ".*$"), "\\1", Time), 0) %>%
    as.numeric() / to_days
    },
  c("day", "hour", "minute", "second"),
  c(1, 24, 1440, 86400)
) %>%
  apply(1, sum) %>% 
  round(2)

在我的实际数据中,只有一个值与 lubridate 解决方案不同,0.960.97

【问题讨论】:

  • kimisc 包中查找gsub()(在字符串中搜索和替换)和hms.to.seconds() 以获取秒数。
  • 你有没有尝试过

标签: r time lubridate


【解决方案1】:

再一次,没有包和一点正则表达式

Time <- c(
  "22 hours 3 minutes 22 seconds", 
  "170 hours 15 minutes 20 seconds", 
  "39 seconds", 
  "6 hours 44 minutes 17 seconds", 
  "9 hours 54 minutes 36 seconds", 
  "357 hours 23 minutes 28 seconds", 
  "464 hours 30 minutes 7 seconds", 
  "51 seconds", 
  "31 hours 39 minutes 2 seconds", 
  "355 hours 29 minutes 10 seconds")

pat <- '(?:(\\d+) hours )?(?:(\\d+) minutes )?(?:(\\d+) seconds)?'
m <- regexpr(pat, Time, perl = TRUE)

m_st <- attr(m, 'capture.start')
m_ln <- attr(m, 'capture.length')

(mm <- mapply(function(x, y) as.numeric(substr(Time, x, y)),
              data.frame(m_st), data.frame(m_st + m_ln - 1)))

(dd <- setNames(data.frame(mm), c('h','m','s')))
#      h  m  s
# 1   22  3 22
# 2  170 15 20
# 3   NA NA 39
# 4    6 44 17
# 5    9 54 36
# 6  357 23 28
# 7  464 30  7
# 8   NA NA 51
# 9   31 39  2
# 10 355 29 10

round(rowSums(dd / data.frame(h = rep(24, nrow(dd)), m = 24 * 60, s = 24 * 60 * 60),
        na.rm = TRUE), 3)
# [1]  0.919  7.094  0.000  0.281  0.413 14.891 19.354  0.001  1.319 14.812

【讨论】:

  • 一切正常,直到我到达最后一个代码round(rowSums(dd / data.frame(h = rep(24, 10), m = 24 * 60, s = 24 * 60 * 60), na.rm = TRUE), 3)我收到此错误Error in Ops.data.frame(dd, data.frame(h = rep(24, 10), m = 24 * 60, s = 24 * : ‘/’ only defined for equally-sized data frames
  • 抱歉,尝试将rep(24, 10) 替换为rep(24, nrow(dd)) 看看是否是问题所在
【解决方案2】:

lubridate 在这里很有用。 hms 自动提取小时、分钟和秒(为您节省一些正则表达式),time_length 转换为天。

> library(lubridate)
> time_length(hms(Time), 'day')
estimate only: convert periods to intervals for accuracy
 [1]  0.9190046  7.0939815         NA  0.2807523  0.4129167 14.8912963 19.3542477         NA
 [9]  1.3187731 14.8119213

但是,如果没有三个数字,hms 将无法解析,因此进行一些预清理会有所帮助:

> library(stringr)
> Time2 <- sapply(Time, function(x){paste(paste(rep(0, 3 - str_count(x, '[0-9]+')), collapse = ' '), x)})
> time_length(hms(Time2), 'day')
estimate only: convert periods to intervals for accuracy
 [1] 9.190046e-01 7.093981e+00 4.513889e-04 2.807523e-01 4.129167e-01 1.489130e+01 1.935425e+01
 [8] 5.902778e-04 1.318773e+00 1.481192e+01

【讨论】:

  • 抱歉,来自stringr。如果你想坚持使用基础 R,你可以用length(unlist(gregexpr('[0-9]+', x))) 替换它,它做同样的事情,但更长更难理解。我没有很好地解释整个表达方式; sapplyTime 中的每个项目传递给一个函数,该函数使用 stringr::str_count 来计算其中单独数字的数量。你需要 3,所以我从 3 中减去,并在前面的额外 0 上使用 rep(0paste。有两个pastes,因为如果有两个零,你需要collapse,所以你只能得到一个字符串。
  • 太棒了!谢谢你的解释。然而,这就是我运行Time2 &lt;- sapply(Time, function(x){paste(paste(rep(0, 3 - str_count(x, '[0-9]+')), collapse = ' '), x)}) 时发生的事情这个错误:Time2 &lt;- sapply(Time, function(x){paste(paste(rep(0, 3 - str_count(x, '[0-9]+')), collapse = ' '), x)}) Error in rep(0, 3 - str_count(x, "[0-9]+")) : invalid 'times' argument Called from: paste(rep(0, 3 - str_count(x, "[0-9]+")), collapse = " ") Browse[1]&gt; 我错过了什么吗?
  • 哦,我不知道您的数据中有NAs。您不能通过 repNA 参数来确定要重复多少次,因此将 x 包裹在 str_countifelse 以捕获 NAs:ifelse(is.na(x), '', x)NAs 最终会变为 0,但您始终可以将 NAs 放回 Time3[is.na(Time)] &lt;- NA,其中 Time3time_length( ... 的结果。
【解决方案3】:

我建议你安装 stringr 包。然后这样做

library(stringr)
options(digits=7)
returndays <- function(alist){
        val <-length(alist)
        #print(val)
        hr <- vector()
        min <- vector()
        sec <- vector()
        day <- vector()
        for (i in 1:val){
                myinfo <-"([1-9][0-9]{0,2}) hours" 
                hr[i] <-str_match(alist[i],myinfo)[,2]
                myinfo2 <-"([1-9][0-9]{0,2}) minutes" 
                min[i] <-str_match(alist[i],myinfo2)[,2]
                myinfo3 <-"([1-9][0-9]{0,2}) seconds" 
                sec[i] <-str_match(alist[i],myinfo3)[,2]

                h <- as.numeric(hr[i])/24

                m <- as.numeric(min[i])/1440

                s <- as.numeric(sec[i])/86400

               day[i] <- sum(h+m+s,na.rm = TRUE)


        }

        return(day)

}

days <-returndays(Time)

days

[1]  0.9190046  7.0939815  0.0000000  0.2807523  0.4129167 14.8912963 19.3542477  0.0000000  1.3187731
[10] 14.8119213

【讨论】:

    【解决方案4】:

    lubridate提供period()函数,可以方便地将小时、分钟、秒等转换为perdiod对象,可以方便地转换为秒:

    period(days = 3, hours = 10, minutes = 3, seconds = 37)
    ## [1] "3d 10H 3M 37S"
    

    我用这个函数来转换你的字符串:

    to_days <- function(hms_char) {
    
       # split string
       v <- strsplit(hms_char, " ")[[1]]
       # get numbers
       idx <- seq(1, by = 2, length = length(v)/2)
       nums <- as.list(v[idx])
       # get units and use them as names
       names(nums) <- v[-idx]
       # apply functions, sum and convert to days
       duration <- do.call(period, nums)
       days <- period_to_seconds(duration)/86400
    
       return(days)
    }
    

    它适用于单个字符串,因此您需要使用sapply 来转换完整的Time

    sapply(Time, to_days, USE.NAMES = FALSE)
    ## [1] 9.190046e-01 7.093981e+00 4.513889e-04 2.807523e-01 4.129167e-01 1.489130e+01 1.935425e+01
    ## [8] 5.902778e-04 1.318773e+00 1.481192e+01
    

    【讨论】:

      猜你喜欢
      • 2011-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-22
      • 2014-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多