【发布时间】:2021-12-01 18:57:04
【问题描述】:
我正在寻找一种从制表符分隔的文本中读取单列的快速方法,该文本作为字符向量存在于内存中。
我正在使用特定于我的领域的文件格式,它大致类似于压缩的 tsv 文件。从此类文件中读取行的子集既快速又容易,但由于内存限制(而且我需要的行不知道先验)。
所以,我最终在内存中得到了一个字符向量,每一行都有一个元素,但制表符分隔符仍在其中。我对如何从该文本中快速提取特定列感到有些困惑。在下面的示例中,提取第三列的最快方法是什么?文本中没有任何“惊喜”,例如 cmets 或引用名称,但在我的实际情况中,列没有固定宽度。目前我发现最快的方法是使用readr::read_tsv() 函数。
library(readr)
set.seed(0)
# About 88Mb of memory
n_examples <- 1e6
text <- paste(
as.character(as.hexmode(sample(n_examples))),
as.character(as.hexmode(sample(n_examples))),
as.character(as.hexmode(sample(n_examples))),
as.character(as.hexmode(sample(n_examples))),
sep = "\t"
)
fun_read.table <- function(x, i) {
read.table(
text = x, sep = "\t",
colClasses = c("character", "character", "character", "character")
)[[i]]
}
fun_read_tsv <- function(x, i) {
read_tsv(file = I(x), col_select = all_of(i),
col_types = "cccc", col_names = LETTERS[1:4])[[1]]
}
bm <- bench::mark(
fun_read.table(text, 3),
fun_read_tsv(text, 3),
min_iterations = 5
)
#> Warning: Some expressions had a GC in every iteration; so filtering is disabled.
print(bm)
#> # A tibble: 2 x 13
#> expression min median `itr/sec` mem_alloc `gc/sec` n_itr
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int>
#> 1 fun_read.table(text, 3) 1.34s 1.57s 0.619 93.2MB 1.11 5
#> 2 fun_read_tsv(text, 3) 879.36ms 903.72ms 1.10 35.1MB 0.219 5
#> # ... with 6 more variables: n_gc <dbl>, total_time <bch:tm>, result <list>,
#> # memory <list>, time <list>, gc <list>
以下是我尝试过的一些替代方案,但速度并不比read_tsv() 快。 data.table::fread() 方法非常慢,因为它首先将输入文本写入临时文件。我还没有想出一个基于正则表达式的方法来捕获第三列,所以我不知道这是否会更快。
library(data.table)
#> Warning: package 'data.table' was built under R version 4.1.1
fun_tstrsplit <- function(x, i) {
tstrsplit(x, "\t", keep = i)[[1]]
}
fun_fread <- function(x, i) {
fread(
text = x, sep = "\t",
colClasses = c("character", "character", "character", "character"),
select = i
)[[1]]
}
fun_scan <- function(x, i) {
ncols <- lengths(regmatches(x[[1]], gregexpr("\t", x[[1]]))) + 1
scan(
text = x, sep = "\t", what = "", quiet = TRUE
)[seq_along(x) %% ncols == i]
}
由reprex package (v2.0.1) 于 2021 年 10 月 13 日创建
【问题讨论】:
-
您能详细说明您尝试了哪个
data.table版本吗?我试图重现,但即使使用setDTthreads(1L)fread对我来说也比read_tsv快。 -
我尝试了 v1.14.2,在我的机器上,无论使用 6 个线程还是 1 个线程,它的中位数都需要约 6.9 秒。在我的 linux 机器上需要 5.1 秒。您是否碰巧有一个 SSD,写入文件的速度比我在 HDD 上的速度快?
-
没错,我有一个 SSD。您可以尝试在启动 R 之前设置
TMPDIR=/dev/shm以将文件转储到内存磁盘而不是硬盘上。