【问题标题】:Reading fixed width file with two width schemes使用两种宽度方案读取固定宽度文件
【发布时间】:2019-06-21 15:52:55
【问题描述】:

我想读入一个大的固定宽度文件到 R。固定宽度文件包含两种类型的行。即:以“A”开头的行包含由一组宽度定义的变量,以“B”开头的行包含由另一组宽度定义的变量。基于mtcars的玩具示例:

AMazda RX4           21.0 160.0
BHornet 4 Drive       1  0    3
BHornet Sportabout    0  0    3
AMazda RX4 Wag       21.0 160.0
ADatsun 710          22.8 108.0
AHornet 4 Drive      21.4 258.0
BValiant              1  0    3
AHornet Sportabout   18.7 360.0
BDuster 360           0  0    3

目前,我使用两个readr::read_fwf 命令读取文件,并在读取As 后删除B 行,反之亦然。但是当然,对于一个固定宽度的大文件,这意味着两次读取数据。而且,如果A 中的一些字符串变量与B 中的一些数值变量在位置上重叠,那么read_fwf 的(最终)列类型错误,我必须处理下游(在下面的玩具示例)。

有什么聪明的想法可以提高速度和工作流程吗?

我目前的“解决方案”:

example <- "
AMazda RX4           21.0 160.0
BHornet 4 Drive       1  0    3
BHornet Sportabout    0  0    3
AMazda RX4 Wag       21.0 160.0
ADatsun 710          22.8 108.0
AHornet 4 Drive      21.4 258.0
BValiant              1  0    3
AHornet Sportabout   18.7 360.0
BDuster 360           0  0    3"

library(tidyverse)
library(readr)

in_a <- read_fwf(example, fwf_widths(c(1, 20, 4, 5), c("code", "name", "mpg", "disp"))) %>%
  filter(code == "A")

in_b <- read_fwf(example, fwf_widths(c(1, 20, 4, 3, 3), c("code", "name", "vs", "am", "gear"))) %>%
  filter(code == "B")

结果

> in_a
# A tibble: 5 x 4
  code  name                mpg  disp
  <chr> <chr>             <dbl> <dbl>
1 A     Mazda RX4          21     160
2 A     Mazda RX4 Wag      21     160
3 A     Datsun 710         22.8   108
4 A     Hornet 4 Drive     21.4   258
5 A     Hornet Sportabout  18.7   360
> in_b
# A tibble: 4 x 5
  code  name                 vs    am  gear
  <chr> <chr>             <dbl> <dbl> <dbl>
1 B     Hornet 4 Drive        1     0     3
2 B     Hornet Sportabout     0     0     3
3 B     Valiant               1     0     3
4 B     Duster 360            0     0     3

【问题讨论】:

    标签: r readr


    【解决方案1】:

    一种方法(如果您只有两种类型)是使用comment= 参数:

    library(readr)
    in_a <- read_fwf("~/StackOverflow/jfeigenbaum.txt", fwf_widths(c(1, 20, 4, 5), c("code", "name", "mpg", "disp")),
                     comment = "B")
    in_a
    # # A tibble: 5 x 4
    #   code  name                mpg  disp
    #   <chr> <chr>             <dbl> <dbl>
    # 1 A     Mazda RX4          21     160
    # 2 A     Mazda RX4 Wag      21     160
    # 3 A     Datsun 710         22.8   108
    # 4 A     Hornet 4 Drive     21.4   258
    # 5 A     Hornet Sportabout  18.7   360
    

    如果你有 2 种以上的类型并且想稍微概括一下,可以使用 pipe() 并 grep 出来(使用命令行 grep,而不是 R 函数 grep())。

    read_fwf(paste(readLines(pipe("grep ^A ~/StackOverflow/jfeigenbaum.txt")), collapse = "\n"),
             fwf_widths(c(1, 20, 4, 5), c("code", "name", "mpg", "disp")))
    # # A tibble: 5 x 4
    #   code  name                mpg  disp
    #   <chr> <chr>             <dbl> <dbl>
    # 1 A     Mazda RX4          21     160
    # 2 A     Mazda RX4 Wag      21     160
    # 3 A     Datsun 710         22.8   108
    # 4 A     Hornet 4 Drive     21.4   258
    # 5 A     Hornet Sportabout  18.7   360
    

    不幸的是,虽然readr::read_table 现在可以使用pipe(...) 作为基于连接的输入,但read_fwf(pipe(...)) 似乎不起作用,因此我们需要上述临时(并且公认效率较低)的解决方法。 (如果您的数据不大,这应该不会造成问题。)

    最后,如果你的数据真的是一个字符串,你可以grep()将它拆分成单独的行:

    example <- strsplit("
    AMazda RX4           21.0 160.0
    BHornet 4 Drive       1  0    3
    BHornet Sportabout    0  0    3
    AMazda RX4 Wag       21.0 160.0
    ADatsun 710          22.8 108.0
    AHornet 4 Drive      21.4 258.0
    BValiant              1  0    3
    AHornet Sportabout   18.7 360.0
    BDuster 360           0  0    3", "\\n")[[1]]
    
    read_fwf(grep("^A", example, value = TRUE),
             fwf_widths(c(1, 20, 4, 5), c("code", "name", "mpg", "disp")))
    # # A tibble: 5 x 4
    #   code  name                mpg  disp
    #   <chr> <chr>             <dbl> <dbl>
    # 1 A     Mazda RX4          21     160
    # 2 A     Mazda RX4 Wag      21     160
    # 3 A     Datsun 710         22.8   108
    # 4 A     Hornet 4 Drive     21.4   258
    # 5 A     Hornet Sportabout  18.7   360
    

    【讨论】:

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