使用正则表达式将一列拆分为两列

Split a Column Into 2 Using Regular Expression

我有一个包含以下格式的时间的数据框:

df<-data.frame(time=c("1655","1055","1123","1505"))
#   time
# 1 1655
# 2 1055
# 3 1123
# 4 925

我想将其更改为标准格式,即带冒号的 16:55:00,但是使用 lubridate 包中的函数 hms 将不起作用。

我正在考虑将 time 列分成 2 列,这样我就可以:

#   time1 time2
# 1 16    55
# 2 10    55
# 3 11    23
# 4  9    25

然后使用 : 作为分隔符将它们组合回去:

#   time
# 1 16:55
# 2 10:55
# 3 11:23
# 4 09:25

但是,我不确定该怎么做(尤其是处理正则表达式)。我试过了:

library(tidyr)
df %>% 
separate(time,c("time1","time2"),sep="[[:digit:]$]{2}") %>%
unite(time,time1,time,sep=":")

当然,这不行。

我们可以使用sprintf将3位数字转换为4位数字,方法是在开头附加0,然后使用sub,我们匹配开头的两个字符并捕获为一组( (.{2})) 并将其替换为后跟 :.

的反向引用 (\1)
df$time <- sub("^(.{2})", "\1:", sprintf("%04d", as.integer(as.character(df$time))))
df$time
#[1] "16:55" "10:55" "11:23" "09:25"

或者另一个选项是 str_pad 来自 stringr

library(stringr)
sub("(.{2})$", ":\1", str_pad(df$time, 4, "left", pad = "0"))
#[1] "16:55" "10:55" "11:23" "09:25"

如果我们更喜欢 tidyverse,那么 separate/unite 也可以,如果我们先 mutatesprintf

library(tidyverse)
df %>% 
     mutate(time = sprintf("%04d", as.integer(as.character(time)))) %>% 
     separate(time, into = c("time1", "time2"), sep=2) %>%
     unite(time, time1, time2, sep=":")
#    time
#1 16:55
#2 10:55
#3 11:23
#4 09:25

str_pad/str_replace 来自 stringr

df %>%
   mutate(time = str_pad(time, 4, "left", pad = "0"),
          time = str_replace(time, "(.{2})", "\1:"))
#   time
#1 16:55
#2 10:55
#3 11:23
#4 09:25

数据

df <- data.frame(time=c("1655","1055","1123","925"))

注意:在不使用 stringsAsFactors 的情况下创建 data.frame 将默认使用 stringsAsFactors=TRUE,因此带有 factor 的列将转换为 integer as.integer(as.character 用作 sprintf

的输入

如果你确实想使用 lubridate 将时间存储为 Period 你可以使用类似下面的东西

df<-data.frame(time=c("1655","1055","1123","1505","955"))
df$time2 <- hm(gsub("(.{2}$)",":\1",df$time))

gsub 在最后两个字符前插入一个“:”

hmlubridate 将其转换为 Period 对象。