R+Tidyverse:Tibbles 似乎不存储毫秒
R+Tidyverse: Tibbles don't appear to store milliseconds
我有一个包含很多值的 CSV。其中有这样存储的时间:
1:34.434
使用readr
,我把它们拼成一个tibble,然后使用dplyr
剪掉所有不需要的行和列。
lapData <- read_csv("sampledata/sampledata.csv")
lapData <- dplyr::select(lapData,
Driver, Lap, Penalty, 'Lap Time', 'Lap type',
'Pressure FR', 'Pressure FL', 'Pressure RR', 'Pressure RL',
'Temp FR', 'Temp FL', 'Temp RR', 'Temp RL',
'Road Temp')
lapData <- dplyr::filter(lapData, Driver == "[personal info]")
lapData <- dplyr::filter(lapData, is.na(Penalty))
lapData <- dplyr::filter(lapData, Lap > 1)
然后我使用print(ggplot(data = lapData, mapping = aes(Lap, 'Lap Time')) + geom_line())
打印数据,它看起来像这样:
如果不清楚,图形线的顶点将四舍五入为秒值,而不是使用我想要的完整毫秒精度。
如果我然后 print(lapData)
,我会在 'Lap Time'
下看到以下条目:
我做了一些研究,发现 this,这似乎表明 tibble 打印输出中的精度损失是无关紧要的,完整的数据仍然存储在 tibble 中。然而,情节与这一说法相矛盾。
如何让绘图显示完整的毫秒分辨率?
您的时间格式与 readr
默认要求的时间格式不一致。具体来说,readr
将 1:34.434
解释为 1 小时 34 分钟,而不是 1 分 34.434 秒。
text <- "id,time
1,1:23.456
2,2:34.567
3,3:45.678
"
cat(text, file = "foo.csv")
library("readr")
tt1 <- read_csv("foo.csv")
tt1
## # A tibble: 3 × 2
## id time
## <dbl> <time>
## 1 1 01:23
## 2 2 02:34
## 3 3 03:45
as.double(tt1$time) / 60 # numbers of minutes
## [1] 83 154 225
文档建议使用 read_csv
的 col_types
参数来指定时间格式。通常,这个 会 有效:
col_types <- list(id = col_integer(), time = col_time(format = "%M:%OS"))
但事实并非如此:
tt2 <- read_csv("foo.csv", col_types = col_types)
## Warning message:
## One or more parsing issues, see `problems()` for details
tt2
## # A tibble: 3 × 2
## id time
## <int> <time>
## 1 1 NA
## 2 2 NA
## 3 3 NA
问题是 readr
的解析器需要用零填充分钟:
parse_time("01:23.456", format = "%M:%OS")
## 00:01:23.456
parse_time("1:23.456", format = "%M:%OS")
## Warning: 1 parsing failure.
## row col expected actual
## 1 -- time like %M:%OS 1:23.456
##
## NA
这让我很惊讶,因为 base R 的解析器没有那个约束:
strptime("1:23.456", format = "%M:%OS")
## [1] "2022-02-11 00:01:23 EST"
一种解决方法是将有问题的列作为字符向量读取,并在
之后通过 strptime
将其强制转换为数字
tt3 <- read_csv("foo.csv", col_types = list(id = col_integer(), time = col_character()))
tt3
## # A tibble: 3 × 2
## id time
## <int> <chr>
## 1 1 1:23.456
## 2 2 2:34.567
## 3 3 3:45.678
library("dplyr")
tt4 <- tt3 %>% mutate(seconds = with(strptime(time, format = "%M:%OS"), 60 * min + sec))
options(pillar.sigfig = 10L)
tt4
## # A tibble: 3 × 3
## id time seconds
## <int> <chr> <dbl>
## 1 1 1:23.456 83.456
## 2 2 2:34.567 154.567
## 3 3 3:45.678 225.678
您也可以使用 lubridate
进行强制转换,它有专门的解析器:
library("lubridate")
tt5 <- tt3 %>% mutate(seconds = as.double(as.duration(ms(time))))
identical(tt4, tt5)
## [1] TRUE
为了避免readr
的猜测,您可以使用常规的read.csv
。
r <- read.csv('foo.csv')
接下来,使用纯毫秒可能会更好。为此,我们可以使用 strptime
和 "%M:%OS"
,其中 returns 漂亮的 "POSIXlt"
格式,我们可以将其放入 with
并使用 min
utes 和 sec
onds.
r$lap_time_ms <- with(strptime(r$lap_time, format="%M:%OS"), 6e4*min + 1e3*sec)
r
# lap_time lap x lap_time_ms
# 1 1:29.134 1 0.9292880 89134
# 2 1:26.233 2 -0.3301566 86233
# 3 1:28.033 3 -1.5426225 88033
# 4 1:26.434 4 -0.9961375 86434
# 5 1:26.634 5 1.1610645 86634
# 6 1:27.634 6 -0.2817558 87634
如果您想要 y-axis 格式化为 <secs>:<mins>
格式,请在 range
圈速中使用 seq
uence,步长以秒为单位。
at <- do.call(seq, c(as.list(range(ceiling(r$lap_time_ms/1e3))*1e3), 1e3))
sq <- at %/% 1e3 |> {\(.) paste(. %/% 60, . %% 60, sep=':')}()
plot(lap_time_ms ~ lap, r, type='l', yaxt='n')
axis(2, at=at, labels=sq)
ggplot2
包也应该可以做到这一点。
数据:
dat <- read.table(header=TRUE, text='lap_time
1:29.134
1:26.233
1:28.033
1:26.434
1:26.634
1:27.634
') |> transform(lap=1:6, x=rnorm(6))
write.csv(dat, 'foo.csv', row.names=FALSE)
我有一个包含很多值的 CSV。其中有这样存储的时间:
1:34.434
使用readr
,我把它们拼成一个tibble,然后使用dplyr
剪掉所有不需要的行和列。
lapData <- read_csv("sampledata/sampledata.csv")
lapData <- dplyr::select(lapData,
Driver, Lap, Penalty, 'Lap Time', 'Lap type',
'Pressure FR', 'Pressure FL', 'Pressure RR', 'Pressure RL',
'Temp FR', 'Temp FL', 'Temp RR', 'Temp RL',
'Road Temp')
lapData <- dplyr::filter(lapData, Driver == "[personal info]")
lapData <- dplyr::filter(lapData, is.na(Penalty))
lapData <- dplyr::filter(lapData, Lap > 1)
然后我使用print(ggplot(data = lapData, mapping = aes(Lap, 'Lap Time')) + geom_line())
打印数据,它看起来像这样:
如果不清楚,图形线的顶点将四舍五入为秒值,而不是使用我想要的完整毫秒精度。
如果我然后 print(lapData)
,我会在 'Lap Time'
下看到以下条目:
我做了一些研究,发现 this,这似乎表明 tibble 打印输出中的精度损失是无关紧要的,完整的数据仍然存储在 tibble 中。然而,情节与这一说法相矛盾。
如何让绘图显示完整的毫秒分辨率?
您的时间格式与 readr
默认要求的时间格式不一致。具体来说,readr
将 1:34.434
解释为 1 小时 34 分钟,而不是 1 分 34.434 秒。
text <- "id,time
1,1:23.456
2,2:34.567
3,3:45.678
"
cat(text, file = "foo.csv")
library("readr")
tt1 <- read_csv("foo.csv")
tt1
## # A tibble: 3 × 2
## id time
## <dbl> <time>
## 1 1 01:23
## 2 2 02:34
## 3 3 03:45
as.double(tt1$time) / 60 # numbers of minutes
## [1] 83 154 225
文档建议使用 read_csv
的 col_types
参数来指定时间格式。通常,这个 会 有效:
col_types <- list(id = col_integer(), time = col_time(format = "%M:%OS"))
但事实并非如此:
tt2 <- read_csv("foo.csv", col_types = col_types)
## Warning message:
## One or more parsing issues, see `problems()` for details
tt2
## # A tibble: 3 × 2
## id time
## <int> <time>
## 1 1 NA
## 2 2 NA
## 3 3 NA
问题是 readr
的解析器需要用零填充分钟:
parse_time("01:23.456", format = "%M:%OS")
## 00:01:23.456
parse_time("1:23.456", format = "%M:%OS")
## Warning: 1 parsing failure.
## row col expected actual
## 1 -- time like %M:%OS 1:23.456
##
## NA
这让我很惊讶,因为 base R 的解析器没有那个约束:
strptime("1:23.456", format = "%M:%OS")
## [1] "2022-02-11 00:01:23 EST"
一种解决方法是将有问题的列作为字符向量读取,并在
之后通过strptime
将其强制转换为数字
tt3 <- read_csv("foo.csv", col_types = list(id = col_integer(), time = col_character()))
tt3
## # A tibble: 3 × 2
## id time
## <int> <chr>
## 1 1 1:23.456
## 2 2 2:34.567
## 3 3 3:45.678
library("dplyr")
tt4 <- tt3 %>% mutate(seconds = with(strptime(time, format = "%M:%OS"), 60 * min + sec))
options(pillar.sigfig = 10L)
tt4
## # A tibble: 3 × 3
## id time seconds
## <int> <chr> <dbl>
## 1 1 1:23.456 83.456
## 2 2 2:34.567 154.567
## 3 3 3:45.678 225.678
您也可以使用 lubridate
进行强制转换,它有专门的解析器:
library("lubridate")
tt5 <- tt3 %>% mutate(seconds = as.double(as.duration(ms(time))))
identical(tt4, tt5)
## [1] TRUE
为了避免readr
的猜测,您可以使用常规的read.csv
。
r <- read.csv('foo.csv')
接下来,使用纯毫秒可能会更好。为此,我们可以使用 strptime
和 "%M:%OS"
,其中 returns 漂亮的 "POSIXlt"
格式,我们可以将其放入 with
并使用 min
utes 和 sec
onds.
r$lap_time_ms <- with(strptime(r$lap_time, format="%M:%OS"), 6e4*min + 1e3*sec)
r
# lap_time lap x lap_time_ms
# 1 1:29.134 1 0.9292880 89134
# 2 1:26.233 2 -0.3301566 86233
# 3 1:28.033 3 -1.5426225 88033
# 4 1:26.434 4 -0.9961375 86434
# 5 1:26.634 5 1.1610645 86634
# 6 1:27.634 6 -0.2817558 87634
如果您想要 y-axis 格式化为 <secs>:<mins>
格式,请在 range
圈速中使用 seq
uence,步长以秒为单位。
at <- do.call(seq, c(as.list(range(ceiling(r$lap_time_ms/1e3))*1e3), 1e3))
sq <- at %/% 1e3 |> {\(.) paste(. %/% 60, . %% 60, sep=':')}()
plot(lap_time_ms ~ lap, r, type='l', yaxt='n')
axis(2, at=at, labels=sq)
ggplot2
包也应该可以做到这一点。
数据:
dat <- read.table(header=TRUE, text='lap_time
1:29.134
1:26.233
1:28.033
1:26.434
1:26.634
1:27.634
') |> transform(lap=1:6, x=rnorm(6))
write.csv(dat, 'foo.csv', row.names=FALSE)