当前位置:主页 > 数据驱动 >

数据科学入门必读:如何使用正则表达式?

 发布时间:2018-07-12 来源:
正则表达式对数据处理而言非常重要。近日,Dataquest 博客发布了一篇针对入门级数据科学家的正则表达式介绍文章,通过实际操作详细阐述了正则表达式的使用方法和一些技巧。

数据科学家的一部分使命是操作大量数据。有时候,这些数据中会包含大量文本语料。比如,假如我们需要搞清楚「巴拿马文件 [注意,可能是敏感词]」丑闻中谁给谁发送过邮件,那么我们就要筛查 1150 万份文档!我们可以采用人工方式,亲自阅读每一封电子邮件,但我们也可以利用 Python 的力量。毕竟,代码存在的意义就是自动执行任务。

即便如此,从头开始写一个脚本也需要大量时间和精力。这就是正则表达式的用武之地。正则表达式(regular expression)也被称为 RE、regex 和 regular pattern,这是一种让我们能快速筛查和分析文本的紧凑型语言。正则表达式始于 1956 年——Stephen Cole Kleene 创造了它并将其用于描述人类神经系统的 McCulloch-Pitts 模型。到了 60 年代,Ken Thompson 将这种标记方法添加到了一个类似 Windows 记事本的文本编辑器中,自那以后,正则表达式不断发展壮大。

正则表达式的一大关键特征是其经济实用的脚本。你甚至可以将其看作是代码中的捷径。没有它,我们就要码更多代码才能实现相同的功能。学习本教程需要基本的 Python 知识。如果你理解 if-else 语句、while 和 for 循环、列表(list)和字典(dictionary),你就能读懂本教程的大部分内容。你也需要一个代码编辑器,比如 Visual Code Studio、PyCharm 或 Atom。此外,了解一点 pandas 的基本知识会很有帮助,这样在我们解读每一行代码时你才不会迷失方向。如果你需要学学 pandas,可以参考这个教程:https://www.dataquest.io/blog/pandas-python-tutorial/

学习完本教程后,你将熟悉一些正则表达式的工作原理,并能使用其基本模式和 Python 的 re 模块提供的函数来分析字符串。我们还将体验正则表达式和 pandas 库高效处理大规模无组织数据集的能力。

现在,我们来看看正则表达式的能力。

介绍我们的数据集

我们将使用来自 Kaggle 的 Fraudulent Email Corpus(欺诈电子邮件语料库)。其中包含 1998 年到 2007 年之间发送的数千封钓鱼邮件。这些邮件读起来很有意思。我们首先将使用单封邮件学习基本的正则表达式命令,然后我们会对整个语料库进行处理。

语料库地址:https://www.kaggle.com/rtatman/fraudulent-email-corpus

介绍 Python 的正则表达式模块

首先,准备数据集:打开那个文本文件,将其设置成「只读」,然后读取它。我们也为其分配了一个变量 fh,表示文件句柄(file handle)。

fh = open(r"test_emails.txt", "r").read()

注意我们直接在目录路径之前使用了 r。这项技术会将一个字符串转换成一个原始字符串,这有助于避免由某些机器阅读字符的方式所导致的冲突,比如 Windows 中目录路径中的反斜杠。

你可能注意到了我们目前没有使用整个语料库。我们只是人工地取了该语料库中前面几封邮件,然后将其做成了一个测试文件。这样做的目的是在本教程中输出显示测试结果时,就不用每次都显示数千行结果了。这能免除很多烦恼。你自己练习的时候使用完整语料库或我们的测试文件都不会有问题。

现在,假设我们想知道这些电子邮件的发件人。我们可以试试只用原始的 Python 来实现:

for line in fh.split("\n"):
if "From:" in line:
print(line)

也可以使用正则表达式:

import re
for line in re.findall("From:.*", fh):
print(line)

我们来解读一下这段代码。我们首先导入了 Python 的 re 模块。然后我们写了操作代码。在这个简单的示例中,这段代码只比原始 Python 少一行。但是,随着任务的增加,正则表达式可以让你的脚本继续保持简单经济。

re.findall() 返回字符串中满足其模式的所有实例的列表。这是 Python 内置的 re 模块中最常用的函数之一。分解看看。该函数的形式是 re.findall(pattern, string),有两个参数。其中,pattern 表示我们希望寻找的子字符串,string 表示我们要在其中查找的主字符串。主字符串可以包含很多行。

.* 是字符串模式的简写。我们马上就会详细解释。现在只需知道它们的作用是匹配 From: 字段中的名称和电子邮箱地址。

在我们继续深入之前,我们先了解一些常见的正则表达式模式。

常见的正则表达式模式

我们在上面的 re.findall() 中使用的模式中包含一个完全拼写出来的字符串 From:。这在我们知道我们所要寻找的东西是什么时非常有用,可以确定到实际的字母以及大小写。如果我们不知道我们所想要的字符串的确切格式,我们将难以为继。幸运的是,正则表达式有解决这类情况的基本模式。我们看看本教程中会使用的一些模式:

\w 匹配字母数字字符,即 a-z、A-Z 和 0-9,也会匹配下划线 _ 和连接号 –
\d 匹配数字,即 0-9
\s 匹配空白字符,包括制表符、换行符、回车符和空格符
\S 匹配非空白字符
. 匹配除换行符 \n 之外的任意字符

有了这些正则表达式模式,你就能在我们继续解释代码时很快理解。

使用正则表达式模式

我们现在可以解释上面 re.findall("From:.*", text) 一行中的 .* 了。首先来看 .

for line in re.findall("From:.", fh):
print(line)

通过在 From: 后面添加一个 .,我们是要寻找 From: 之后另外的一个字符。因为 . 是查找除 \n 之外的任意字符,所以这会得到我们看不到的空格。我们可以多加一些点来验证这个情况

for line in re.findall("From:...........", fh):
print(line)

看起来加点就能让我们得到这一行的其余内容了。但这很单调乏味,而且我们不知道需要加多少个点。这就是星号 * 发挥作用的地方。

* 匹配 0 个或更多个其左侧的模式的实例。也就是说它会查找重复的模式。当我们查找重复模式时,我们说我们的搜索是「贪婪匹配」。如果我们没有查找重复模式,我们可以说我们的搜索是「非贪婪匹配」或「懒惰匹配」。

让我们使用 * 构建一个 . 的贪婪搜索

for line in re.findall("From:.*", fh):
print(line)

因为 * 匹配 0 个或多个其左侧模式的实例且 . 在其左侧,所以我们可以获取 From: 字段中的所有字符,直到该行结束。这样就用美丽而简洁的代码输出显示了一整行。

我们甚至可以更进一步只取出其中的名称。

match = re.findall("From:.*", fh)
for line in match:
print(re.findall("\".*\"", line))

这里,我们先使用之前的做法通过 re.findall() 得到了包含 From:.* 模式的行的列表。接下来,我们遍历这个列表。在这一次训练中,我们都再执行一次 re.findall()。这一次,该函数先从匹配第一个引号开始。

文章评论

互联网 自媒体专栏 智能硬件 资本动态 移动互联网 游戏 数据驱动 滚动新闻 O2O 访问移动版
Copyright © 2002-2013 搞数码网 版权所有  电话:0510-898978789 邮箱:89898989@qq.com 地址:北京市新会金水岸国商大厦B-6-B