我有固定宽度的数据文件(.dbf),它们没有行分隔符。以下是该数据文件的两行:
20141101 77h 3.210 0 3 20141102 76h 3.090 0 3
对于日期(8),一行的宽度是c(8,4,7,41)
,有些时间是度量(4),数据点(7),以及我可以在一个"休息&#中总结的一些其他列34;第(41)栏。在一行之后没有分隔符,下一行只是附加到第一行。所有时间步骤基本上都是连续写入一条大线。此文件中只包含数字,字符和空格。
由于缺少行分隔符,read.fwf('filepath', widths = c(8,4,7,41))
R在第一行后停止读取。
当没有行分隔符时,是否有一个参数告诉read.fwf()
何时开始读取新行?或者我应该使用不同的读命令吗?
提前致谢。
答案 0 :(得分:4)
也许不是最好的主意,但这应该有效:
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
返回一个列表,此处包含1个元素,因为只有一行作为输入,因此我们将其与[[1]]
一起删除。
现在返回的是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
答案 1 :(得分:3)
readLines
,substr
,trimws
,separate
( tidyr )和{{1>的另一种可能不那么优雅的解决方案( dplyr ):
mutate_all
给出:
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
内使用funs(ul = type.convert(trimws(.)))
。
答案 2 :(得分:0)
除了其他答案外,还有一些关于dbf files
的一般信息:
除非这是一次性读取静态文件,否则最好先检查文件/字段结构,以防随时间变化。有关dbf文件的内部结构,请参阅here。
但也许更重要的是:
dbf文件中的每条记录前面都有一个字节用于删除标记 strong>。如果这是一个空格,则不删除记录,如果它是星号*
,则记录被标记为删除(在文件打包之前,记录不会从dbf文件中删除),你可能想跳过那些记录。例如,数据的第一部分也可以用“DELETED
”覆盖。
所以,在你的记录c(8,4,7,41)
中, rest列(41)的最后一个字节实际上是它后面的记录的删除标志 - 以及文件中的最后一个记录该字段只有40个字节(但如果你很幸运,该文件有一个EOF标记(0x1a
),所以也许你的大小没有问题。)
因此,您的记录实际上应该是:c(1,8,4,7,40)
,其中1
是删除标记,并且更快地开始一个字节。