Разделение значений времени из строки, имеющей программирование H, M, S - R - PullRequest
0 голосов
/ 03 мая 2018

Пусть S1 - вектор различных значений времени

s1 = c("PT1H57M3S", "PT1H3M46S","PT1H33S","PT1H2M", "PT18S","PT18M9S", "PT1H39M22S")

Я хочу разделить значения часов, минут и секунд например PT1H57M3S должен идти в столбцы H M S 1 57 3 Я поместил только несколько типов различных строковых значений. в противном случае он является частью столбца данных. Подскажите пожалуйста как это сделать в R Программирование

Ответы [ 4 ]

0 голосов
/ 03 мая 2018

Вместо того, чтобы разбивать их на разные переменные, более надежное решение состоит в том, чтобы анализировать время в некотором классе времени, например hms или chron (или даже просто difftime или POSIXct). В настоящее время hms - это хороший выбор, поскольку он хорошо поддерживается Tibble, если вы используете Tidyverse.

Все это говорит о том, что сложная часть на самом деле не конвертируется, в первую очередь она разбирается с одним из вышеперечисленных. Единственный способ сделать это - lubridate::parse_date_time, который анализирует POSIXct, но будет угадывать среди предоставленных форматов, пока не сработает один, что экономит много кода потока управления.

s1 <- c("PT1H57M3S", "PT1H3M46S","PT1H33S","PT1H2M", "PT18S","PT18M9S", "PT1H39M22S")

hms::as.hms(
    lubridate::parse_date_time(
        s1, 
        # token orders to try, in order
        orders = c('PT%HH%MM%SS', 'PT%HH%SS', 'PT%MM%SS', 'PT%SS'), 
        exact = TRUE,    # take orders as literal strptime-style formats
        truncated = 2),    # allow 0-2 missing tokens on end of orders
    tz = 'UTC')    # parse_date_time returns POSIXct in UTC time zone
#> 01:57:03
#> 01:03:46
#> 01:00:33
#> 01:02:00
#> 00:00:18
#> 00:18:09
#> 01:39:22
0 голосов
/ 03 мая 2018

Вот базовое решение R:

df <- data.frame(H = s1, M = s1, S = s1, stringsAsFactors = FALSE)

df$H <- regmatches(df$H, regexec("\\d{1,2}(?=H)", df$H, perl = TRUE))
df$M <- regmatches(df$M, regexec("\\d{1,2}(?=M)", df$M, perl = TRUE))
df$S <- regmatches(df$S, regexec("\\d{1,2}(?=S)", df$S, perl = TRUE))
df[] <- lapply(df, as.integer) # Convert columns to integer data type

# Output
   H  M  S
1  1 57  3
2  1  3 46
3  1 NA 33
4  1  2 NA
5 NA NA 18
6 NA 18  9
7  1 39 22
0 голосов
/ 03 мая 2018

Вы можете использовать базу r:

a=sub("PT(\\d+H)?(\\d+M)?(\\d+S)?","\\1,\\2,\\3",s1)
read.csv(h=F,text=gsub("[HMS]","",a),col.names = c("H","M","S"))
   H  M  S
1  1 57  3
2  1  3 46
3  1 NA 33
4  1  2 NA
5 NA NA 18
6 NA 18  9
7  1 39 22
0 голосов
/ 03 мая 2018

Мы могли бы split на границе между буквой и цифрой, затем преобразовать ее в data.frame и использовать rbindlist из data.table

library(data.table)
rbindlist(
 lapply(strsplit(s1, "(?<=[A-Z])(?=[0-9])|(?<=[0-9])(?=[A-Z])", perl = TRUE),
 function(x) {
   x1 <- x[-1];val <- x1[seq(1, length(x1), by = 2)]
   nm <- x1[seq(2, length(x1), by = 2)]
   setNames(as.data.frame.list(val), nm)}),
 fill = TRUE)
#    H  M  S
#1:  1 57  3
#2:  1  3 46
#3:  1 NA 33
#4:  1  2 NA
#5: NA NA 18
#6: NA 18  9
#7:  1 39 22

Мы могли бы также сделать это с tidyverse

library(tidyverse)
library(stringi)
out <- map2_df(stri_extract_all_regex(s1, "\\d+"), 
        stri_extract_all_regex(s1, "[HMS]"), ~ .x %>%
               as.integer %>%
               as.list %>% 
               set_names(.y) ) 
out
#A tibble: 7 x 3
#      H     M     S
#  <int> <int> <int>
#1     1    57     3
#2     1     3    46
#3     1    NA    33
#4     1     2    NA
#5    NA    NA    18
#6    NA    18     9
#7     1    39    22

Если нам нужно заменить NA на 0

out[is.na(out)] <- 0

Или, если нам нужно сделать это путем преобразования во временной класс,

library(lubridate)
v1 <- parse_date_time(sub("^PT", "", s1), 
   order = rlang::syms(tolower(unique(gsub("[^HMS]+", "", s1)))))
tibble(Hour = hour(v1), Minute = minute(v1), Seconds = seconds(v1))
# A tibble: 7 x 3
#   Hour Minute Seconds
#  <int>  <int>   <dbl>
#1     1     57       3
#2     1      3      46
#3     1      0      33
#4     1      0       2
#5     0      0      18
#6    18      0       9
#7     1     39      22

Здесь мы подобрали форматы программно из строки ввода


Или мы можем сделать только с base R

v1 <- do.call(pmax, c(lapply(paste0("PT", gsub("(.)", "%\\1\\1", 
 unique(gsub("[^HMS]+", "", s1)))), strptime, x = s1), list(na.rm= TRUE)))
data.frame(hour = v1$hour, minute = v1$min, sec = v1$sec)
#  hour minute sec
#1    1     57   3
#2    1      3  46
#3    1      0  33
#4    1      2   0
#5    0      0  18
#6    0     18   9
#7    1     39  22
...