导入没有行分隔符的固定宽度数据文件
Import fixed width data file with no line separator
我有没有行分隔符的固定宽度数据文件 (.dbf)。这是该数据文件的两行内容:
20141101 77h 3.210 0 3 20141102 76h 3.090 0 3
对于日期 (8)、一些时间度量 (4)、数据点 (7) 以及我可以总结在一个中的其他一些列,一行的宽度是 c(8,4,7,41)
"rest" 栏目 (41)。在一行之后没有分隔符,下一行只是附加到第一行。所有时间步长基本上都连续地写在一行中。此文件中只有数字、字符和白色space。
With read.fwf('filepath', widths = c(8,4,7,41))
由于缺少行分隔符,R 在第一行之后停止读取。
是否有参数告诉 read.fwf()
在没有行分隔符的情况下何时开始读取新行?或者我应该使用不同的读取命令?
提前致谢。
也许不是最好的主意,但这应该可行:
content <- scan('filepath','character',sep='~') # Warning choose a sep not appearing in datas to get the whole file.
# Split content in lines:
lines <- regmatches(content,gregexpr('.{60}',content))[[1]]
x <- tempfile()
write(lines,x)
data <- read.fwf(x, widths = c(8,4,7,41))
unlink(x)
想法是读取整个文件,将每次出现的 60 个字符放入一个条目中,将其写入临时文件,并在删除临时文件之前从该临时文件中读取数据。
另一种方法适用于正则表达式和包 stringr
(仍然包含上述扫描产生的内容):
library(stringr)
d <- data.frame( str_match_all( content, "(.{8})(.{4})(.{7})(.{41})")[[1]][,2:5], stringsAsFactors=FALSE)
给出:
V1 V2 V3 V4
1 20141101 77h 3.210 0 3
2 20141102 76h 3.090 0 3
str_match_all
return 一个列表,这里有 1 个元素,因为只有一行作为输入,所以我们用 [[1]]
.
删除它
现在 return 是 5 列,第一列是完全匹配,其他是捕获组,所以我们对第 2 列到第 5 列的矩阵进行子集化,只得到我们需要的 4 列并包装它在 as.data.frame
中得到一个 data.frame 最后。
然后您可以使用 colnames(d) <- c('date','time','data_point','rest')
命名列
如果你想清理空白,你可以将 str_extract_all 结果包装在 trimws
中(感谢 @jaap 提醒这个功能),如下所示:
td <- data.frame( trimws( str_match_all( content, "(.{8})(.{4})(.{7})(.{41})")[[1]][,2:5] ), stringsAsFactors=FALSE)
输出:
X1 X2 X3 X4
1 20141101 77h 3.210 0 3
2 20141102 76h 3.090 0 3
一个不同的,可能不太优雅的解决方案 readLines
, substr
, trimws
, separate
(tidyr ) 和 mutate_all
(dplyr):
txt <- readLines('filepath')
dfx <- data.frame(V1 = sapply(seq(from=1, to=nchar(txt), by=60),
function(x) substr(txt, x, x+59)))
library(dplyr)
library(tidyr)
dfx %>%
separate(V1, c(paste0("V",LETTERS[1:5])), c(8,12,19,55)) %>%
mutate_all(trimws)
给出:
VA VB VC VD VE
1 20141101 77h 3.210 0 3
2 20141102 76h 3.090 0 3
要获得不同的列名,只需将 c(paste0("V",LETTERS[1:5])
替换为您想要的列名向量即可。
如果要将列转换为正确的 类 而不是 character
,可以在 mutate_all
.
中使用 funs(ul = type.convert(trimws(.)))
除了其他答案外,还有一些关于 dbf files
的一般信息:
除非这是静态文件的一次性读取,否则最好先检查 file/fields 结构,以防随时间变化。有关 dbf 文件的内部结构,请参阅 here。
但也许更重要:
dbf 文件中的每条记录前面都有一个字节用于删除标志。如果这是 space,则记录不会被删除,如果它是星号 *
,则记录被标记为删除(记录不会从 dbf 文件中删除,直到文件被 打包),您可能想跳过这些记录。例如,数据的第一部分也可以用“DELETED
”覆盖。
因此,在您的记录 c(8,4,7,41)
中,rest 列的最后一个字节 (41) 实际上是其后记录的删除标志 - 以及文件中的最后一条记录对于该字段只有 40 个字节(但如果你幸运的话,该文件有一个 EOF 标记 (0x1a
),所以你可能对那里的大小没有问题)。
因此,您的记录实际上应该是:c(1,8,4,7,40)
,其中 1
是删除标志,并且提前一个字节开始。
我有没有行分隔符的固定宽度数据文件 (.dbf)。这是该数据文件的两行内容:
20141101 77h 3.210 0 3 20141102 76h 3.090 0 3
对于日期 (8)、一些时间度量 (4)、数据点 (7) 以及我可以总结在一个中的其他一些列,一行的宽度是 c(8,4,7,41)
"rest" 栏目 (41)。在一行之后没有分隔符,下一行只是附加到第一行。所有时间步长基本上都连续地写在一行中。此文件中只有数字、字符和白色space。
With read.fwf('filepath', widths = c(8,4,7,41))
由于缺少行分隔符,R 在第一行之后停止读取。
是否有参数告诉 read.fwf()
在没有行分隔符的情况下何时开始读取新行?或者我应该使用不同的读取命令?
提前致谢。
也许不是最好的主意,但这应该可行:
content <- scan('filepath','character',sep='~') # Warning choose a sep not appearing in datas to get the whole file.
# Split content in lines:
lines <- regmatches(content,gregexpr('.{60}',content))[[1]]
x <- tempfile()
write(lines,x)
data <- read.fwf(x, widths = c(8,4,7,41))
unlink(x)
想法是读取整个文件,将每次出现的 60 个字符放入一个条目中,将其写入临时文件,并在删除临时文件之前从该临时文件中读取数据。
另一种方法适用于正则表达式和包 stringr
(仍然包含上述扫描产生的内容):
library(stringr)
d <- data.frame( str_match_all( content, "(.{8})(.{4})(.{7})(.{41})")[[1]][,2:5], stringsAsFactors=FALSE)
给出:
V1 V2 V3 V4
1 20141101 77h 3.210 0 3
2 20141102 76h 3.090 0 3
str_match_all
return 一个列表,这里有 1 个元素,因为只有一行作为输入,所以我们用 [[1]]
.
现在 return 是 5 列,第一列是完全匹配,其他是捕获组,所以我们对第 2 列到第 5 列的矩阵进行子集化,只得到我们需要的 4 列并包装它在 as.data.frame
中得到一个 data.frame 最后。
然后您可以使用 colnames(d) <- c('date','time','data_point','rest')
如果你想清理空白,你可以将 str_extract_all 结果包装在 trimws
中(感谢 @jaap 提醒这个功能),如下所示:
td <- data.frame( trimws( str_match_all( content, "(.{8})(.{4})(.{7})(.{41})")[[1]][,2:5] ), stringsAsFactors=FALSE)
输出:
X1 X2 X3 X4
1 20141101 77h 3.210 0 3
2 20141102 76h 3.090 0 3
一个不同的,可能不太优雅的解决方案 readLines
, substr
, trimws
, separate
(tidyr ) 和 mutate_all
(dplyr):
txt <- readLines('filepath')
dfx <- data.frame(V1 = sapply(seq(from=1, to=nchar(txt), by=60),
function(x) substr(txt, x, x+59)))
library(dplyr)
library(tidyr)
dfx %>%
separate(V1, c(paste0("V",LETTERS[1:5])), c(8,12,19,55)) %>%
mutate_all(trimws)
给出:
VA VB VC VD VE
1 20141101 77h 3.210 0 3
2 20141102 76h 3.090 0 3
要获得不同的列名,只需将 c(paste0("V",LETTERS[1:5])
替换为您想要的列名向量即可。
如果要将列转换为正确的 类 而不是 character
,可以在 mutate_all
.
funs(ul = type.convert(trimws(.)))
除了其他答案外,还有一些关于 dbf files
的一般信息:
除非这是静态文件的一次性读取,否则最好先检查 file/fields 结构,以防随时间变化。有关 dbf 文件的内部结构,请参阅 here。
但也许更重要:
dbf 文件中的每条记录前面都有一个字节用于删除标志。如果这是 space,则记录不会被删除,如果它是星号 *
,则记录被标记为删除(记录不会从 dbf 文件中删除,直到文件被 打包),您可能想跳过这些记录。例如,数据的第一部分也可以用“DELETED
”覆盖。
因此,在您的记录 c(8,4,7,41)
中,rest 列的最后一个字节 (41) 实际上是其后记录的删除标志 - 以及文件中的最后一条记录对于该字段只有 40 个字节(但如果你幸运的话,该文件有一个 EOF 标记 (0x1a
),所以你可能对那里的大小没有问题)。
因此,您的记录实际上应该是:c(1,8,4,7,40)
,其中 1
是删除标志,并且提前一个字节开始。