我有一个带有字符向量的数据框,格式为天,小时,分钟和秒,表示为"1d 3h 2m 28s":
> head(status[5])
Duration
1 0d 20h 46m 31s
2 2d 0h 13m 54s
3 2d 0h 13m 53s
4 0d 9h 53m 38s
5 5d 12h 17m 37s
6 0d 10h 21m 19s
Run Code Online (Sandbox Code Playgroud)
我可以使用正则表达式为组件解析它,但无法想出将持续时间转换为秒的好方法.我可以gsub
将向量转换为一个表达式,该表达式将导致秒数,但eval
在结果上使用时会遇到路障.
我可以做类似于这里推荐的事情,但希望遵循正则表达式路线 - 即使它不是最有效的.我只处理解析各种小型HTML表.
status$duration <- gsub("(\\d+)d\\s+(\\d+)h\\s+(\\d+)m\\s+(\\d+)s.*","\\1*86400+\\2*3600+\\3*60+\\4",as.character(status[,5]),perl=TRUE)
Run Code Online (Sandbox Code Playgroud)
上面创建了一个可以进行评估的表达式,但是当涉及到parse(text=status$duration)
后续时我会遗漏一些东西eval
.
在perl中,我习惯于在正则表达式中使用"捕获的变量"并立即使用它们而不是仅在替换字符串中使用它们.R中是否有类似的可能性?
谢谢,由于头脑模糊,我可能会遗漏一些非常简单的东西.
你快到了.问题是该eval
功能没有矢量化.这意味着您需要将结果字符串的每个元素包装到一个apply
语句中,以依次评估每个元素.
首先重新创建数据:
status <- c("0d 20h 46m 31s", "2d 0h 13m 54s", "2d 0h 13m 53s",
"0d 9h 53m 38s", "5d 12h 17m 37s", "0d 10h 21m 19s")
duration <- gsub("(\\d+)d\\s+(\\d+)h\\s+(\\d+)m\\s+(\\d+)s.*","\\1*86400+\\2*3600+\\3*60+\\4",
as.character(status),perl=TRUE)
[1] "0*86400+20*3600+46*60+31" "2*86400+0*3600+13*60+54" "2*86400+0*3600+13*60+53"
[4] "0*86400+9*3600+53*60+38" "5*86400+12*3600+17*60+37" "0*86400+10*3600+21*60+19"
Run Code Online (Sandbox Code Playgroud)
要评估单个元素:
eval(parse(text=duration[1]))
[1] 74791
Run Code Online (Sandbox Code Playgroud)
将其包含在sapply
您喜欢的apply
语句中以评估所有字符串:
sapply(duration, function(x)eval(parse(text=x)))
0*86400+20*3600+46*60+31 2*86400+0*3600+13*60+54
74791 173634
2*86400+0*3600+13*60+53 0*86400+9*3600+53*60+38
173633 35618
5*86400+12*3600+17*60+37 0*86400+10*3600+21*60+19
476257 37279
Run Code Online (Sandbox Code Playgroud)
您可以先使用空格分割字符串strsplit
.
a <- c("0d 20h 46m 31s", "2d 0h 13m 54s", "2d 0h 13m 53s",
"0d 9h 53m 38s", "5d 12h 17m 37s", "0d 10h 21m 19s")
a.values <- sapply(a, strsplit, " ")
Run Code Online (Sandbox Code Playgroud)
现在,a.values
将是:
> a.values
$`0d 20h 46m 31s`
[1] "0d" "20h" "46m" "31s"
$`2d 0h 13m 54s`
[1] "2d" "0h" "13m" "54s"
[cut]
Run Code Online (Sandbox Code Playgroud)
现在,我们编写一个小函数,它采用像这样的4个元素向量并将其转换为秒.基本上我所做的是删除每个元素末尾的字母,将其转换为数字并将其乘以一个适当的值(86400 = 24*60*60天,3600 = 60*60小时等等).
convert.to.sec <- function(timestamp)
{
# Remove the last character (d, h, m, s) from each element
values <- sapply(timestamp, function(x){as.numeric(substr(x, 1, nchar(x)-1))})
mult <- c(86400, 3600, 60, 1)
res <- sum(mult * values)
}
Run Code Online (Sandbox Code Playgroud)
现在我们可以做到
sapply(a.values, convert.to.sec)
Run Code Online (Sandbox Code Playgroud)
etvoilà!
0d 20h 46m 31s 2d 0h 13m 54s 2d 0h 13m 53s 0d 9h 53m 38s 5d 12h 17m 37s
74791 173634 173633 35618 476257
0d 10h 21m 19s
37279
Run Code Online (Sandbox Code Playgroud)
下面的第一个和最后一个解决方案似乎是最简单的,但具有复杂正则表达式的解决方案更接近于perl中可能完成的工作.
在列出解决方案本身之前,请注意,在它们中我们假设输入是tt
,转换向量mult
是4向量,其组件是一天,一小时,一分钟和一秒中的秒数.我们可以mult
在评论中设置或计算如下所示:
tt <- c("0d 20h 46m 31s", "2d 0h 13m 54s", "2d 0h 13m 53s",
"0d 9h 53m 38s", "5d 12h 17m 37s", "0d 10h 21m 19s")
# mult <- c(86400, 3600, 60, 1)
mult <- rev(cumprod(rev(c(24, 60, 60, 1))))
Run Code Online (Sandbox Code Playgroud)
以下是4种方法:
1)strapply提取数字我们可以strapply
在gsubfn包中使用以避免复杂的正则表达式. strapply
用于提取在矩阵中排列它们的所有数字,并通过mult
在纯数字向量中将结果排成一行来相乘:
library(gsubfn)
mat <- strapply(tt, "\\d+", as.numeric, simplify = TRUE)
secs <- c(mult %*% mat)
Run Code Online (Sandbox Code Playgroud)
这两行可以组合成一个单独的语句,但如果您希望mat
单独检查,我们将保留它.
2)复杂的正则表达式的另一种可能性,同样使用的strapply
是以下单个语句.捕获的字符串会在遇到它们时被置于自由变量中,因此第一次捕获进入day
,第二次进入hour
,等等.这可能更接近你在perl中所做的事情:
secs <- strapply(tt, "(\\d+)d (\\d+)h (\\d+)m (\\d+)s",
~ 86400 * as.numeric(day) + 3600 * as.numeric(hour) +
60 * as.numeric(minute) + as.numeric(second), simplify = TRUE)
Run Code Online (Sandbox Code Playgroud)
3)复杂的正则表达式,但矢量化甚至更短:
secs <- strapply(tt, "(\\d+)d (\\d+)h (\\d+)m (\\d+)s",
~ as.numeric(list(...)) %*% mult, simplify = TRUE)
Run Code Online (Sandbox Code Playgroud)
4)strsplit,这是另一个单一的陈述答案.这个不使用,strapply
但利用了这样一个事实,即字符串末尾的匹配分隔符只是被删除而没有以下空字符串输出.详情?strsplit
请见.
secs <- sapply(strsplit(tt, "[dhms]"), function(x) as.numeric(x) %*% mult)
Run Code Online (Sandbox Code Playgroud)
上述任何结果都是:
> secs
[1] 74791 173634 173633 35618 476257 37279
Run Code Online (Sandbox Code Playgroud)