【问题标题】:Reading fixed width format data into R with entries exceeding column width将固定宽度格式数据读取到 R 中,条目超过列宽
【发布时间】:2018-02-28 19:37:02
【问题描述】:

我需要使用美国人口普查局分发的大都会地区年度建筑许可证,它们是downloadable here 作为固定宽度格式的文本文件。这是文件的摘录(我已经删除了列名,因为它们的格式不是很好,可以在将文件读入日期框架后替换):

999 10180 Abilene, TX                             306     298       8       0       0       0
184 10420 Akron, OH                               909     905       0       4       0       0
999 13980 Blacksburg-Christiansburg-Radford,
  VA                                              543     455       0       4      84       3
145 14010 Bloomington, IL                         342     214       4       0     124       7
160 15380 Buffalo-Cheektowaga-Niagara Falls,*
  NY                                             1964     931      14      14    1005      68
268 15500 Burlington, NC                         1353     938      12      16     387      20

从上面的摘录中可以看出,名称列中的许多条目超过了列的宽度(看起来是 36 个字符)。我已经尝试了 utils 包和 readr 的各种 fwf 读取功能,但找不到将这些条目考虑在内的解决方案。任何提示将不胜感激。


编辑:原始文件摘录由 mod 编辑以进行格式化,在此过程中,超出第三列宽度的示例条目被删除。我已经更新了摘录以重新包含它们并删除了列名。

我运行了@markdly 的代码,该代码在此编辑之前提交,适用于所有不存在此问题的条目。我将结果导出到 csv,并在下面包含了一段摘录,以显示这些条目会发生什么:

"38","999",NA,"13980",NA,"Blacksburg-Christiansburg-Radford,",NA,NA,NA,NA,NA,NA
"39","V","A",NA,NA,NA,"543",455,0,4,84,3
"40","145",NA,"14010",NA,"Bloomington, IL","342",214,4,0,124,7
"51","160",NA,"15380",NA,"Buffalo-Cheektowaga-Niagara Falls,*",NA,NA,NA,NA,NA,NA
"52","N","Y",NA,NA,NA,"1964",931,14,14,1005,68
"53","268",NA,"15500",NA,"Burlington, NC","1353",938,12,16,387,20

编辑 2:大多数我实际上正在研究的主要都市区不属于这个问题类别,所以虽然有数据会很好,但如果没有可行的解决方案,是否有办法从数据集中完全删除这些条目?

【问题讨论】:

  • 我稍微编辑了这个,以便更准确地显示您正在处理的内容。

标签: r fixed-width read.fwf


【解决方案1】:

编辑
根据更新的信息,某些记录的文件宽度不固定。在这种情况下,我认为readr::read_tableread_fwf 更有用。以下示例是一种tidyverse 方法来导入和处理其中一个源文件 (tb3u2016.txt)。基本方法可能涉及使用 readLines 之类的东西。

步骤 1 读入文件并为拆分记录分配一个公共记录 id

library(tidyverse)
df <- read_table("tb3u2016.txt", col_names = FALSE, skip = 11) %>%
  rownames_to_column() %>%
  mutate(record = if_else(lag(is.na(X2) & rowname > 1), lag(rowname), rowname))

df[37:40, ]
#> # A tibble: 4 x 8
#>   rowname                                                    X1    X2
#>     <chr>                                                 <chr> <int>
#> 1      37 999 13900 Bismarck, ND                            856   629
#> 2      38          999 13980 Blacksburg-Christiansburg-Radford,    NA
#> 3      39   VA                                              543   455
#> 4      40 145 14010 Bloomington, IL                         342   214
#> # ... with 5 more variables: X3 <int>, X4 <int>, X5 <int>, X6 <int>,
#> #   record <chr>

第 2 步 合并拆分记录文本,然后使用 tidyr::extract 将内容放入单独的变量中。修剪空白并删除多余的记录。

df <- df %>%
  mutate(new_X1 = if_else(rowname != record, paste0(lag(X1), X1), X1)) %>%
  extract(new_X1, c("CSA", "CBSA", "Name", "Total"), "([0-9]+) ([0-9]+) (.+) ([0-9]+)") %>%
  mutate(Name = trimws(Name)) %>%
  filter((lead(record) != record) | rowname == 1) %>%
  select(CSA, CBSA, Name, Total, X2, X3, X4, X5, X6)

df[37:39, ]
#> # A tibble: 3 x 9
#>     CSA  CBSA                                 Name Total    X2    X3    X4
#>   <chr> <chr>                                <chr> <chr> <int> <int> <int>
#> 1   999 13900                         Bismarck, ND   856   629    16     6
#> 2   999 13980 Blacksburg-Christiansburg-Radford,VA   543   455     0     4
#> 3   145 14010                      Bloomington, IL   342   214     4     0
#> # ... with 2 more variables: X5 <int>, X6 <int>

以下是使用readr::read_fwf 为问题的早期版本提供的解决方案的精简版本。

示例数据

library(readr)

# example data
txt <- "                                                                                        Num of
                                                                                        Struc-
                                                                                        tures
                                                                                        With
                                                                      3 and 4  5 Units  5 Units
CSA CBSA  Name                                   Total 1 Unit 2 Units   Units  or more  or more

999 10180 Abilene, TX                             306     298       8       0       0       0
184 10420 Akron, OH                               909     905       0       4       0       0" 

write_file(txt, "example.txt")

解决方案

col_widths <- c(3, 1, 5, 1, 36, 8, 8, 8, 8, 8, NA)
col_names <- c("CSA", "blank_1", "CBSA", "blank_2", "Name", "Total", "units_1", "units_2", 
               "units_3_and_4", "units_5_or_more", "num_struc_5_or_more")
df <- read_fwf("example.txt", fwf_widths(col_widths, col_names), skip = 7)
df
#> # A tibble: 2 x 11
#>     CSA blank_1  CBSA blank_2        Name Total units_1 units_2
#>   <int>   <chr> <int>   <chr>       <chr> <int>   <int>   <int>
#> 1   999    <NA> 10180    <NA> Abilene, TX   306     298       8
#> 2   184    <NA> 10420    <NA>   Akron, OH   909     905       0
#> # ... with 3 more variables: units_3_and_4 <int>, units_5_or_more <int>,
#> #   num_struc_5_or_more <int>

【讨论】:

  • 感谢您的回复@markdly。正如我对帖子所做的编辑中所述,为了格式化而对 mod 进行的编辑删除了问题条目的示例。这些条目超过了名称列的 36 个字符宽度。我运行了您的代码,它适用于所有其他条目,但在原始帖子中添加了 csv 输出的摘录,以显示有问题的条目的样子。
  • @user2424281,我明白你的意思 - 任何特别长的名称都会导致记录跨越两行。在这种情况下,您可能需要使用类似答案stackoverflow.com/a/35761217/8475145 的方式逐行解析文件。为了快速修复,如果它们不是必需的,您可以通过在 Total / units_1 / unit_2 列等中对 NA 进行过滤来排除这些问题记录...
  • @user2424281,我认为您可以使用read_table 以及一些数据整理来做到这一点。有关详细信息,请参阅编辑后的答案...
  • 非常感谢您的帮助。感谢您愿意使用更新的信息返回此问题。一旦有机会,我会亲自尝试您的解决方案,但与此同时,我会继续将您的解决方案标记为答案,因为看起来您已经充分了解了一切!
  • 我终于开始运行您的代码,但在第二个块之后遇到错误。 ("if (drop) warningc("drop allowed") 中的错误:参数不能解释为逻辑")
猜你喜欢
  • 2013-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多