R处理PDF多列表格separate分列后数据错乱如何还原原表
问题根源
你当前的写法从根上就不适合提取PDF表格:pdf_text()只会返回按阅读顺序拼接的纯文本流,完全丢失原表格的列坐标、单元格跨行、跨页排版信息。你后续直接按换行拆行、用separate()按空白拆列的逻辑,会把单元格内部的空格、换行都当成分隔符,必然出现列错位、内容截断、数据丢失的问题,靠调正则根本补不回来位置信息的缺失。
可落地的解决方法
方法1:用专用表格提取包直接识别(准确率最高,代码量最少)
优先用tabulizer包,它底层基于Tabula PDF表格提取引擎,能自动识别表格线、列边界,不需要手动写拆分规则。如果自动识别有偏差,手动传列坐标就能100%对齐。
# 加载依赖 library(tabulizer) library(tidyverse) mintz_file = "https://www.mintz.com/sites/default/files/media/documents/2019-02-08/State%20Legislation%20on%20Biosimilars.pdf" # 提取2-23页的表格,针对带线框的规则表格用lattice方法识别率更高 raw_tables <- extract_tables( file = mintz_file, pages = 2:23, method = "lattice", output = "data.frame" ) # 合并跨页的表格,清理重复表头、空行 final_df <- bind_rows(raw_tables) %>% filter(trimws(as.character(State)) != "") %>% distinct()
如果自动识别出现列错位,先运行locate_areas(mintz_file, pages=2)交互点选表格范围和列边界,把得到的坐标参数传入extract_tables的area和columns参数即可,不需要反复调拆分逻辑。
注:这个包依赖Java运行环境,如果你不想装Java,就用下面的方法。
方法2:基于字符坐标手动拆分(兼容pdftools,不需要额外依赖)
用pdf_data()代替pdf_text(),这个函数会返回页面内每一个字符的x/y轴坐标、字号信息,你可以直接根据字符位置找列间隙,完全不需要靠正则猜分割点。
library(pdftools) library(tidyverse) mintz_file = "https://www.mintz.com/sites/default/files/media/documents/2019-02-08/State%20Legislation%20on%20Biosimilars.pdf" # 提取2-23页所有字符的坐标数据 raw_chars <- pdf_data(mintz_file)[2:23] # 第一步:计算全局列分割点:统计所有字符的x坐标分布,字符间隔大于20像素的位置就是列间隙 all_x <- map_dfr(raw_chars, ~.x) %>% pull(x) x_gaps <- diff(sort(unique(all_x))) col_breaks <- c(0, sort(unique(all_x))[which(x_gaps > 20)], 612) # 612是标准Letter页面宽度 # 第二步:逐页按坐标拼接单元格内容 final_df <- map_dfr(raw_chars, function(page){ page %>% # 给每个字符匹配所属列 mutate(col_id = cut(x, breaks = col_breaks, labels = c("idx","State","Substitution_Requirements", "Pharmacy_Notification_Requirements","Recordkeeping_Requirements"))) %>% # 同一行(y坐标相同)同一列的字符合并为单元格文本 group_by(y, col_id) %>% summarise(cell_text = paste(text, collapse = " "), .groups = "drop") %>% # 转成宽表格式 pivot_wider(names_from = col_id, values_from = cell_text) %>% arrange(y) }) %>% # 清理无关的序号列、空行、跨页重复表头 select(-y, -idx) %>% filter(trimws(as.character(State)) != "") %>% distinct()
原代码的具体错误点
- 直接按
\n拆分文本,会把单元格内部的换行误判为行分隔,导致同一个单元格的内容被拆成多行 - 调用
separate()时没有指定明确分隔符,默认按任意空白拆分,只要单元格内容里包含空格就会被错拆到不同列 - 用硬编码删前2行的逻辑鲁棒性极差,跨页出现的页眉、页脚、重复表头都会混入数据,无法统一清理
内容的提问来源于stack exchange,提问作者bandcar
相关产品推荐
相关产品推荐

