如何用 Python 和正则表达式抽取文本结构化信息?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何用 Python 和正则表达式抽取文本结构化信息?相关的知识,希望对你有一定的参考价值。

痛点很多人的日常工作,都是要和大量的文本打交道的。例如学者需要阅读大量的文献材料,从中找到灵感、数据与论据。学生需要阅读很多教科书

参考技术A

痛点

很多人的日常工作,都是要和大量的文本打交道的。

例如学者需要阅读大量的文献材料,从中找到灵感、数据与论据。

学生需要阅读很多教科书和论文,然后写自己的报告或者做幻灯。

财经分析师,需要从大量的新闻报道中,找到行业的发展趋势和目标企业动态的蛛丝马迹。

不是所有的文本处理,都那么新鲜而有趣。

有一项重要但繁琐的工作,就是从大量的文本当中抽取结构化的信息。

许多数据分析的场景,都要求输入结构化的信息。

例如在咱们之前介绍过的《贷还是不贷:如何用 Python 和机器学习帮你决策?》和《如何用 Python 和深度神经网络锁定即将流失的客户?》中,你都看到了,机器模型更喜欢被结构化的表格信息来喂养。

然而,结构化的信息,不一定就在那里,静候你来使用。很多时候,它蕴藏在以往生成的非结构化文本中。

你可能早已习惯,人工阅读文本信息,把关键点摘取出来,然后把它们拷贝粘贴放到一个表格中。从原理上讲,这样做无可厚非。但是实际操作里面,效率太低,而且太麻烦。

大部分人,是不愿意从事这种简单重复的枯燥工作的。

一遍遍机械重复鼠标划定文本范围,“Ctrl+C”、切换到表格文档、找准输入位置,再 “Ctrl+V”……

这种事儿做得太多,对你的肩肘关节,甚至是身心健康,都有可能造成不利影响。

想不想尝试用一种更简单的自动化方式,替你快速完成这些烦人的操作步骤呢??

读过本文后,希望你能找到答案。

样例

这里,我们举一个极端简化的中文文本抽取信息例子。

之所以这样做,是为了避免你在解读数据上花费太多时间。

我更希望,你能够聚焦于方法,从而掌握新知。

假设一个高中班主任,高考后让班长统计一下学生们的毕业去向。班长很认真地进行了调查,然后做了如下汇报:

张华考上了北京大学
李萍进了中等技术学校
韩梅梅进了百货公司
……

为了让你对样例足够熟悉,甚至有共鸣,这里我从 1998 年版的新华字典中,“借鉴” 了部分内容。

够贴心吧?

现实生活中,一个班大概不会只有 3 个人,因此你可以想象这是一个长长的句子列表。

但其实班主任有个隐含的意思没有表达出来,即:

我想要一张表格!

所以,看到这一长串的句子,你可以想象他的表情。

班长估计也很难堪:

想要表格你早说啊!

这时候,假设你是班长,怎么办?

信息都在文本里面。但如果需要转换成表格,就得一个个信息点去寻找和处理。

其实,对于四五十人的班级来说,手动操作也不是什么太难的事情。

但是设想一下,如果你需要处理的数据量,是这个例子的十倍、百倍甚至千万倍呢?

继续坚持手动处理?

这不仅麻烦,而且不现实。

我们需要找到一种简单的方法,帮助我们自动抽取相应的信息。

此处我们使用的方法,是正则表达式

正则

“正则表达式” 这个名字,初听起来好像很玄妙。实际上,它是从英文 “regular expression” 翻译过来的。

如果译成白话,那就是 “有规律的表述形式”。

这,听起来,是不是就更加接地气了?

但是,给你补一下 “假行家 101” 课程:

说别人听得懂的话,你能唬得住谁?

约定俗成,咱们继续沿用 “正则表达式”,来称呼它好了。

从创生之日起,它就给文本处理带来了高效率。

但是,用它的主要人群,却不是时常跟文字打交道的作家、编辑、学者、文员,而是……

程序员

程序员写的代码,是文本;程序员处理的数据,很多也是文本格式。其中便有很多显著的规律可循。

正是靠着正则表达式这种独门秘籍,许多别人做起来,需要昏天黑地一整周的任务,程序员可以半小时搞定,然后喝着咖啡等下班。

即便到了泛人工智能的今天,正则表达式依然有许多令你意想不到的应用。

例如人机对话系统。

你可能看了新闻报道,总以为人机对话都是靠着知识图谱或者深度学习搞出来的。

不能说这里面,没有上述炫酷技术的参与。但它们充其量,只占其中一部分,或许还只是一小部分。

生产实践里面,大量的对话规则后面,并不是让你倍感神奇深奥的神经网络,而是一堆正则表达式。

你可能会担心,这样高端的应用技术,自己能掌握吗?

答案是:

当然!

正则表达式,并不难学。

尤其是当你把它和 Python 结合到一起,那简直就是效率神器了。

我们这就来看看,正则表达式怎么帮我们识别出样例文本里面 “人名” 和 “去向” 信息。

试练

请你开启一个浏览器,键入这个网址(https://regex101.com/)。

你会看见如下界面。

它可是一个正则表达式实验的利器。我教 INFO 5731 课程时,学生们就是在掌握了这个工具以后,迅速玩儿转了正则表达式。

这么好的工具,一定要价不菲吧?

不,它是免费的。你放心大胆使用就好了。

我们首先把左侧的编程语言,从默认的 php ,调整为 Python。

之后,把需要进行处理的文本,贴到中间空白的大文本框里面。

下面我们来尝试进行 “匹配”。

什么叫做匹配呢?

就是你写一个表达式,电脑便拿着鸡毛当令箭,在每一行文本上,都认认真真地找有没有符合该表达式的文本段落。

如有,则会高亮显示出来。

这里我们观察一下,发现每个句子里面,人员去向前面,都有一个 “了” 字。

好,我们就在中部上方小文本框里,把 “了” 字输入进去。

可以看到,三句话里面的 “了”,全都亮了

这就是你接触到的第一种匹配方式 —— 按照字符原本的意思来查找一致的内容。

因为样例文本的规律性,我们可以把 “了” 当成一个定位符,它后面,到句子结束位置,是 “去向” 信息。

咱们需要找的一半结构化信息,不就是这个 “去向” 吗?

我们尝试匹配 “去向”。

怎么匹配呢?这次每一行的字儿都不一样啊?

没关系,正则表达式强大之处,此时就显示出来了。

你可以用一个点号,也就是.,表示任意字符。

字母、数字、标点…… 甚至是中文,也能涵盖在内。

然后我们继续想想看,去向信息这里,会有几个字呢?

不好说。

例子里面这简单的三句话,就有 “4 个字” 或者 “6 个字” 两种情况。

所以,我们无法指定去向信息里面字符的长度。

但这也没关系,我们只需要用一个星号(*),就可以代表出现次数,从 0 到无穷大都可以匹配。

当然,实际情况中,是不会真出现无穷大的。

我们在刚才输入的基础上,加上.*,结果就成了这个样子:

不错嘛!

不过似乎去向信息和 “了” 字儿都是一样颜色的高亮。那不就混到了一起吗?

我们可不想这样。

怎么办?

请你在.* 的两侧,尝试加入一对小括号(注意,不要用中文全角符号)试试看。

你会发现,这次 “了” 依然用蓝色表示,而后面的去向信息,已经变成了绿色。

这一对小括号,很重要,它叫做 “分组”,是提取信息的基本单位。

我们的任务已经解决了一半了,是吧?

下面我们来尝试把人名一并抽取出来。

我们来找人名的锚定位置。

细细观察,你不难发现,每个人名的后面,都有个动词跟着。

升学的同学,用的是 “考” 字,而就业的同学,用的是 “进” 字。

我们先尝试一下 “考” 字。

这里我们尝试直接把 “考” 字放在 “了” 字以前。但是你会发现,什么匹配结果也没有。

为什么?

回看数据,你会发现,人家用的原词是 “考上了”。

当然这里我们可以输入 “上” 字。不过你要考虑一下更为通用的情况。

好比说,“考取了” 怎么办?“考入了” 呢?

更好的方式,是继续使用我们刚才学会的 “大招”,在 “考” 和 “了” 之间,插入一个.*。

这时候,你的正则表达式的样子是 考.*了(.*)

看,第一行的信息成功匹配了吧?

但是,那后面还有两行没有匹配,怎么办?

我们依样画葫芦,就会发现,使用进.*了(.*) 就能正确匹配后两行。

问题来了:

匹配第一行的,匹配不了后两行,反之亦然。

这不好。我们希望写的表达式,能够更通用

怎么办?

我们看看正则表达式当中 “或” 关系的表示。

这里,我们可以把两个字符用竖线隔开,旁边用中括号括起来,代表两者任一出现,都算匹配成功。

也就是,把正则表达式,写成这样:[考|进].*了(.*)

太棒了,三行的内容都已经匹配成功。

这里,动词词组,和代表时态的 “了” 作为中间锚定信息,我们可以放心大胆,把之前的人名信息,提取出来了。

也就是这样写:(.*)[考|进].*了(.*)

注意此时,人名分组是绿色,去向分组是红色的。

我们成功提取了两组信息!庆祝一下!

可是,如果你给班主任看这里的结果,估计他不会满意。

表格,我要表格!

别着急,该 Python 出场了。

下面我们尝试在 Python 把数据正式提取出来。

环境

本文的配套源代码,我放在了 Github 上。

你可以在我的公众号 “玉树芝兰”(nkwangshuyi)后台回复 “regex”,查看完整的代码链接。

如果你对我的教程满意,欢迎在页面右上方的 Star 上点击一下,帮我加一颗星。谢谢!

注意这个页面的中央,有个按钮,写着 “在 Colab 打开”(Open in Colab)。请你点击它。

然后,Google Colab 就会自动开启。

我建议你点一下上图中红色圈出的 “COPY TO DRIVE” 按钮。这样就可以先把它在你自己的 Google Drive 中存好,以便使用和回顾。

Colab 为你提供了全套的运行环境。你只需要依次执行代码,就可以复现本教程的运行结果了。

如果你对 Google Colab 不熟悉,没关系。我这里有一篇教程,专门讲解 Google Colab 的特点与使用方式。

为了你能够更为深入地学习与了解代码,我建议你在 Google Colab 中开启一个全新的 Notebook ,并且根据下文,依次输入代码并运行。在此过程中,充分理解代码的含义。

这种看似笨拙的方式,其实是学习的有效路径。

代码

首先,读入 Python 正则表达式包。

import re

然后,我们把数据准备好。注意为了演示代码的通用性,我这里在最后加了一行文字,区别于之前的文字规律,看看我们的代码能否正确处理它。

data = """张华考上了北京大学
李萍进了中等技术学校
韩梅梅进了百货公司
他们都有光明的前途"""

然后,该写正则表达式了。你真的需要自己手动来写吗?

当然不必。

强大的 regex101 网站,已经帮助我们准备好了。

请你点击上图中红色圈出的按钮,网站会为你准备好一个初始代码的模板,可以匹配你需要的模式。

你不需要完全照搬代码。其中有这样一句,是很重要的,拷贝过来,贴到 Colab Notebook 就好。

regex = r"(.*)[考|进].*了(.*)"

以上就是你的正则表达式,在 Python 里面应有的样子。

我们准备一个空列表,用来接收数据。

mylist = []

接着,写一个循环。

for line in data.split('n'):
 mysearch = re.search(regex, line)
 if mysearch:
   name = mysearch.group(1)
   dest = mysearch.group(2)
   mylist.append((name, dest))

我给你解释一下这个循环里面,各条语句的含义:

data.split('n') 把文本数据按行来拆分开。这样我们就可以针对每一行,来获取数据。mysearch = re.search(regex, line) 这一句尝试匹配模式到该行内容。if mysearch 这个判断语句,是让程序分辨一下,该行是否有我们要找的模式。例如最后一行文字,里面并没有咱们前面分析的文字模式。遇到这样的行,直接跳过。name = mysearch.group(1) 是说匹配的第一组内容,也就是 regex101 网站里绿色代表的人名分组存到 name 变量里。下一句依次类推。注意 group 对应你正则表达式里面小括号出现的顺序,从 1 开始计数。mylist.append((name, dest)) 把该行抽取到的信息,存入到咱们之前定义的空列表里面。

注意,如果不加 mysearch = re.search(regex, line) 这一句,程序会对每一行都尝试匹配并且抽取分组内容,那么结果就会报这样的错误:

所以你看,用正则表达式抽取信息时,不能蛮干。

此时,我们查看一下 mylist 这个列表里面的内容:

mylist

结果为:

[('张华', '北京大学'), ('李萍', '中等技术学校'), ('韩梅梅', '百货公司')]

不错,一个不多,一个不少,恰好是我们需要的。

我们要把它导出成为表格。方法有很多,但是最简便顺手的,是用 Pandas 数据分析软件包。

import pandas as pd

只需要利用 pd.DataFrame 函数,我们就能把上面列表和元组(tuple)组成的一个二维结构,变成数据框。

df = pd.DataFrame(mylist)
df.columns = ['姓名', '去向']

注意,这里我们还非常细心地修改了表头。

看看你的劳动成果吧:

df

有了数据框,转换成为 Excel ,就是一行代码的事情了:

df.to_excel("dest.xlsx", index=False)

进入 Files 标签页,刷新并且查看一下当前目录下的内容:

这个 dest.xlsx 就是输出的结果了。下载之后我们可以用 Excel 打开查看。

任务完成!

你可以把结果提交给班主任,看他满意的笑容了。

小结

这篇教程里面,咱们谈了如何利用文本字符规律,借助 Python 和正则表达式,来提取结构化信息。

希望你已经掌握了以下本领:

    了解正则表达式的功用;

    用 regex101 网站尝试正则表达式匹配,并且生成初步的代码;

    用 Python 批量提取信息,并且根据需求导出结构化数据为指定格式。

再次强调一下,对于这么简单的样例,使用上述方法,绝对是大炮轰蚊子。

然而,如果你需要处理的数据是海量的,这个方法给你节省下来的时间,会非常可观。

希望你能够举一反三,在自己的工作中灵活运用它。

更多Python知识,请关注:Python自学网!!

如何用Python从海量文本抽取主题?

摘自https://www.jianshu.com/p/fdde9fc03f94

 

你在工作、学习中是否曾因信息过载叫苦不迭?有一种方法能够替你读海量文章,并将不同的主题和对应的关键词抽取出来,让你谈笑间观其大略。本文使用Python对超过1000条文本做主题抽取,一步步带你体会非监督机器学习LDA方法的魅力。想不想试试呢?

 
技术图片
 

淹没

每个现代人,几乎都体会过信息过载的痛苦。文章读不过来,音乐听不过来,视频看不过来。可是现实的压力,使你又不能轻易放弃掉。

假如你是个研究生,教科书和论文就是你不得不读的内容。现在有了各种其他的阅读渠道,微信、微博、得到App、多看阅读、豆瓣阅读、Kindle,还有你在RSS上订阅的一大堆博客……情况就变得更严重了。

因为对数据科学很感兴趣,你订阅了大量的数据科学类微信公众号。虽然你很勤奋,但你知道自己依然遗漏了很多文章。

学习了 Python爬虫课 以后,你决定尝试一下自己的屠龙之术。依仗着爬虫的威力,你打算采集到所有数据科学公众号文章。

你仔细分析了微信公众号文章的检索方式,制定了关键词列表。巧妙利用搜狗搜索引擎的特性,你编写了自己的爬虫,并且成功地于午夜放到了云端运行。

开心啊,激动啊……

第二天一早,天光刚亮,睡眠不足的你就兴冲冲地爬起来去看爬取结果。居然已经有了1000多条!你欣喜若狂,导出成为csv格式,存储到了本地机器,并且打开浏览。

 
技术图片
 

兴奋了10几分钟之后,你冷却了下来,给自己提出了2个重要的问题。

  • 这些文章都值得读吗?
  • 这些文章我读得过来吗?

一篇数据科学类公众号,你平均需要5分钟阅读。这1000多篇……你拿出计算器认真算了一下。

 
技术图片
 

读完这一宿采集到的文章,你不眠不休的话,也需要85个小时。

在你阅读的这85个小时里面,许许多多的数据科学类公众号新文章还会源源不断涌现出来。

你感觉自己快被文本内容淹没了,根本透不过气……

学了这么长时间Python,你应该想到——我能否用自动化工具来分析它?

好消息,答案是可以的。

但是用什么样的工具呢?

翻了翻你自己的武器库,你发现了词云情感分析决策树

然而,在帮你应对信息过载这件事儿上,上述武器好像都不大合适。

词云你打算做几个?全部文章只做一个的话,就会把所有文章的内容混杂起来,没有意义——因为你知道这些文章谈的就是数据科学啊!如果每一篇文章都分别做词云,1000多张图浏览起来,好像也没有什么益处。

你阅读数据科学类公众号文章是为了获得知识和技能,分析文字中蕴含的情感似乎于事无补。

决策树是可以用来做分类的,没错。可是它要求的输入信息是结构化标记数据,你手里握着的这一大堆文本,却刚好是非结构化标记数据。

全部武器都哑火了。

没关系。本文帮助你在数据科学武器库中放上一件新式兵器。它能够处理的,就是大批量的非结构无标记数据。在机器学习的分类里,它属于非监督学习(unsupervised machine learning)范畴。具体而言,我们需要用到的方法叫主题建模(topic model)或者主题抽取(topic extraction)。

主题

既然要建模,我们就需要弄明白建立什么样的模型。

根据维基百科的定义,主题模型是指:

在机器学习和自然语言处理等领域是用来在一系列文档中发现抽象主题的一种统计模型。

这个定义本身好像就有点儿抽象,咱们举个例子吧。

还是维基百科上,对一条可爱的小狗有这样一段叙述。

阿博(Bo;2008年10月9日-) 是美国第44任总统巴拉克·奥巴马的宠物狗,也是奥巴马家族的成员之一。阿博是一只已阉割的雄性黑色长毛葡萄牙水犬。奥巴马一家本来没有养狗,因为他的大女儿玛丽亚对狗过敏。但为了延续白宫主人历年均有养狗的传统,第一家庭在入主白宫后,花了多个月去观察各种犬种,并特地选择了葡萄牙水犬这一种掉毛少的低敏狗。

我们来看看这条可爱的小狗照片:

 
技术图片
 

问题来了,这篇文章的主题(topic)是什么?

你可能脱口而出,“狗啊!”

且慢,换个问法。假设一个用户读了这篇文章,很感兴趣。你想推荐更多他可能感兴趣的文章给他,以下2段文字,哪个选项更合适呢?

选项1:

阿富汗猎狗(Afghan Hound)是一种猎犬,也是最古老的狗品种。阿富汗猎狗外表厚实,细腻,柔滑,它的尾巴在最后一环卷曲。阿富汗猎狗生存于伊朗,阿富汗东部的寒冷山上,阿富汗猎狗最初是用来狩猎野兔和瞪羚。阿富汗猎狗其他名称包含巴尔赫塔子库奇猎犬,猎犬,俾路支猎犬,喀布尔猎犬,或非洲猎犬。

选项2:

1989年夏天,奥巴马在西德利·奥斯汀律师事务所担任暑期工读生期间,结识当时已是律师的米歇尔·鲁滨逊。两人于1992年结婚,现有两个女儿——大女儿玛丽亚在1999年于芝加哥芝加哥大学医疗中心出生,而小女儿萨沙在2001年于芝加哥大学医疗中心出生。

给你30秒,思考一下。

你的答案是什么?

我的答案是——不确定。

人类天生喜欢把复杂问题简单化。我们恨不得把所有东西划分成具体的、互不干扰的分类,就如同药铺的一个个抽屉一样。然后需要的时候,从对应的抽屉里面取东西就可以了。

 
技术图片
 

这就像是职业。从前我们说“三百六十行”。随便拿出某个人来,我们就把他归入其中某一行。

现在不行了,反例就是所谓的“斜杠青年”。

主题这个事情,也同样不那么泾渭分明。介绍小狗Bo的文章虽然不长,但是任何单一主题都无法完全涵盖它。

如果用户是因为对小狗的喜爱,阅读了这篇文章,那么显然你给他推荐选项1会更理想;但是如果用户关注的是奥巴马的家庭,那么比起选项2来,选项1就显得不是那么合适了。

我们必须放弃用一个词来描述主题的尝试,转而用一系列关键词来刻画某个主题(例如“奥巴马”+“宠物“+”狗“+”第一家庭“)。

在这种模式下,以下的选项3可能会脱颖而出:

据英国《每日邮报》报道,美国一名男子近日试图绑架总统奥巴马夫妇的宠物狗博(Bo),不惜由二千多公里远的北达科他州驱车往华盛顿,但因为走漏风声,被特勤局人员逮捕。奥巴马夫妇目前养有博和阳光(Sunny)两只葡萄牙水犬。

讲到这里,你大概弄明白了主题抽取的目标了。可是面对浩如烟海的文章,我们怎么能够把相似的文章聚合起来,并且提取描述聚合后主题的重要关键词呢?

主题抽取有若干方法。目前最为流行的叫做隐含狄利克雷分布(Latent Dirichlet allocation),简称LDA。

LDA相关原理部分,置于本文最后。下面我们先用Python来尝试实践一次主题抽取。如果你对原理感兴趣,不妨再做延伸阅读。

准备

准备工作的第一步,还是先安装Anaconda套装。详细的流程步骤请参考《 如何用Python做词云 》一文。

从微信公众平台爬来的datascience.csv文件,请从 这里 下载。你可以用Excel打开,看看下载是否完整和正确。

 
技术图片
 

如果一切正常,请将该csv文件移动到咱们的工作目录demo下。

到你的系统“终端”(macOS, Linux)或者“命令提示符”(Windows)下,进入我们的工作目录demo,执行以下命令。

pip install jieba
pip install pyldavis

运行环境配置完毕。

在终端或者命令提示符下键入:

jupyter notebook
 
技术图片
 

Jupyter Notebook已经正确运行。下面我们就可以正式编写代码了。

代码

我们在Jupyter Notebook中新建一个Python 2笔记本,起名为topic-model。

 
技术图片
 

为了处理表格数据,我们依然使用数据框工具Pandas。先调用它。

import pandas as pd

然后读入我们的数据文件datascience.csv,注意它的编码是中文GB18030,不是Pandas默认设置的编码,所以此处需要显式指定编码类型,以免出现乱码错误。

df = pd.read_csv("datascience.csv", encoding=‘gb18030‘)

我们来看看数据框的头几行,以确认读取是否正确。

df.head()

显示结果如下:

 
技术图片
 

没问题,头几行内容所有列都正确读入,文字显式正常。我们看看数据框的长度,以确认数据是否读取完整。

df.shape

执行的结果为:

(1024, 3)

行列数都与我们爬取到的数量一致,通过。

下面我们需要做一件重要工作——分词。这是因为我们需要提取每篇文章的关键词。而中文本身并不使用空格在单词间划分。此处我们采用“结巴分词”工具。这一工具的具体介绍和其他用途请参见《如何用Python做中文分词?》一文。

我们首先调用jieba分词包。

import jieba

我们此次需要处理的,不是单一文本数据,而是1000多条文本数据,因此我们需要把这项工作并行化。这就需要首先编写一个函数,处理单一文本的分词。

def chinese_word_cut(mytext):
    return " ".join(jieba.cut(mytext))

有了这个函数之后,我们就可以不断调用它来批量处理数据框里面的全部文本(正文)信息了。你当然可以自己写个循环来做这项工作。但这里我们使用更为高效的apply函数。如果你对这个函数有兴趣,可以点击这段教学视频查看具体的介绍。

下面这一段代码执行起来,可能需要一小段时间。请耐心等候。

df["content_cutted"] = df.content.apply(chinese_word_cut)

执行过程中可能会出现如下提示。没关系,忽略就好。

Building prefix dict from the default dictionary ...
Loading model from cache /var/folders/8s/k8yr4zy52q1dh107gjx280mw0000gn/T/jieba.cache
Loading model cost 0.406 seconds.
Prefix dict has been built succesfully.

执行完毕之后,我们需要查看一下,文本是否已经被正确分词。

df.content_cutted.head()

结果如下:

0    大 数据 产业 发展 受到 国家 重视 , 而 大 数据 已经 上升 为 国家 战略 , 未...
1    点击 上方 “ 硅谷 周边 ” 关注 我 , 收到 最新 的 文章 哦 ! 昨天 , Goo...
2    国务院 总理 李克强 当地 时间 20 日 上午 在 纽约 下榻 饭店 同 美国 经济 、 ...
3    2016 年 , 全峰 集团 持续 挖掘 大 数据 、 云 计算 、 “ 互联网 + ” 等...
4    贵州 理工学院 召开 大 数据分析 与 应用 专题 分享 会   借 “ 创响 中国 ” 贵...
Name: content_cutted, dtype: object

单词之间都已经被空格区分开了。下面我们需要做一项重要工作,叫做文本的向量化。

不要被这个名称吓跑。它的意思其实很简单。因为计算机不但不认识中文,甚至连英文也不认识,它只认得数字。我们需要做的,是把文章中的关键词转换为一个个特征(列),然后对每一篇文章数关键词出现个数。

假如这里有两句话:

I love the game.
I hate the game.

那么我们就可以抽取出以下特征:

  • I
  • love
  • hate
  • the
  • game

然后上面两句话就转换为以下表格:

 
技术图片
 

第一句表示为[1, 1, 0, 1, 1],第二句是[1, 0, 1, 1, 1]。这就叫向量化了。机器就能看懂它们了。

原理弄清楚了,让我们引入相关软件包吧。

from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer

处理的文本都是微信公众号文章,里面可能会有大量的词汇。我们不希望处理所有词汇。因为一来处理时间太长,二来那些很不常用的词汇对我们的主题抽取意义不大。所以这里做了个限定,只从文本中提取1000个最重要的特征关键词,然后停止。

n_features = 1000

下面我们开始关键词提取和向量转换过程:

tf_vectorizer = CountVectorizer(strip_accents = ‘unicode‘,
                                max_features=n_features,
                                stop_words=‘english‘,
                                max_df = 0.5,
                                min_df = 10)
tf = tf_vectorizer.fit_transform(df.content_cutted)

到这里,似乎什么都没有发生。因为我们没有要求程序做任何输出。下面我们就要放出LDA这个大招了。

先引入软件包:

from sklearn.decomposition import LatentDirichletAllocation

然后我们需要人为设定主题的数量。这个要求让很多人大跌眼镜——我怎么知道这一堆文章里面多少主题?!

别着急。应用LDA方法,指定(或者叫瞎猜)主题个数是必须的。如果你只需要把文章粗略划分成几个大类,就可以把数字设定小一些;相反,如果你希望能够识别出非常细分的主题,就增大主题个数。

对划分的结果,如果你觉得不够满意,可以通过继续迭代,调整主题数量来优化。

这里我们先设定为5个分类试试。

n_topics = 5
lda = LatentDirichletAllocation(n_topics=n_topics, max_iter=50,
                                learning_method=‘online‘,
                                learning_offset=50.,
                                random_state=0)

把我们的1000多篇向量化后的文章扔给LDA,让它欢快地找主题吧。

这一部分工作量较大,程序会执行一段时间,Jupyter Notebook在执行中可能暂时没有响应。等待一会儿就好,不要着急。

lda.fit(tf)

程序终于跑完了的时候,你会看到如下的提示信息:

LatentDirichletAllocation(batch_size=128, doc_topic_prior=None,
             evaluate_every=-1, learning_decay=0.7,
             learning_method=‘online‘, learning_offset=50.0,
             max_doc_update_iter=100, max_iter=50, mean_change_tol=0.001,
             n_jobs=1, n_topics=5, perp_tol=0.1, random_state=0,
             topic_word_prior=None, total_samples=1000000.0, verbose=0)

可是,这还是什么输出都没有啊。它究竟找了什么样的主题?

主题没有一个确定的名称,而是用一系列关键词刻画的。我们定义以下的函数,把每个主题里面的前若干个关键词显示出来:

def print_top_words(model, feature_names, n_top_words):
    for topic_idx, topic in enumerate(model.components_):
        print("Topic #%d:" % topic_idx)
        print(" ".join([feature_names[i]
                        for i in topic.argsort()[:-n_top_words - 1:-1]]))
    print()

定义好函数之后,我们暂定每个主题输出前20个关键词。

n_top_words = 20

以下命令会帮助我们依次输出每个主题的关键词表:

tf_feature_names = tf_vectorizer.get_feature_names()
print_top_words(lda, tf_feature_names, n_top_words)

执行效果如下:

Topic #0:
学习 模型 使用 算法 方法 机器 可视化 神经网络 特征 处理 计算 系统 不同 数据库 训练 分类 基于 工具 一种 深度
Topic #1:
这个 就是 可能 如果 他们 没有 自己 很多 什么 不是 但是 这样 因为 一些 时候 现在 用户 所以 非常 已经
Topic #2:
企业 平台 服务 管理 互联网 公司 行业 数据分析 业务 用户 产品 金融 创新 客户 实现 系统 能力 产业 工作 价值
Topic #3:
中国 2016 电子 增长 10 市场 城市 2015 关注 人口 检索 30 或者 其中 阅读 应当 美国 全国 同比 20
Topic #4:
人工智能 学习 领域 智能 机器人 机器 人类 公司 深度 研究 未来 识别 已经 医疗 系统 计算机 目前 语音 百度 方面
()

在这5个主题里,可以看出主题0主要关注的是数据科学中的算法和技术,而主题4显然更注重数据科学的应用场景。

剩下的几个主题可以如何归纳?作为思考题,留给你花时间想一想吧。

到这里,LDA已经成功帮我们完成了主题抽取。但是我知道你不是很满意,因为结果不够直观。

那咱们就让它直观一些好了。

执行以下命令,会有有趣的事情发生。

import pyLDAvis
import pyLDAvis.sklearn
pyLDAvis.enable_notebook()
pyLDAvis.sklearn.prepare(lda, tf, tf_vectorizer)

对,你会看到如下的一张图,而且还是可交互的动态图哦。

 
技术图片
 

需要说明的是,由于pyLDAvis这个包兼容性有些问题。因此在某些操作系统和软件环境下,你执行了刚刚的语句后,没有报错,却也没有图形显示出来。

没关系。这时候请你写下以下语句并执行:

data = pyLDAvis.sklearn.prepare(lda, tf, tf_vectorizer)
pyLDAvis.show(data)

Jupyter会给你提示一些警告。不用管它。因为此时你的浏览器会弹出一个新的标签页,结果图形会在这个标签页里正确显示出来。

如果你看完了图后,需要继续程序,就回到原先的标签页,点击Kernel菜单下的第一项Interrupt停止绘图,然后往下运行新的语句。

图的左侧,用圆圈代表不同的主题,圆圈的大小代表了每个主题分别包含文章的数量。

图的右侧,列出了最重要(频率最高)的30个关键词列表。注意当你没有把鼠标悬停在任何主题之上的时候,这30个关键词代表全部文本中提取到的30个最重要关键词。

如果你把鼠标悬停在1号上面:

 
技术图片
 

右侧的关键词列表会立即发生变化,红色展示了每个关键词在当前主题下的频率。

以上是认为设定主题数为5的情况。可如果我们把主题数量设定为10呢?

你不需要重新运行所有代码,只需要执行下面这几行就可以了。

这段程序还是需要运行一段时间,请耐心等待。




n_topics = 10
lda = LatentDirichletAllocation(n_topics=n_topics, max_iter=50,
                                learning_method=‘online‘,
                                learning_offset=50.,
                                random_state=0)
lda.fit(tf)
print_top_words(lda, tf_feature_names, n_top_words)
pyLDAvis.sklearn.prepare(lda, tf, tf_vectorizer)

程序输出给我们10个主题下最重要的20个关键词。

Topic #0:
这个 就是 如果 可能 用户 一些 什么 很多 没有 这样 时候 但是 因为 不是 所以 不同 如何 使用 或者 非常
Topic #1:
中国 孩子 增长 市场 2016 学生 10 2015 城市 自己 人口 大众 关注 其中 教育 同比 没有 美国 投资 这个
Topic #2:
data 变量 距离 http 样本 com www 检验 方法 分布 计算 聚类 如下 分类 之间 两个 一种 差异 表示 序列
Topic #3:
电子 采集 应当 或者 案件 保护 规定 信用卡 收集 是否 提取 设备 法律 申请 法院 系统 记录 相关 要求 无法
Topic #4:
系统 检索 交通 平台 专利 智能 监控 采集 海量 管理 搜索 智慧 出行 视频 车辆 计算 实现 基于 数据库 存储
Topic #5:
可视化 使用 工具 数据库 存储 hadoop 处理 图表 数据仓库 支持 查询 开发 设计 sql 开源 用于 创建 用户 基于 软件
Topic #6:
学习 算法 模型 机器 深度 神经网络 方法 训练 特征 分类 网络 使用 基于 介绍 研究 预测 回归 函数 参数 图片
Topic #7:
企业 管理 服务 互联网 金融 客户 行业 平台 实现 建立 社会 政府 研究 资源 安全 时代 利用 传统 价值 医疗
Topic #8:
人工智能 领域 机器人 智能 公司 人类 机器 学习 未来 已经 研究 他们 识别 可能 计算机 目前 语音 工作 现在 能够
Topic #9:
用户 公司 企业 互联网 平台 中国 数据分析 行业 产业 产品 创新 项目 2016 服务 工作 科技 相关 业务 移动 市场
()

附带的是可视化的输出结果:

 
技术图片
 

如果不能直接输出图形,还是按照前面的做法,执行:

data = pyLDAvis.sklearn.prepare(lda, tf, tf_vectorizer)
pyLDAvis.show(data)

你马上会发现当主题设定为10的时候,一些有趣的现象发生了——大部分的文章抱团出现在右上方,而2个小部落(8和10)似乎离群索居。我们查看一下这里的8号主题,看看它的关键词构成。

 
技术图片
 

通过高频关键词的描述,我们可以猜测到这一主题主要探讨的是政策和法律法规问题,难怪它和那些技术、算法与应用的主题显得如此格格不入。

说明

前文帮助你一步步利用LDA做了主题抽取。成就感爆棚吧?然而这里有两点小问题值得说明。

首先,信息检索的业内专家一看到刚才的关键词列表,就会哈哈大笑——太粗糙了吧!居然没有做中文停用词(stop words)去除!没错,为了演示的流畅,我们这里忽略了许多细节。很多内容使用的是预置默认参数,而且完全忽略了中文停用词设置环节,因此“这个”、“如果”、“可能”、“就是”这样的停用词才会大摇大摆地出现在结果中。不过没有关系,完成比完美重要得多。知道了问题所在,后面改进起来很容易。有机会我会写文章介绍如何加入中文停用词的去除环节。

另外,不论是5个还是10个主题,可能都不是最优的数量选择。你可以根据程序反馈的结果不断尝试。实际上,可以调节的参数远不止这一个。如果你想把全部参数都搞懂,可以继续阅读下面的“原理”部分,按图索骥寻找相关的说明和指引。

原理

前文我们没有介绍原理,而是把LDA当成了一个黑箱。不是我不想介绍原理,而是过于复杂。

只给你展示其中的一个公式,你就能管窥其复杂程度了。

 
技术图片
 

透露给你一个秘密:在计算机科学和数据科学的学术讲座中,讲者在介绍到LDA时,都往往会把原理这部分直接跳过去。

好在你不需要把原理完全搞清楚,再去用LDA抽取主题。

这就像是学开车,你只要懂得如何加速、刹车、换挡、打方向,就能让车在路上行驶了。即便你通过所有考试并取得了驾驶证,你真的了解发动机或电机(如果你开的是纯电车)的构造和工作原理吗?

但是如果你就是希望了解LDA的原理,那么我给你推荐2个学起来不那么痛苦的资源吧。

首先是教程幻灯。slideshare是个寻找教程的好去处。 这份教程 浏览量超过20000,内容深入浅出,讲得非常清晰。

 
技术图片
 

但如果你跟我一样,是个视觉学习者的话,我更推荐你看 这段 Youtube视频。

 
技术图片
 

讲者是Christine Doig,来自Continuum Analytics。咱们一直用的Python套装Anaconda就是该公司的产品。

Christine使用的LDA原理解释模型,不是这个LDA经典论文中的模型图(大部分人觉得这张图不易懂):

 
技术图片
 

她深入阅读了各种文献后,总结了自己的模型图出来:

 
技术图片
 

用这个模型来解释LDA,你会立即有豁然开朗的感觉。

祝探索旅程愉快!

讨论

除了本文提到的LDA算法,你还知道哪几种用于主题抽取的机器学习算法?你觉得主题建模(topic model)在信息检索等领域还有哪些可以应用的场景?欢迎留言分享给大家,我们一起交流讨论。

喜欢请点赞。还可以微信关注和置顶我的公众号“玉树芝兰”(nkwangshuyi)

如果你对数据科学感兴趣,不妨阅读我的系列教程索引贴《如何高效入门数据科学?》,里面还有更多的有趣问题及解法。

 


作者:王树义
链接:https://www.jianshu.com/p/fdde9fc03f94
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

以上是关于如何用 Python 和正则表达式抽取文本结构化信息?的主要内容,如果未能解决你的问题,请参考以下文章

文本信息抽取与结构化详聊如何用BERT实现关系抽取

如何用正则表达式判断文本中包含有汉字

易语言如何用正则表达式来读取制定的网页文本 最好源码上,谢谢

如何用正则表达式限制文本框输入数字位数?

如何用正则表达式限制文本框输入数字位数

在前台页面中如何用正则表达式判断文本框中