统计师的Python日记第九天:正则表达式
Posted 数说工作室
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了统计师的Python日记第九天:正则表达式相关的知识,希望对你有一定的参考价值。
本文是【统计师的Python日记】第9天的日记
回顾一下:
第1天学习了Python的基本页面、操作,以及几种主要的容器类型。
第2天学习了python的函数、循环和条件、类。
第3天了解了Numpy这个工具库。
第4、5两天掌握了Pandas这个库的基本用法。
第6天学习了数据的合并堆叠。
第7天开始学习数据清洗,着手学会了重复值删除、异常值处理、替换、创建哑变量等技能。
第8天接着学习数据清洗,一些常见的数据处理技巧,如分列、去除空白等被我一一攻破
原文复习(点击查看):
】
今天将带来第9天的学习日记。
目录如下:
前言
1. 正则表达式简介
(1)元字符
(2)函数
2. 用正则表达式处理Pandas数据
(1)匹配行
(2)提取匹配文字
(3)提取匹配文字的一部分
统计师的Python日记【第9天:正则表达式】
前言
根据我的Python学习计划:
Numpy → Pandas → 掌握一些数据清洗、规整、合并等功能 → 掌握正则表达式 → 掌握类似与SQL的聚合等数据管理功能 → 能够用Python进行统计建模、假设检验等分析技能 → 能用Python打印出100元钱 → 能用Python帮我洗衣服、做饭 → 能用Python给我生小猴子......
在数据清洗的学习过程中,发现文本数据的处理并非一招半式能解决,有时必须要搬出利器——正则表达式。在之前的【SAS正则表达式】系列中(在后台回复【sasre】查看),我用正则表达式做文本处理做的非常之爽,比如下面这列数据:
(01)1872-8756
Body shop P1
Book B13
(05)9212-0098
PD(05)9206-4571
Shushuophone
(12) 6753-5513
None here
PD(12)6434-4532
P&DWashing
......(未显示完)
这是一份产品名单,有的用数字来编码,有的直接是产品的名字,现在想把数字编码(也即红色字体)的部分提取出来,看似没有什么规律,但是在SAS中,用正则表达式两行代码就搞定了。
现在,要挑战用正则表达式处理Pandas的数据。
1. 正则表达式简介
虽然在SAS中学了正则表达式的基础,Python稍有不同,现在还是简单复习一下:
(1)元字符
元字符是一系列代码,用来简化表达某种意思,比如:
\d 表示数字
\D 表示非数字
\w 表示单词字符
\W 表示非单词字符
等等。
有一个技术博客里给了很好的总结,网址:http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html,以备查询。
(2)函数
在SAS中,PRXPARSE()是获取一个正则表达式的pattern,在Python中对应的就是 compile() 。
import re
pattern = re.compile(元字符表达式)
比如说,
pattern = re.compile('\d') 就是把一个数字存到pattern里了;
pattern = re.compile('[0-9]') 也可以用这个,表示把0-9任意一个存到pattern里去;
pattern = re.compile(',') 就是把逗号存到pattern里去。
那它有什么用呢?pattern后面可以接函数,来实现一些文本处理的功能,比如:
pattern.split(text) 对text按照pattern分割成list;
pattern.findall(text) 匹配text中所有符合pattern的部分;
pattern.search(text) 匹配text中第一个符合pattern的部分;
pattern.match(text) 匹配text开头符合pattern的部分;
pattern.sub(subtext, text) 将text中符合pattern的部分替换为subtext。
有点抽象,来具体学习一下,以text为例:
text="Shu Shuojun, I love u, 520"
这个文本,如果想将它变成一个list,用逗号来分割,我可以这样:
pattern = re.compile(',')
pattern.split(text)
结果为:
而利用 findall(),我可以寻找某种格式的字符,相当于SAS中的PRXMATCH(),比如想找到以Sh开头的字符:
pattern = re.compile('Sh\w*')
pattern.findall(text)
\w表示单词字符,*表示匹配前面的表达式0次或无限次,\w*也就是匹配一个单词0次或无限次,'Sh\w*'这个元字符的意思就是:匹配以Sh开头,后面跟着N个单词字符的文本(N取0到无穷)。
Sh开头的两个单词都被匹配出来了。
search() 跟findall类似,findall返回的是字符串中所有的匹配项,search则只返回第一个匹配项,的起始位置和结束位置!相当于SAS中的PRXSUBSTR(),同样以刚才的例子,换成search()看看会发生什么变化:
pattern = re.compile('Sh\w*')
pattern.search(text)
search()返回的是起始位置和结束位置,分别记录在这个东东的.start()和.end()两个函数里面,因此要这样:
pattern = re.compile('Sh\w*')
m = pattern.search(text)
print text [ m.start() : m.end() ]
所以search()只记录了第一个匹配项的开头和结束位置。
还有一个函数 match(),与search()不同之处在于,它只匹配字符串的开头部分:
从这里看与search没什么差别,因为text的开头就是Shu,如果换一下只匹配Shushuo看看,也就是pattern改成:pattern = re.compile('Sh\w\w+')
用search()完美匹配出来了,+表示匹配前面的字符至少一次。用match()呢?
不行,匹配不出来,因为Shuojun不是出现在开头。
sub() 方法是用来替换,SAS中的PRXCHANGES也提供了替换,比如现在想把text中的520换成250:
pattern = re.compile('\d+')
pattern.sub('250',text)
\d表示数字字符,\d+表示匹配数字字符至少1次,由于text中的数字只有520,因此,text中符合pattern的必然是520这部分。
pattern.sub('250',text)就是把text中520换成250:
在SAS中,学过 “打包”,
在Python的正则表达式也可以“打包”,比如将”I love shushuo”中的shu和shuo分别打包:
text = 'I love shushuo'
pattern = re.compile( '(shu)(shuo)' )
m = pattern.search(text)
m.groups()
再比如,将ve和shuo打包:
正则表达式是文本分析的利器,在爬虫中用处也非常大。但本文中,我要挑战的是对DataFrame结构数据进行正则表达式的处理。参照SAS正则表达的介绍,试图将在SAS中实现的功能在Python中也能实现。
2. 用正则表达式处理Pandas数据
(1)匹配行
我在SAS中用正则表达式解决的第一个问题是是这样的:
(01)1872-8756
Body shop P1
Book B13
(05)9212-0098
PD(05)9206-4571
Shu shuo phone
(12) 6753-5513
None here
PD(12)6434-4532
P&DWashing
......(未显示完)
也就是开头的问题,这一份产品列表,现在只想要数字编码、也就是红色字体的部分。如何操作?
先来分析一下:
首先两个PD不是必须的,有的有、有的没有,但后面(XX)括号里面两个数字是必须的,我就按照这样的模式来获取红色字体部分:
pattern = re.compile('P?D?\D\d{2}\D\s?\d{4}-\d{4}')
这个表达式如何匹配的?
编号 |
P |
D |
( |
XX |
) |
空格 |
XXXX |
- |
XXXX |
正则表达式 |
P? |
D? |
\D |
\d{2} |
\D |
\s? |
\d{4} |
- |
\d{4} |
对于单个字符串很简单,findall一下就可以了,正如第一部分的介绍,但是对于DataFrane的数据结构,该如何实现?
先读入Pandas中去,数据就命名为production:
我捣鼓出了两种方法:
方法一:
pattern = re.compile('P?D?\D\d{2}\D\s?\d{4}-\d{4}')
matchPro = [] #用来储存匹配的观测值
for i in production['text']: #进行逐行匹配
if re.findall(pattern, i): #判断是否匹配
matchPro.append(i) #如果匹配了就把这个观测值放进matchPro中去
pd.DataFrame(matchPro, columns=['text']) #最终生成匹配出来的DataFrame数据。
成功匹配出来了。
方法二:
思路是将匹配行的索引记录下来,而不是观测值:
pattern = re.compile('P?D?\D\d{2}\D\s?\d{4}-\d{4}')
delIndexSet = [] #用来储存匹配行的索引
for i in production['text']: #逐行匹配
if re.findall(pattern, i): #是否匹配
delIndex = list(production['text']).index(i) #如果匹配了就获取这行的索引
delIndexSet.append(delIndex) #将匹配行的索引放进delIndex
pd.DataFrame(production,index=delIndexSet) #获取原数据中的匹配行
也可以成功匹配出来。
(2)提取匹配文字
在SAS正则表达式中还遇到了新的问题:
(01)1872-8756
Body shop P1
Book B13
(05)9212-0098
PD(05)9206-4571
Shushuo phone
(12) 6753-5513
None here
PD(12)6434-4532
P&D Washing
PC Pro4321S: (09) 1352-3154
这是一份新的产品列表,现在多了最后一行,这一行是产品的名字和数字编码放在一起了,我只想要数字编码的部分,即红色部分,前面的不想要,怎么办?
第一部分中介绍了search()提取了匹配部分的开头和结尾部分,这个一定可以帮我解决!
先把数据读入Pandas,仍然命名为production:
解决代码如下:
pattern = re.compile('P?D?\D\d{2}\D\s?\d{4}-\d{4}')
matchPro = [] #将匹配部分的文字装入这个list
for i in production['text']: #逐行匹配
if re.search(pattern, i):
m = re.search(pattern, i)
matchText = i[m.start():m.end()] #如果匹配,那么利用匹配部分开头和结尾的位置,来获取匹配的字符
matchPro.append(matchText) #装入matchPro中
pd.DataFrame(matchPro, columns=['text'])
结果如下:
(3)提取匹配文字中的一部分
刚刚对于这个例子:
(01)1872-8756
Body shop P1
Book B13
(05)9212-0098
PD(05)9206-4571
Shushuo phone
(12) 6753-5513
None here
PD(12)6434-4532
P&D Washing
PC Pro4321S: (09) 1352-3154
我成功的写了一个正则表达式,提取出来匹配的部分,元字符为:
P?D?\D\d{2}\D\s?\d{4}-\d{4}
这个表达式和红色字体部分是对应的。那么有一个问题,假如我想提取出来这段匹配文字的任一部分呢?比如(09) 1352-3154这个括号里的数字,按照情节设定,括号里的数字代表产品的类型,现在想把它提取出来。
和SAS一样,同样用“打包”的思路,前面已经学过在Python中如何打包了:
pattern = re.compile('P?D?\D(\d{2})\D\s?\d{4}-\d{4}') #将括号里的数字“打包”
matchType = []
for i in production['text']:
if re.search(pattern, i):
m = re.search(pattern, i)
type = m.groups()[0] #打包后的内容存在groups()中
matchType.append(type)
pd.DataFrame(matchType,columns=['type'])
哎呀,只有一列,我不知道每个数字跟原来的哪个对应啊,我得把原数据也加上:
pattern = re.compile('P?D?\D(\d{2})\D\s?\d{4}-\d{4}')
matchPro = []
matchType = []
match = {}
for i in production['text']:
if re.search(pattern, i):
m = re.search(pattern, i)
type = i[m.start():m.end()]
matchText = m.groups()[0]
matchType.append(type)
matchPro.append(matchText)
match['text'] = matchPro
match['type'] = matchType
pd.DataFrame(match,columns=['text','type'])
这样,原数据也有,打包的部分也有了,结果如下:
(4)总结
虽然具体的问题千奇百怪,但核心的方法都是一样的,正则表达式函数+迭代 = Pandas数据的处理。考验的还是Python技巧的综合运用。
做个小游戏,您觉得本【统计师的Python日记】系列如何?
1、不好——跳转至A
2、好——跳转至2.1
2.1 打赏吗?
打赏——跳转至B
不打赏——跳转至2.2
2.2 点击文末广告?
点击——跳转至C
不点击——跳转至A
A 有什么建议意见呢?您可以在文末评论区留言,帮我做的越来越好!
B 谢谢爷~!劳驾您在文末打赏,我会再接再厉哒!
C 谢谢小哥,谢谢美女~!广告商会给我打赏哒!
1. 授权白名单请给数说君留言
2. 关于数据分析的提问或求助,可以在分答中搜索“数说君”找我。
4. 查看所有文章请在后台回复【M】
数说工作室,数据中看大势
微信ID:shushuojun
长按二维码关注数说工作室
以上是关于统计师的Python日记第九天:正则表达式的主要内容,如果未能解决你的问题,请参考以下文章