前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >统计师的Python日记【第九天:正则表达式】

统计师的Python日记【第九天:正则表达式】

作者头像
数说君
发布2018-04-04 16:26:35
1.8K0
发布2018-04-04 16:26:35
举报
文章被收录于专栏:数说工作室

本文是【统计师的Python日记】第9天的日记

回顾一下:

  • 第1天学习了Python的基本页面、操作,以及几种主要的容器类型。
  • 第2天学习了python的函数、循环和条件、类。
  • 第3天了解了Numpy这个工具库。
  • 第4、5两天掌握了Pandas这个库的基本用法。
  • 第6天学习了数据的合并堆叠。
  • 第7天开始学习数据清洗,着手学会了重复值删除、异常值处理、替换、创建哑变量等技能。
  • 第8天接着学习数据清洗,一些常见的数据处理技巧,如分列、去除空白等被我一一攻破

原文复习(点击查看):

第1天:谁来给我讲讲Python?

第2天:再接着介绍一下Python呗

【第3天:Numpy你好】

【第4天:欢迎光临Pandas】

【第四天的补充

【第5天:Pandas,露两手】

【第6天:数据合并】

【第七天:数据清洗(1)】

【第8天:数据清洗(2)文本处理】

今天将带来第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技巧的综合运用。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2016-06-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数说工作室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档