将条目超过列宽的固定宽度格式数据读入 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_table
比read_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>
我需要使用美国人口普查局分发的按大都市区划分的年度建筑许可数据,这些 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_table
比read_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>