如何将含双记录类型的固定宽度ASCII仇恨犯罪文件导入R?
导入2021年仇恨犯罪固定宽度文件到R的分步方案
1. 准备字段定义
首先需要获取官方提供的固定宽度字段说明文档,明确BH(Batch Header)和IR(Incident Report)两种记录各自的字段名、起始位置、宽度及数据类型。整理成如下结构的字段定义表:
# BH记录字段定义示例 bh_spec <- tibble::tribble( ~col_name, ~start, ~end, ~type, "record_type", 1, 2, "character", "ori", 3, 9, "character", "agency_name", 10, 59, "character", # 按实际文档补充剩余BH字段 ) # IR记录字段定义示例 ir_spec <- tibble::tribble( ~col_name, ~start, ~end, ~type, "record_type", 1, 2, "character", "incident_number", 3, 12, "character", "offense_date", 13, 20, "date", # 按实际文档补充剩余IR字段 )
2. 读取原始文件
用readr::read_lines()读取整份文件的每一行,保留原始行结构以便后续区分记录类型:
library(readr) raw_lines <- read_lines("2021_hate_crime_master_file.txt")
3. 标记并分组记录
给每行标记记录类型,然后以BH记录为节点生成分组ID,关联对应的IR记录:
library(dplyr) library(tidyr) # 转换为数据框并标记记录类型 line_df <- tibble(line = raw_lines) %>% mutate(record_type = substr(line, 1, 2)) # 生成BH分组ID:每遇到BH记录,分组ID递增 line_df <- line_df %>% mutate(bh_group_id = cumsum(record_type == "BH")) # 提取BH数据并拆分字段 bh_data <- line_df %>% filter(record_type == "BH") %>% mutate(across(line, ~str_sub(., bh_spec$start, bh_spec$end))) %>% unnest_wider(line, names_sep = "") %>% rename_with(~bh_spec$col_name, starts_with("line")) %>% select(bh_group_id, everything()) # 提取IR数据并拆分字段 ir_data <- line_df %>% filter(record_type == "IR") %>% mutate(across(line, ~str_sub(., ir_spec$start, ir_spec$end))) %>% unnest_wider(line, names_sep = "") %>% rename_with(~ir_spec$col_name, starts_with("line")) %>% select(bh_group_id, everything())
4. 生成目标数据结构
选项1:带BH分组的单数据框
通过分组ID左连接BH和IR数据,无事件的BH记录会保留(IR字段为NA):
combined_df <- bh_data %>% left_join(ir_data, by = "bh_group_id")
选项2:按ORI分组的多数据框列表
将数据按ORI拆分并存储为命名列表,方便单独查看每个机构的事件数据:
ori_data_list <- combined_df %>% group_split(ori) %>% set_names(combined_df$ori %>% unique()) # 示例:获取指定ORI的数据 ori_data_list["NY0010000"]
5. 数据类型转换
根据字段定义转换对应的数据类型,比如日期、数值字段:
# 转换BH数据类型示例 bh_data <- bh_data %>% mutate( total_incidents = as.integer(total_incidents), report_date = as.Date(report_date, format = "%Y%m%d") ) # 转换IR数据类型示例 ir_data <- ir_data %>% mutate( offense_date = as.Date(offense_date, format = "%Y%m%d"), number_offenses = as.integer(number_offenses) )
注意:实际操作需严格匹配官方文档的字段宽度和格式;若遇编码问题,可在read_lines()中添加locale = locale(encoding = "latin1")参数调整。
内容的提问来源于stack exchange,提问作者Kayla Ehman
相关产品推荐
相关产品推荐

