将条目超过列宽的固定宽度格式数据读入 R

Reading fixed width format data into R with entries exceeding column width

我需要使用美国人口普查局分发的按大都市区划分的年度建筑许可数据,这些 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:大多数 我实际查看的主要都市区不属于这个问题类别,所以虽然有数据会很好那些这样做的,如果没有可行的解决方案,是否有办法将这些条目从数据集中完全删除?

编辑:
根据更新的信息,某些记录的文件宽度不是固定的。在这种情况下,我觉得readr::read_tableread_fwf更有用。以下示例是导入和处理其中一个源文件 (tb3u2016.txt) 的 tidyverse 方法。基本方法可能涉及使用 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将内容放入单独的变量中。 Trim 空格并删除多余的记录。

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>