第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()

 

importre

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)

printtext [ 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的正则表达式也可以“打包”,比如将”Ilove 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 iin 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']: #逐行匹配

   ifre.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 iin production['text']:

   ifre.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 iin production['text']:

   ifre.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技巧的综合运用。

 

来自 <http://mp.weixin.qq.com/s/OqA44irTacLi1eYt31wNHw>

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值