问题来自头条文章:

他们总说,R 语言处理文本数据、自动化办公不行,是真不行吗?本篇就来个 吊打 Python 看看。


1 问题描述

  • 有多个 word 文档:
  • 文档内容:
  • 需求:

批量读取 word 文档内容,按名目:序号、方名、组成、用法、主治、备注,整理成 Excel 表格,结果如下:

注:若未包含【组成】【用法】【主治】,则内容都归入备注。

2 解决问题

首先强调:文本数据处理,肯定离不开正则表达式!

  • 加载包

library(readtext) # 读取文本数据 library(tidyverse) # stringr, tidyr包, 处理文本数据 library(writexl) # 写出到Excel

  • 批量读取 word 文档

df = readtext("word") # 注意,word文档不能在打开状态 df

得到的结果是数据框,每个文档占一行,doc_id 列是文件名,text 列是文档内容,一个文档内容整个是一个字符串。

  • 按药方切分列

df = df %>% separate_rows(text, sep = "n(?=\d{5})") # 切分标志是: n五位数字序号 df

此时,一个药方占一行了。

  • 解决一个药方的信息提取,写成函数

f = function(x) { tibble( 序号 = str_extract(x, "^\d{5}"), 方名 = str_extract(x, "(?<=\d·).*?(?=n)"), 组成 = str_extract(x, "(?<=【组成】).*?(?=n【用法】|$)"), 用法 = str_extract(x, "(?<=【用法】).*?(?=n【主治】|$)"), 主治 = str_extract(x, "(?<=【主治】).*?(?=n|$)"), 备注 = ifelse(str_detect(x, "【"), NA, str_extract(x, "(?<=n).*#34;))) }

说明:都是正则表达式提取,主要用到零宽断言,根据两端标志提取中间想要的内容。为什么看着这么啰嗦呢?是因为数据是有陷阱的:不是每个药方都包含【组成】、【用法】、【主治】,所以右端需要设置为下一项或结尾标志$。

测试一个看看:

f(df$text[[1]])

没问题!(我差点被对不齐给骗了)

  • 循环迭代,批量解决问题

就是把函数 f 依次应用到数据的 text列,结果按行合并:

rlt = map_dfr(df$text, f) rlt

  • 写出到 Excel 文件

write_xlsx(rlt, "结果表.xlsx")

结果就是问题描述中的结果表(略)。

问题解决!

以上是为了给大家展示中间过程,方便大家理解。拿掉不必要的中间过程,借助管道,完整代码如下:

library(readtext) # 读取文本数据 library(tidyverse) # stringr, tidyr包, 处理文本数据 library(writexl) # 写出到Excel df = readtext("word") %>% separate_rows(text, sep = "n(?=\d{5})") f = function(x) { tibble( 序号 = str_extract(x, "^\d{5}"), 方名 = str_extract(x, "(?<=\d·).*?(?=n)"), 组成 = str_extract(x, "(?<=【组成】).*?(?=n【用法】|$)"), 用法 = str_extract(x, "(?<=【用法】).*?(?=n【主治】|$)"), 主治 = str_extract(x, "(?<=【主治】).*?(?=n|$)"), 备注 = ifelse(str_detect(x, "【"), NA, str_extract(x, "(?<=n).*#34;))) } map_dfr(df$text, f) %>% write_xlsx("结果表.xlsx")