在R中读取固定宽度格式文件

4
我正在尝试使用read.fwf将这个固定宽度的文件读入R中:
http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for
当我执行此函数时,我会得到一些奇怪的错误,除非我以非常特定的方式阅读它,否则无法解决。
> fwf <- read.fwf("getdata_wksst8110.for", 1:9, skip = 4)
> head(fwf)
  V1 V2  V3   V4 V5     V6  V7       V8   V9
1 NA  3 JAN 1990 NA 23.4-0 0.4 25.1-0.3 26.6
2 NA 10 JAN 1990 NA 23.4-0 0.8 25.2-0.3 26.6
3 NA 17 JAN 1990 NA 24.2-0 0.3 25.3-0.3 26.5
4 NA 24 JAN 1990 NA 24.4-0 0.5 25.5-0.4 26.5
5 NA 31 JAN 1990 NA 25.1-0 0.2 25.8-0.2 26.7
6 NA  7 FEB 1990 NA 25.8 0 0.2 26.1-0.1 26.8

然而,通过将输出与原始文件进行比较,您可以清楚地看到它是错误的。确实应该有9列,但它正在切割我的日期列和其他列。
如果我使用sep = " "参数,它只会抛出一个错误:
> fwf <- read.fwf("getdata_wksst8110.for", 1:9, skip = 4, sep = " ")
Error in scan(file, what, nmax, sep, dec, quote, skip, nlines, na.strings,  : 
  line 6 did not have 25 elements

请问有人能帮我解决为什么这个代码没有按照我预期的方式读取吗?

我找到了一个与使用这个函数相关的有用链接,但这更多地是一个性能相关的问题。作者从未定义宽度=列参数。

谢谢您考虑这个微不足道的问题。

所以我按@MrFlick建议使用宽度向量重新运行操作,数据看起来好多了。然而,我看到的是“sep”参数明显在制造麻烦。如果我使用sep=" ",它会抛出奇怪的错误。但是如果我不使用sep,则会弄乱我的列结果。

*

Non-jerked results using widths = c(10, 4, 4, 4, 4, 4, 4, 4, 4)
    > head(fwf)
              V1 V2 V3   V4 V5 V6   V7  V8 V9
    1  03JAN1990 NA 23 4-0.  4 25 .1-0 0.3  2
    2  10JAN1990 NA 23 4-0.  8 25 .2-0 0.3  2
    3  17JAN1990 NA 24 2-0.  3 25 .3-0 0.3  2
    4  24JAN1990 NA 24 4-0.  5 25 .5-0 0.4  2
    5  31JAN1990 NA 25 1-0.  2 25 .8-0 0.2  2
    6  07FEB1990 NA 25 8 0.  2 26 .1-0 0.1  2

使用以下方法得到抖动结果:

fwf <- read.fwf("getdata_wksst8110.for", widths = c(10, 4, 4, 4, 4, 4, 4, 4, 4), skip = 4, sep = " ") 出现错误:在第6行中未找到25个元素

我在sep参数上漏掉了什么吗?

#

修改@MrFlick的脚本似乎基本符合要求!但是第一行数据依旧有问题,这使得我无法对hd[4]进行汇总。奇怪的是,删除第一行hd[-1,]并没有起到任何作用。好吧。

hd<-read.fwf("http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for", 
             widths=c(10,rep(c(9,4),4)), skip=3)

trim <- function(x) gsub("^\\s+|\\s+$","",x)
main <- paste0(trim(hd[1,seq(2, ncol(hd), by=2)]), trim(hd[1,seq(3, ncol(hd), by=2)]))
sub <- trim(as.vector(hd[2,]))

names(hd) <- make.names(c(sub[1],paste(rep(main, each=2), sub[-1])))

你认为 1:9 是在做什么?该参数应该指定每列的宽度(以字符数为单位)。看起来你并没有正确地指定列宽。此外,你可能需要查看 readr 包中的 read_fwf 函数,因为基本的 read.fwf 函数效率相当低(如果这是一个问题的话)。 - MrFlick
我阅读了文档和read.table。所有列的列宽都是可变的。例如,日期为9L,其他8列通常在3到4L之间变化。-0.50 = 4,25.5 = 3,0.03 = 3等。 - Zach
这就是为什么要提供一个宽度向量。因此,如果第一个字符是8个字符,第二个字符是4个字符,那么你就需要从c(8,4,...)开始。您为9列中的每一列指定一个宽度。 - MrFlick
我总是忘记可以提供一个数据点向量来使用。 - Zach
设置 widths = 4,意味着您只有一个宽度为4的列。如果您有9个宽度为4的列,则可以执行 widths=c(4,4,4,4,4,4,4,4,4) 或更简洁地执行 widths=rep(4,9)。这就是固定宽度文件的问题,您需要指定所有列的宽度;这是解析文件的唯一方法。 - MrFlick
1个回答

3

下面是一个应该能读取数据的命令

dd<-read.fwf("http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for",
    widths=c(10, rep(c(9,4),4)), skip=4)

请注意,宽度需要考虑每行中的所有字符,因此即使在列之间有空格,您也需要将其分配给其中一个列。
然后,我想不出一个非常干净的方法来获取标题。这个方法虽然可行,但很丑并且做了一些假设。
hd<-read.fwf("http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for", 
    widths=c(10,rep(c(9,4),4)), skip=2, nrow=2, as.is=T)

trim <- function(x) gsub("^\\s+|\\s+$","",x)
main <- paste0(trim(hd[1,seq(2, ncol(hd), by=2)]), trim(hd[1,seq(3, ncol(hd), by=2)]))
sub <- trim(as.vector(hd[2,]))

names(dd) <- make.names(c(sub[1],paste(rep(main, each=2), sub[-1])))

最后,您可以使用以下代码创建正确的日期值:

dd$Week <- as.Date(as.character(dd$Week), "%d%b%Y")

您不应该使用sep=参数。实际上,read.fwf的作用是将固定宽度文件重新编写为使用sep作为分隔符的分隔文件,然后使用更标准的read.table()读取分隔文件。默认值sep="\t"通常是安全的,因为通常您的实际数据中没有制表符。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接