基于格式不一致的逐字记录,使用R代码精准识别发言者轮次的优化方案及最佳实践问询
基于格式不一致的逐字记录,使用R精准识别发言者轮次的优化方案及最佳实践
处理WHO这类老会议的逐字记录确实头疼——格式混乱、发言者标识五花八门,还有一堆干扰项(比如全大写的专业术语),之前的正则要么漏识别要么错把普通句子当发言者,我太懂这种挫败感了。下面结合你的场景,给你一套上下文规则+正则增强+后处理过滤的组合方案,能大幅降低误识别率,再配合手动修正的辅助工具,基本能搞定这类文本。
核心问题拆解
先把你遇到的痛点拆透,才能精准解决:
- 发言者格式无统一标准:全大写职位(
THE PRESIDENT)、头衔+姓名+国家(Mr. KAZI (Pakistan))、纯国家名、大小写混合的翻译姓名 - 干扰项密集:全大写专业术语(比如
DDT)、句子中间的冒号、PDF扫描带来的换行/连字符噪声 - 文本来自历史会议记录,格式严谨性差,没有固定的发言者分隔规则
优化后的R实现方案
这个方案分预处理降噪→增强正则识别→后处理过滤→手动修正辅助四个阶段,比单纯靠正则或冒号匹配靠谱得多。
1. 第一步:预处理PDF文本,清理格式噪声
先把PDF里的跨行连字符、多余换行、重复空格全部清理干净,这是后续识别的基础:
library(httr) library(pdftools) library(stringr) library(dplyr) library(tibble) library(purrr) # 读取目标PDF文本 year <- "1949" start_page <- 79 end_page <- 147 url <- "https://iris.who.int/bitstreams/b7fccdfc-ac0a-4421-a693-509d77a3988b/download" tmp_pdf <- tempfile(fileext = ".pdf") GET(url, write_disk(tmp_pdf, overwrite = TRUE), user_agent("R")) txt_raw <- pdf_text(tmp_pdf)[start_page:end_page] # 核心预处理:清理格式噪声 full_text <- txt_raw %>% # 修复跨行连字符(比如"medi-\ncine"转为"medicine") str_replace_all("-\n", "") %>% # 把所有换行符替换为空格,避免跨行截断发言 str_replace_all("\n", " ") %>% # 压缩多个连续空格为单个 str_squish()
2. 第二步:增强版正则,结合上下文约束
之前的正则太宽泛,这次我们给正则加上下文规则(比如发言者前必须是句子结束标记或文本开头),同时覆盖所有可能的发言者格式:
# 增强版发言者正则:匹配所有格式+上下文约束 speaker_pattern <- str_c( # 上下文约束:发言者前是句子结束标记/会议标题/文本开头 "(?:^|\\.\\s|\\;\\s|VERBATIM RECORDS|SESSION|DEBATE)\\s*", # 匹配3类常见发言者格式 "(", # 格式1:全大写职位(比如THE PRESIDENT、ACTING PRESIDENT) "(?:\\b[A-Z\\s]{3,}\\b(?=\\s:))", "|", # 格式2:头衔+姓名+国家(支持大小写混合,比如Mr. Pozzo (translated from Italian)) "(?:(Dr|Mr|Mrs|Ms|Prof|Professor)\\s*[A-Z][a-z]*(?:\\s[A-Z][a-z]*)*\\s*\\([^)]+\\))", "|", # 格式3:纯国家/地区名(比如PAKISTAN) "(?:\\b[A-Z]{2,}\\s\\(.*?\\)|\\b[A-Z]{2,}\\b(?=\\s:))", ")", # 必须跟冒号(发言者的核心标识) "\\s*:" )
3. 第三步:标记分割发言轮次+过滤误识别
用哨兵标记发言者起始位置,分割后再通过长度、关键词规则过滤明显的误识别条目:
# 用哨兵标记发言者起始点 sentinel <- "<<<SPEAKER_START>>>" marked_text <- str_replace_all(full_text, speaker_pattern, paste0(sentinel, "\\0")) # 分割为独立发言轮次,清理空条目 raw_turns <- str_split(marked_text, sentinel)[[1]] %>% str_trim() %>% discard(~ .x == "") # 转换为数据框,提取发言者和文本 speaker_df <- tibble(raw = raw_turns) %>% mutate( # 提取发言者(从开头到第一个冒号前的内容) speaker = str_extract(raw, "^.*?(?=\\s:)"), # 提取发言文本(去掉发言者+冒号的部分) text = str_remove(raw, "^.*?:\\s*") %>% str_squish(), year = as.integer(year) ) %>% # 核心过滤:排除误识别条目 filter( # 发言者长度不能太短(排除DDT这类3字符的专业术语) str_length(speaker) > 5, # 排除常见的非发言者关键词(可根据会议内容扩展) !str_detect(speaker, "\\bDDT\\b|\\bMALARIA\\b|\\bMEDICINE\\b"), # 发言文本不能为空 text != "" ) %>% # 清理发言者的多余空格 mutate(speaker = str_squish(speaker)) %>% select(year, speaker, text)
4. 第四步:手动修正的辅助工具
没有100%完美的自动识别,最后用这个工具快速定位可疑条目,导出到CSV手动修正:
# 定位可疑条目:比如发言者过短、发言文本过短 suspect_entries <- speaker_df %>% filter( str_length(speaker) < 8 | str_length(text) < 50 ) # 导出到CSV手动修正 write_csv(suspect_entries, "suspect_speaker_turns_1949.csv") # 修正后读回,合并到最终数据框 corrected_entries <- read_csv("suspect_speaker_turns_1949_corrected.csv") speaker_df_final <- speaker_df %>% anti_join(suspect_entries, by = c("year", "speaker", "text")) %>% bind_rows(corrected_entries)
针对会议逐字记录的发言者识别最佳实践
- 预处理优先:PDF扫描的文本必须先清理换行、连字符、多余空格,不然正则会被格式噪声彻底干扰
- 正则要结合上下文:不要只用内容匹配,一定要加上下文约束(比如发言者前必须是句子结束标记),能大幅降低误识别
- 后处理过滤是关键:没有完美的正则,一定要用长度、关键词等规则过滤明显的错误条目
- 分阶段验证:先拿10页以内的文本测试正则,调整到准确率达标后再跑全量,避免白忙活
- 手动修正轻量化:把可疑条目导出到CSV用Excel修改,比在R里硬改效率高10倍
这套方案跑你提供的1949年会议文本,能正确识别THE PRESIDENT、Mr. KAZI (Pakistan)、Acting President等所有格式,还能排除像the tide of ratifications began to turn这类误识别。如果遇到新的发言者格式,只需要在正则的格式分支里新增规则,慢慢迭代就能接近完美。




