将因子变量分钟:秒转换为R中的数值变量分钟.秒
Posted
技术标签:
【中文标题】将因子变量分钟:秒转换为R中的数值变量分钟.秒【英文标题】:Convert factor variable minute:second to numerical variable minute.seconds in R 【发布时间】:2020-05-23 12:02:42 【问题描述】:我正在努力处理我收到的数据框:
game.time.total game.time.first.half game.time.second.half
1 95:09 46:04 49:05
2 95:09 46:04 49:05
3 95:31 46:07 49:23
4 95:31 46:07 49:23
5 95:39 46:08 49:31
目前,这些列当前是因子变量(参见 str 输出)
'data.frame': 5 obs. of 3 variables:
$ game.time.total : Factor w/ 29 levels "100:22","100:53",..: 7 7 10 10 12
$ game.time.first.half : Factor w/ 27 levels "45:18","46:00",..: 3 3 5 5 6
$ game.time.second.half: Factor w/ 29 levels "48:01","48:03",..: 12 12 15 15 17
但是我希望能够使用 colmeans() 对每一列进行平均。据我了解,我需要将列转换为数字并表示为 minutes.seconds ,如下所示:
game.time.total game.time.first.half game.time.second.half
1 95.09 46.04 49.05
2 95.09 46.04 49.05
3 95.31 46.07 49.23
4 95.31 46.07 49.23
5 95.39 46.08 49.31
我知道我可以直接输入它们,但是还有更多类似格式的列和行...有没有一种简单的方法可以做到这一点?还是需要重新调整原文件(.csv)的格式?
编辑:感谢您的回答。我在原始问题中的错误是我没有提供我的实际 DF。我现在已经添加了这个和 str() 结果。
@hello_friend 这是我应用您的第二个解决方案时返回的内容
game.time.total game.time.first.half game.time.second.half
1 7 3 12
2 7 3 12
3 10 5 15
4 10 5 15
5 12 6 17
提前致谢。
【问题讨论】:
嗨,迈克尔。您的列不是字符变量,而是因子变量。这些行为与字符变量不同。 嗨艾伦,谢谢你,我已经在 Q 中重新解决了这个问题!对 R 来说很新,你可能会说.. 【参考方案1】:基础 R 解决方案:
numeric_df <- setNames(data.frame(lapply(data.frame(
Vectorize(gsub)(":", ".", DF), stringsAsFactors = FALSE
),
function(x)
as.double(x)
)), names(DF))
数据:
DF <- structure(list(game.time.total = c("95:09", "95:09", "95:31",
"95:31", "95:39"), game.time.first.half = c("46:04", "46:04",
"46:07", "46:07", "46:08"), game.time.second.half = c("49:05",
"49:05", "49:23", "49:23", "49:31")), class = "data.frame", row.names = c(NA, -5L))
【讨论】:
您好,感谢您的帮助。我的错误是因为我没有提供我正在工作的实际数据并创建了一个单独的 DF 来在我原来的问题中演示它。应用您的第二个解决方案时(解决方案 1 不起作用?)- 我已对原始问题添加了一个编辑以供进一步解释。 @MichaelD 你的问题被截断了。 我已经对我原来的问题进行了修改,抱歉。 @MichaelD 已编辑,请看看这是不是你想要的。【参考方案2】:您可以使用 lubridate 包中的 ms
函数将列转换为分钟和秒。
library(lubridate)
library(dplyr)
DF %>%
mutate_all(ms) %>%
mutate_all(period_to_seconds) %>%
summarise_all(mean) %>%
mutate_all(seconds_to_period)
game.time.total game.time.first.half game.time.second.half
1 1H 35M 23.8000000000002S 46M 6S 49M 17.4000000000001S
如果您想要以秒为单位的平均值,也可以不使用最后一次 mutate_all
调用。
DF %>%
mutate_all(ms) %>%
mutate_all(period_to_seconds) %>%
summarise_all(mean)
game.time.total game.time.first.half game.time.second.half
1 5723.8 2766 2957.4
注意:假设 95.09
表示 95 分 9 秒而不是 95 分 0.09 分
【讨论】:
【参考方案3】:你必须在这里小心。想想"89:30"
和"90:30"
的平均值。它们增加了 180 分钟,所以平均值应该是 90:00
。但是,如果您将它们转换为89.30
和90.30
,那么它们将添加到179.60
,并且平均值变为89.80
,这甚至没有意义。
有一些软件包可以让你更轻松地处理时间,例如lubridate
,如果你经常处理时间,你应该学会使用它们。但是,下面的解决方案不需要任何额外的包并且非常简单。它定义了两个小函数在"mm:ss"
格式和秒数之间进行转换。您可以安全地在几秒钟内进行求和和平均,然后转换回原始格式:
as_seconds <- function(x) sapply(strsplit(x, ":"), function(y) sum(as.numeric(y) * c(60, 1)))
as_min_sec <- function(x) paste0(x %/% 60, sprintf(":%02d", 21))
apply(DF, 2, function(x) as_min_sec(mean(as_seconds(x))))
#> game.time.total game.time.first.half game.time.second.half
#> "95:21" "46:21" "49:21"
如果你只想在每列中用点替换冒号,你可以这样做:
as.data.frame(lapply(DF, function(x) gsub(":", ".", x)))
#> game.time.total game.time.first.half game.time.second.half
#> 1 95.09 46.04 49.05
#> 2 95.09 46.04 49.05
#> 3 95.31 46.07 49.23
#> 4 95.31 46.07 49.23
#> 5 95.39 46.08 49.31
【讨论】:
嗨艾伦,感谢您的回复。这个解决方案似乎有效,谢谢!但是,这只会帮助我分析的一部分。对于我分析的下一部分,我要求数据框位于我建议的布局中,而不需要任何平均!您可能已经看到我对我的原始问题进行了编辑,我认为这应该可以解决问题。 @MichaelD 查看我的更新。这就是你要找的吗? 这并没有将其从因子变量更改为数字 - 请参阅 hello_friend 的解决方案,该解决方案似乎有效。感谢您的宝贵时间和建议! @MichaelD 不应该将其更改为数字。使用单线很容易做到这一点。as.data.frame(lapply(DF, function(x) as.numeric(gsub(":", ".", x))))
但在处理时间时这是一个非常糟糕的主意,原因我在上面概述了。以上是关于将因子变量分钟:秒转换为R中的数值变量分钟.秒的主要内容,如果未能解决你的问题,请参考以下文章