【问题标题】:Read fixed width text file读取固定宽度的文本文件
【发布时间】:2013-01-01 06:22:09
【问题描述】:

我正在尝试将这个格式难看的数据集加载到我的 R 会话中: http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for

Weekly SST data starts week centered on 3Jan1990

Nino1+2      Nino3        Nino34        Nino4
Week          SST SSTA     SST SSTA     SST SSTA     SST SSTA 
03JAN1990     23.4-0.4     25.1-0.3     26.6 0.0     28.6 0.3 
10JAN1990     23.4-0.8     25.2-0.3     26.6 0.1     28.6 0.3 
17JAN1990     24.2-0.3     25.3-0.3     26.5-0.1     28.6 0.3

到目前为止,我可以阅读这些行

  x = readLines(path)

但该文件将“空白”与“-”混合作为分隔符,而且我不是正则表达式专家。 我感谢任何帮助将其变成一个漂亮而干净的 R 数据框。 谢谢!

【问题讨论】:

  • 并查看read.fwf 以读取固定宽度格式的数据。
  • 我认为处理每一行是一个更好的主意。它混合了 '-' 和 ' ' 字符。
  • 或者,您可以说空格或 - 只是一个字符,因此首先用制表符替换所有多次出现的空格,然后拆分所有制表符分隔的条目的 on - 或空格。
  • 固定宽度 = 无分隔符。这意味着“-”是一个减号,空格也不是分隔符,它们只会在数字没有填满整个可用宽度时出现

标签: r fixed-width


【解决方案1】:

这是一个固定宽度的文件。使用read.fwf()阅读:

x <- read.fwf(
  file=url("http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for"),
  skip=4,
  widths=c(12, 7, 4, 9, 4, 9, 4, 9, 4))

head(x)

            V1   V2   V3   V4   V5   V6   V7   V8  V9
1  03JAN1990   23.4 -0.4 25.1 -0.3 26.6  0.0 28.6 0.3
2  10JAN1990   23.4 -0.8 25.2 -0.3 26.6  0.1 28.6 0.3
3  17JAN1990   24.2 -0.3 25.3 -0.3 26.5 -0.1 28.6 0.3
4  24JAN1990   24.4 -0.5 25.5 -0.4 26.5 -0.1 28.4 0.2
5  31JAN1990   25.1 -0.2 25.8 -0.2 26.7  0.1 28.4 0.2
6  07FEB1990   25.8  0.2 26.1 -0.1 26.8  0.1 28.4 0.3

更新

readr(2015 年 4 月发布)提供了一种简单快速的替代方案。

library(readr)

x <- read_fwf(
  file="http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for",   
  skip=4,
  fwf_widths(c(12, 7, 4, 9, 4, 9, 4, 9, 4)))

速度比较:readr::read_fwf()utils::read.fwf () 快约 2 倍。

【讨论】:

  • @Andrie 你是怎么知道宽度和跳跃的?
  • @Koba:我将其中一行复制并粘贴到具有列数的文本编辑器中,并且我手动计算了每列的宽度(包括需要时的空格)。您还可以告诉您在获取原始数据之前需要跳过 4 整行。
  • @Pavithra 的回答下面带有用于跳过不需要的空白的负列宽可能更适合接受的答案。
  • @Andrie 你是如何得到 fwf_widths 值的?
  • @Ala 我相信readr::fwf_empty 会尝试为您猜测宽度。 readr::read_fwf 的示例显示了 readr::fwf_empty 的用法。
【解决方案2】:

另一种确定宽度的方法...

df <- read.fwf(
  file=url("http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for"),
  widths=c(-1, 9, -5, 4, 4, -5, 4, 4, -5, 4, 4, -5, 4, 4),
  skip=4
)

widths 参数中的 -1 表示应该忽略一个单字符列,widths 参数中的 -5 表示应该忽略一个五个字符列,同样...

参考:https://www.inkling.com/read/r-cookbook-paul-teetor-1st/chapter-4/recipe-4-6

【讨论】:

    【解决方案3】:

    首先,这个问题直接来自 Leeks 的 Coursera“获取数据并清理它”课程。虽然问题的另一部分,但困难的部分是读取文件。

    也就是说,该课程主要用于学习。

    我讨厌 R 的固定宽度程序。它很慢,而且对于大量变量,否定某些列等很快就会变得很痛苦。

    我认为使用readLines() 更容易,然后使用substr() 来制作变量

    x <- readLines(con=url("http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for"))
    
    # Skip 4 lines
    x <- x[-(1:4)]
    
    mydata <- data.frame(var1 = substr(x, 1, 10),
                         var2 = substr(x, 16, 19),
                         var3 = substr(x, 20, 23),
                         var4 = substr(x, 29, 32)  # and so on and so on
                         )
    

    【讨论】:

    • 这种方法对我有用。另外两个提示:1)您可以将 mydata 定义为您需要的数据。因此,如果您只需要第四列数据,它可以像mydata &lt;- data.frame(var4 = substr(x,29,32)) 一样简单。此外,对于 Windows 用户,带有 TextFX 插件的 Notepad++ 将为您提供一个简单明了的计数字符标尺,这样您就可以确定在 substr 中输入的开始和停止值的内容。但是请注意,停止值比您要保留的最后一个字符的位置大一。
    【解决方案4】:

    您现在可以使用 Hadley Wickham 的 readr 包中的 read_fwf() 函数。

    与基础 read.fwf() 相比,预计会有巨大的性能提升。

    【讨论】:

      【解决方案5】:

      我记录了here 在 R 中读取固定宽度文件的替代方案列表,并提供了一些最快的基准。

      我的首选方法是将freadstringi 结合使用;它作为最快的方法具有竞争力,并且具有将您的数据存储为data.table 的额外好处(IMO):

      library(data.table)
      library(stringi)
      
      col_ends <- 
        list(beg = c(1, 10, 15, 19, 23, 28, 32, 36,
                     41, 45, 49, 54, 58),
             end = c(9, 14, 18, 22, 27, 31, 35,
                     40, 44, 48, 53, 57, 61))
      
      data = fread(
        "http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for", 
        header = FALSE, skip = 4L, sep = NULL
        )[, lapply(1:(length(col_ends$beg)),
                   function(ii) 
                     stri_sub(V1, col_ends$beg[ii], col_ends$end[ii]))
          ][ , paste0("V", c(2, 5, 8, 11)) := NULL]
      #              V1   V3   V4   V6   V7   V9  V10  V12  V13
      #    1: 03JAN1990 23.4 -0.4 25.1 -0.3 26.6  0.0 28.6  0.3
      #    2: 10JAN1990 23.4 -0.8 25.2 -0.3 26.6  0.1 28.6  0.3
      #    3: 17JAN1990 24.2 -0.3 25.3 -0.3 26.5 -0.1 28.6  0.3
      #    4: 24JAN1990 24.4 -0.5 25.5 -0.4 26.5 -0.1 28.4  0.2
      #    5: 31JAN1990 25.1 -0.2 25.8 -0.2 26.7  0.1 28.4  0.2
      #   ---                                                  
      # 1365: 24FEB2016 27.1  0.9 28.4  1.8 29.0  2.1 29.5  1.4
      # 1366: 02MAR2016 27.3  1.0 28.6  1.8 28.9  1.9 29.5  1.4
      # 1367: 09MAR2016 27.7  1.2 28.6  1.6 28.9  1.8 29.6  1.5
      # 1368: 16MAR2016 27.5  1.0 28.8  1.7 28.9  1.7 29.6  1.4
      # 1369: 23MAR2016 27.2  0.9 28.6  1.4 28.8  1.5 29.5  1.2
      

      请注意,fread 会自动去除前导空格和尾随空格 - 有时,这是不可取的,在这种情况下设置 strip.white = FALSE


      我们也可以从列宽向量ww 开始:

      ww <- c(9, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4)
      nd <- cumsum(ww)
      
      col_ends <-
        list(beg = c(1, nd[-length(nd)]+1L),
             end = nd)
      

      我们可以通过使用负索引来选择更稳健地排除哪些列,例如:

      col_ends <- 
        list(beg = c(1, -10, 15, 19, -23, 28, 32, -36,
                     41, 45, -49, 54, 58),
             end = c(9, 14, 18, 22, 27, 31, 35,
                     40, 44, 48, 53, 57, 61))
      

      然后将col_ends$beg[ii] 替换为abs(col_ends$beg[ii]) 并在下一行:

      paste0("V", which(col_ends$beg < 0))
      

      最后,如果您还希望以编程方式读取列名,您可以使用readLines 进行清理:

      cols <-
        gsub("\\s", "", 
             sapply(1:(length(col_ends$beg)),
                    function(ii) 
                      stri_sub(readLines(URL, n = 4L)[4L], 
                               col_ends$beg[ii]+1L,
                               col_ends$end[ii]+1L)))
      
      cols <- cols[cols != ""]
      

      (请注意,将此步骤与fread 结合需要创建表的副本以删除标题行,因此对于大型数据集效率低下)

      【讨论】:

        【解决方案6】:

        我对 R 一无所知,但我可以为您提供一个匹配这些行的正则表达式:

        \s[0-9]{2}[A-Z]{3}[0-9]{4}(\s{5}[0-9]+\.[0-9]+[ -][0-9]+\.[0-9]+){4}
        

        【讨论】:

          猜你喜欢
          • 2016-01-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-05-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多