有效地将二进制数据读入R
Read binary data into R efficiently
我正在从一个文本文件中读取结构如下的二进制数据:
0101010100101010101010101010
1010101001010101010101010111
1111101010101010100101010101
文件有800行。每一行都一样长(但文件之间的长度不同,因此硬编码没有意义)。我想将输入存储在一个数据框中,其中每一行是一行,每两个数字存储在不同的列中,例如:
col1 col2 col3 col4
0 1 0 1
目前我是这样做的
as.matrix(read.table(text=gsub("", ' ', readLines("input"))))->g
但是,这需要很长时间,因为每行大约有 70,000 个 0/1。
有更快的方法吗?
我建议从 "readr" 包中探索 read_fwf
。你可以这样做:
library(readr)
len <- nchar(readLines("yourfile.txt", n = 1))
read_fwf("yourfile.txt", fwf_widths(rep(1, len)))
或者,您可以尝试 the "iotools" package,这可能会更快:
library(iotools)
len <- nchar(readLines("yourfile.txt", n = 1))
input.file("yourfile.txt", formatter = dstrfw,
col_types = rep("integer", len), widths = rep(1, len))
这是一个小的 POC:
a <- tempfile()
writeLines("0101010100101010101010101010
1010101001010101010101010111
1111101010101010100101010101", a)
len <- nchar(readLines(a, n = 1))
library(readr)
read_fwf(a, fwf_widths(rep(1, len)))
# Source: local data frame [3 x 28]
#
# X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X27 X28
# 1 0 1 0 1 0 1 0 1 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
# 2 1 0 1 0 1 0 1 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1
# 3 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 1 0 1 0 1 0 1 0 1
您的数据维度似乎确实 read_fwf
令人窒息。我做了一个小测试来比较 "iotools" 方法与 awk
+ fread
.
示例数据如下:
## Creates a file named "somefile.txt"
set.seed(1)
A <- replicate(10, sample(0:1, 70000, TRUE), FALSE)
A <- sapply(A, paste, collapse = "")
writeLines(rep(A, 800/length(A)), "somefile.txt")
这是函数和结果。我已经编写了这些函数,以便您应该能够在您的实际数据上尝试它们,看看哪个最适合您。
显然,readr
似乎在现阶段不在画面中:-)
Freadr <- function(infile = "somefile.txt") {
len <- nchar(readLines(infile, n = 1))
read_fwf(infile, fwf_widths(rep(1, len)))
}
system.time(temp1 <- Freadr())
# |===============================================================| 100% 53 MB
# user system elapsed
# 466.740 0.384 466.506
Fiotools <- function(infile = "somefile.txt") {
len <- nchar(readLines(infile, n = 1))
input.file(infile, formatter = dstrfw,
col_types = rep("integer", len), widths = rep(1, len))
}
system.time(temp2 <- Fiotools())
# user system elapsed
# 7.248 0.016 7.273
Fawk <- function(infile = "somefile.txt") {
cmd <- sprintf("awk '{gsub(/./,\"&,\", );print }' %s", infile)
fread(cmd)
}
system.time(temp3 <- Fawk())
# user system elapsed
# 12.948 0.156 13.109
就此而言,使用基数 R 也不错:
fun4 <- function(infile = "somefile.txt") {
do.call(rbind, lapply(strsplit(readLines(infile), "", TRUE), as.numeric))
}
system.time(fun4())
# user system elapsed
# 9.056 0.260 9.304
结果是 matrix
,因此您可能需要添加几秒钟才能转换为 data.frame
或 data.table
,如果这确实是您想要的。
你可以 pipe
和 awk
read.table(pipe("awk '{gsub(/./,\"& \", );print }' yourfile.txt"))
# V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20 V21
#1 0 1 0 1 0 1 0 1 0 0 1 0 1 0 1 0 1 0 1 0 1
#2 1 0 1 0 1 0 1 0 0 1 0 1 0 1 0 1 0 1 0 1 0
#3 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 1 0
# V22 V23 V24 V25 V26 V27 V28
#1 0 1 0 1 0 1 0
#2 1 0 1 0 1 1 1
#3 1 0 1 0 1 0 1
或
read.table(pipe("awk '{gsub(\"\",\" \", );print }' yourfile.txt"))
fread
也可以和awk
组合
library(data.table)
fread("awk '{gsub(/./,\"&,\", );print }' yourfile.txt")
使用与 OP 数据集相似的数据集,
library(stringi)
write.table(stri_rand_strings(800,70000, '[0-1]'), file='binary1.txt',
row.names=FALSE, quote=FALSE, col.names=FALSE)
system.time(fread("awk '{gsub(/./,\"&,\", );print }' binary1.txt"))
# user system elapsed
#16.444 0.108 16.542
从后续问题、数据结构和原始解决方案来看,您似乎想要一个矩阵(因为所有列都是同一类型)而不是 data.frame在问题的正文中(并导致下游出现问题!)。数据好像不大,读进去拆分成一个个字母
lns = strsplit(readLines("somefile.txt"), "")
然后取消列表,将字符串匹配为整数,并整形为矩阵
v = match(unlist(lns), c("0", "1")) - 1L
m = matrix(v, nrow=length(lns), byrow=TRUE)
或作为函数
input2matrix <- function(fname) {
lns = strsplit(readLines("somefile.txt"), "")
v = match(unlist(lns), c("0", "1")) - 1L
matrix(v, nrow=length(lns), byrow=TRUE)
}
对于 800 x 70000 行示例,这大约需要 5 秒。与其他响应相比,它也比所有其他解决方案都快(我无法轻松安装 iotools,抱怨 C 级缺少符号 Rspace)并且不对 OS 和 [= 的可用性做出假设22=] 工具(以及除 R 之外的这些工具的知识!)。
我正在从一个文本文件中读取结构如下的二进制数据:
0101010100101010101010101010
1010101001010101010101010111
1111101010101010100101010101
文件有800行。每一行都一样长(但文件之间的长度不同,因此硬编码没有意义)。我想将输入存储在一个数据框中,其中每一行是一行,每两个数字存储在不同的列中,例如:
col1 col2 col3 col4
0 1 0 1
目前我是这样做的
as.matrix(read.table(text=gsub("", ' ', readLines("input"))))->g
但是,这需要很长时间,因为每行大约有 70,000 个 0/1。
有更快的方法吗?
我建议从 "readr" 包中探索 read_fwf
。你可以这样做:
library(readr)
len <- nchar(readLines("yourfile.txt", n = 1))
read_fwf("yourfile.txt", fwf_widths(rep(1, len)))
或者,您可以尝试 the "iotools" package,这可能会更快:
library(iotools)
len <- nchar(readLines("yourfile.txt", n = 1))
input.file("yourfile.txt", formatter = dstrfw,
col_types = rep("integer", len), widths = rep(1, len))
这是一个小的 POC:
a <- tempfile()
writeLines("0101010100101010101010101010
1010101001010101010101010111
1111101010101010100101010101", a)
len <- nchar(readLines(a, n = 1))
library(readr)
read_fwf(a, fwf_widths(rep(1, len)))
# Source: local data frame [3 x 28]
#
# X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X27 X28
# 1 0 1 0 1 0 1 0 1 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
# 2 1 0 1 0 1 0 1 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1
# 3 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 1 0 1 0 1 0 1 0 1
您的数据维度似乎确实 read_fwf
令人窒息。我做了一个小测试来比较 "iotools" 方法与 awk
+ fread
.
示例数据如下:
## Creates a file named "somefile.txt"
set.seed(1)
A <- replicate(10, sample(0:1, 70000, TRUE), FALSE)
A <- sapply(A, paste, collapse = "")
writeLines(rep(A, 800/length(A)), "somefile.txt")
这是函数和结果。我已经编写了这些函数,以便您应该能够在您的实际数据上尝试它们,看看哪个最适合您。
显然,readr
似乎在现阶段不在画面中:-)
Freadr <- function(infile = "somefile.txt") {
len <- nchar(readLines(infile, n = 1))
read_fwf(infile, fwf_widths(rep(1, len)))
}
system.time(temp1 <- Freadr())
# |===============================================================| 100% 53 MB
# user system elapsed
# 466.740 0.384 466.506
Fiotools <- function(infile = "somefile.txt") {
len <- nchar(readLines(infile, n = 1))
input.file(infile, formatter = dstrfw,
col_types = rep("integer", len), widths = rep(1, len))
}
system.time(temp2 <- Fiotools())
# user system elapsed
# 7.248 0.016 7.273
Fawk <- function(infile = "somefile.txt") {
cmd <- sprintf("awk '{gsub(/./,\"&,\", );print }' %s", infile)
fread(cmd)
}
system.time(temp3 <- Fawk())
# user system elapsed
# 12.948 0.156 13.109
就此而言,使用基数 R 也不错:
fun4 <- function(infile = "somefile.txt") {
do.call(rbind, lapply(strsplit(readLines(infile), "", TRUE), as.numeric))
}
system.time(fun4())
# user system elapsed
# 9.056 0.260 9.304
结果是 matrix
,因此您可能需要添加几秒钟才能转换为 data.frame
或 data.table
,如果这确实是您想要的。
你可以 pipe
和 awk
read.table(pipe("awk '{gsub(/./,\"& \", );print }' yourfile.txt"))
# V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20 V21
#1 0 1 0 1 0 1 0 1 0 0 1 0 1 0 1 0 1 0 1 0 1
#2 1 0 1 0 1 0 1 0 0 1 0 1 0 1 0 1 0 1 0 1 0
#3 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 1 0
# V22 V23 V24 V25 V26 V27 V28
#1 0 1 0 1 0 1 0
#2 1 0 1 0 1 1 1
#3 1 0 1 0 1 0 1
或
read.table(pipe("awk '{gsub(\"\",\" \", );print }' yourfile.txt"))
fread
也可以和awk
library(data.table)
fread("awk '{gsub(/./,\"&,\", );print }' yourfile.txt")
使用与 OP 数据集相似的数据集,
library(stringi)
write.table(stri_rand_strings(800,70000, '[0-1]'), file='binary1.txt',
row.names=FALSE, quote=FALSE, col.names=FALSE)
system.time(fread("awk '{gsub(/./,\"&,\", );print }' binary1.txt"))
# user system elapsed
#16.444 0.108 16.542
从后续问题、数据结构和原始解决方案来看,您似乎想要一个矩阵(因为所有列都是同一类型)而不是 data.frame在问题的正文中(并导致下游出现问题!)。数据好像不大,读进去拆分成一个个字母
lns = strsplit(readLines("somefile.txt"), "")
然后取消列表,将字符串匹配为整数,并整形为矩阵
v = match(unlist(lns), c("0", "1")) - 1L
m = matrix(v, nrow=length(lns), byrow=TRUE)
或作为函数
input2matrix <- function(fname) {
lns = strsplit(readLines("somefile.txt"), "")
v = match(unlist(lns), c("0", "1")) - 1L
matrix(v, nrow=length(lns), byrow=TRUE)
}
对于 800 x 70000 行示例,这大约需要 5 秒。与其他响应相比,它也比所有其他解决方案都快(我无法轻松安装 iotools,抱怨 C 级缺少符号 Rspace)并且不对 OS 和 [= 的可用性做出假设22=] 工具(以及除 R 之外的这些工具的知识!)。