回顧一下:
第1天學(xué)習(xí)了Python的基本頁面、操作,以及幾種主要的容器類型。
第2天學(xué)習(xí)了python的函數(shù)、循環(huán)和條件、類。
第3天了解了Numpy這個(gè)工具庫。
第4、5兩天掌握了Pandas這個(gè)庫的基本用法。
第6天學(xué)習(xí)了數(shù)據(jù)的合并堆疊。
第7天開始學(xué)習(xí)數(shù)據(jù)清洗,著手學(xué)會(huì)了重復(fù)值刪除、異常值處理、替換、創(chuàng)建啞變量等技能。
第8天接著學(xué)習(xí)數(shù)據(jù)清洗,一些常見的數(shù)據(jù)處理技巧,如分列、去除空白等被我一一攻破
原文復(fù)習(xí)(點(diǎn)擊查看):
【第1天:誰來給我講講Python?】【第2天:再接著介紹一下Python唄】【第3天:Numpy你好】【第4天:歡迎光臨Pandas】【第四天的補(bǔ)充】
【第5天:Pandas,露兩手】【第6天:數(shù)據(jù)合并】【第7天:數(shù)據(jù)清洗(1)】【第8天:數(shù)據(jù)清洗(2)文本處理】今天將帶來第9天的學(xué)習(xí)日記。
目錄如下:
前言
1. 正則表達(dá)式簡介
(1)元字符
(2)函數(shù)
2. 用正則表達(dá)式處理Pandas數(shù)據(jù)
(1)匹配行
(2)提取匹配文字
(3)提取匹配文字的一部分
統(tǒng)計(jì)師的Python日記【第9天:正則表達(dá)式】
前言
根據(jù)我的Python學(xué)習(xí)計(jì)劃:
Numpy → Pandas → 掌握一些數(shù)據(jù)清洗、規(guī)整、合并等功能 → 掌握正則表達(dá)式 → 掌握類似與SQL的聚合等數(shù)據(jù)管理功能 → 能夠用Python進(jìn)行統(tǒng)計(jì)建模、假設(shè)檢驗(yàn)等分析技能 → 能用Python打印出100元錢 → 能用Python幫我洗衣服、做飯 → 能用Python給我生小猴子......
在數(shù)據(jù)清洗的學(xué)習(xí)過程中,發(fā)現(xiàn)文本數(shù)據(jù)的處理并非一招半式能解決,有時(shí)必須要搬出利器——正則表達(dá)式。在之前的【SAS正則表達(dá)式】系列中(在后臺(tái)回復(fù)【sasre】查看),我用正則表達(dá)式做文本處理做的非常之爽,比如下面這列數(shù)據(jù):
(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
......(未顯示完)
這是一份產(chǎn)品名單,有的用數(shù)字來編碼,有的直接是產(chǎn)品的名字,現(xiàn)在想把數(shù)字編碼(也即紅色字體)的部分提取出來,看似沒有什么規(guī)律,但是在SAS中,用正則表達(dá)式兩行代碼就搞定了。
現(xiàn)在,要挑戰(zhàn)用正則表達(dá)式處理Pandas的數(shù)據(jù)。
1. 正則表達(dá)式簡介
雖然在SAS中學(xué)了正則表達(dá)式的基礎(chǔ),Python稍有不同,現(xiàn)在還是簡單復(fù)習(xí)一下:
(1)元字符
元字符是一系列代碼,用來簡化表達(dá)某種意思,比如:
\d 表示數(shù)字
\D 表示非數(shù)字
\w 表示單詞字符
\W 表示非單詞字符
等等。
有一個(gè)技術(shù)博客里給了很好的總結(jié),網(wǎng)址:http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html,以備查詢。
(2)函數(shù)
在SAS中,PRXPARSE()是獲取一個(gè)正則表達(dá)式的pattern,在Python中對(duì)應(yīng)的就是 compile() 。
import re
pattern = re.compile(元字符表達(dá)式)
比如說,
pattern = re.compile('\d') 就是把一個(gè)數(shù)字存到pattern里了;
pattern = re.compile('[0-9]') 也可以用這個(gè),表示把0-9任意一個(gè)存到pattern里去;
pattern = re.compile(',') 就是把逗號(hào)存到pattern里去。
那它有什么用呢?pattern后面可以接函數(shù),來實(shí)現(xiàn)一些文本處理的功能,比如:
pattern.split(text) 對(duì)text按照pattern分割成list;
pattern.findall(text) 匹配text中所有符合pattern的部分;
pattern.search(text) 匹配text中第一個(gè)符合pattern的部分;
pattern.match(text) 匹配text開頭符合pattern的部分;
pattern.sub(subtext, text) 將text中符合pattern的部分替換為subtext。
有點(diǎn)抽象,來具體學(xué)習(xí)一下,以text為例:
text='Shu Shuojun, I love u, 520'
這個(gè)文本,如果想將它變成一個(gè)list,用逗號(hào)來分割,我可以這樣:
pattern = re.compile(',')
pattern.split(text)
結(jié)果為:
而利用 findall(),我可以尋找某種格式的字符,相當(dāng)于SAS中的PRXMATCH(),比如想找到以Sh開頭的字符:
pattern = re.compile('Sh\w*')
pattern.findall(text)
\w表示單詞字符,*表示匹配前面的表達(dá)式0次或無限次,\w*也就是匹配一個(gè)單詞0次或無限次,'Sh\w*'這個(gè)元字符的意思就是:匹配以Sh開頭,后面跟著N個(gè)單詞字符的文本(N取0到無窮)。
Sh開頭的兩個(gè)單詞都被匹配出來了。
search() 跟findall類似,findall返回的是字符串中所有的匹配項(xiàng),search則只返回第一個(gè)匹配項(xiàng),的起始位置和結(jié)束位置!相當(dāng)于SAS中的PRXSUBSTR(),同樣以剛才的例子,換成search()看看會(huì)發(fā)生什么變化:
pattern = re.compile('Sh\w*')
pattern.search(text)
search()返回的是起始位置和結(jié)束位置,分別記錄在這個(gè)東東的.start()和.end()兩個(gè)函數(shù)里面,因此要這樣:
pattern = re.compile('Sh\w*')
m = pattern.search(text)
print text [ m.start() : m.end() ]
所以search()只記錄了第一個(gè)匹配項(xiàng)的開頭和結(jié)束位置。
還有一個(gè)函數(shù) match(),與search()不同之處在于,它只匹配字符串的開頭部分:
從這里看與search沒什么差別,因?yàn)閠ext的開頭就是Shu,如果換一下只匹配Shushuo看看,也就是pattern改成:pattern = re.compile('Sh\w\w+')
用search()完美匹配出來了,+表示匹配前面的字符至少一次。用match()呢?
不行,匹配不出來,因?yàn)镾huojun不是出現(xiàn)在開頭。
sub() 方法是用來替換,SAS中的PRXCHANGES也提供了替換,比如現(xiàn)在想把text中的520換成250:
pattern = re.compile('\d+')
pattern.sub('250',text)
\d表示數(shù)字字符,\d+表示匹配數(shù)字字符至少1次,由于text中的數(shù)字只有520,因此,text中符合pattern的必然是520這部分。
pattern.sub('250',text)就是把text中520換成250:
在SAS中,學(xué)過 “打包”,
在Python的正則表達(dá)式也可以“打包”,比如將”I love shushuo”中的shu和shuo分別打包:
text = 'I love shushuo'
pattern = re.compile( '(shu)(shuo)' )
m = pattern.search(text)
m.groups()
再比如,將ve和shuo打包:
正則表達(dá)式是文本分析的利器,在爬蟲中用處也非常大。但本文中,我要挑戰(zhàn)的是對(duì)DataFrame結(jié)構(gòu)數(shù)據(jù)進(jìn)行正則表達(dá)式的處理。參照SAS正則表達(dá)的介紹,試圖將在SAS中實(shí)現(xiàn)的功能在Python中也能實(shí)現(xiàn)。
2. 用正則表達(dá)式處理Pandas數(shù)據(jù)
(1)匹配行
我在SAS中用正則表達(dá)式解決的第一個(gè)問題是是這樣的:
(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
......(未顯示完)
也就是開頭的問題,這一份產(chǎn)品列表,現(xiàn)在只想要數(shù)字編碼、也就是紅色字體的部分。如何操作?
先來分析一下:
首先兩個(gè)PD不是必須的,有的有、有的沒有,但后面(XX)括號(hào)里面兩個(gè)數(shù)字是必須的,我就按照這樣的模式來獲取紅色字體部分:
pattern = re.compile('P?D?\D\d{2}\D\s?\d{4}-\d{4}')
這個(gè)表達(dá)式如何匹配的?
編號(hào)
P
D
(
XX
)
空格
XXXX
-
XXXX
正則表達(dá)式
P?
D?
\D
\d{2}
\D
\s?
\d{4}
-
\d{4}
對(duì)于單個(gè)字符串很簡單,findall一下就可以了,正如第一部分的介紹,但是對(duì)于DataFrane的數(shù)據(jù)結(jié)構(gòu),該如何實(shí)現(xiàn)?
先讀入Pandas中去,數(shù)據(jù)就命名為production:
我搗鼓出了兩種方法:
方法一:
pattern = re.compile('P?D?\D\d{2}\D\s?\d{4}-\d{4}')
matchPro = [] #用來儲(chǔ)存匹配的觀測值
for i in production['text']: #進(jìn)行逐行匹配
if re.findall(pattern, i): #判斷是否匹配
matchPro.append(i) #如果匹配了就把這個(gè)觀測值放進(jìn)matchPro中去
pd.DataFrame(matchPro, columns=['text']) #最終生成匹配出來的DataFrame數(shù)據(jù)。
成功匹配出來了。
方法二:
思路是將匹配行的索引記錄下來,而不是觀測值:
pattern = re.compile('P?D?\D\d{2}\D\s?\d{4}-\d{4}')
delIndexSet = [] #用來儲(chǔ)存匹配行的索引
for i in production['text']: #逐行匹配
if re.findall(pattern, i): #是否匹配
delIndex = list(production['text']).index(i) #如果匹配了就獲取這行的索引
delIndexSet.append(delIndex) #將匹配行的索引放進(jìn)delIndex
pd.DataFrame(production,index=delIndexSet) #獲取原數(shù)據(jù)中的匹配行
也可以成功匹配出來。
(2)提取匹配文字
在SAS正則表達(dá)式中還遇到了新的問題:
(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
這是一份新的產(chǎn)品列表,現(xiàn)在多了最后一行,這一行是產(chǎn)品的名字和數(shù)字編碼放在一起了,我只想要數(shù)字編碼的部分,即紅色部分,前面的不想要,怎么辦?
第一部分中介紹了search()提取了匹配部分的開頭和結(jié)尾部分,這個(gè)一定可以幫我解決!
先把數(shù)據(jù)讀入Pandas,仍然命名為production:
解決代碼如下:
pattern = re.compile('P?D?\D\d{2}\D\s?\d{4}-\d{4}')
matchPro = [] #將匹配部分的文字裝入這個(gè)list
for i in production['text']: #逐行匹配
if re.search(pattern, i):
m = re.search(pattern, i)
matchText = i[m.start():m.end()] #如果匹配,那么利用匹配部分開頭和結(jié)尾的位置,來獲取匹配的字符
matchPro.append(matchText) #裝入matchPro中
pd.DataFrame(matchPro, columns=['text'])
結(jié)果如下:
(3)提取匹配文字中的一部分
剛剛對(duì)于這個(gè)例子:
(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
我成功的寫了一個(gè)正則表達(dá)式,提取出來匹配的部分,元字符為:
P?D?\D\d{2}\D\s?\d{4}-\d{4}
這個(gè)表達(dá)式和紅色字體部分是對(duì)應(yīng)的。那么有一個(gè)問題,假如我想提取出來這段匹配文字的任一部分呢?比如(09) 1352-3154這個(gè)括號(hào)里的數(shù)字,按照情節(jié)設(shè)定,括號(hào)里的數(shù)字代表產(chǎn)品的類型,現(xiàn)在想把它提取出來。
和SAS一樣,同樣用“打包”的思路,前面已經(jīng)學(xué)過在Python中如何打包了:
pattern = re.compile('P?D?\D(\d{2})\D\s?\d{4}-\d{4}') #將括號(hào)里的數(shù)字“打包”
matchType = []
for i in production['text']:
if re.search(pattern, i):
m = re.search(pattern, i)
type = m.groups()[0] #打包后的內(nèi)容存在groups()中
matchType.append(type)
pd.DataFrame(matchType,columns=['type'])
哎呀,只有一列,我不知道每個(gè)數(shù)字跟原來的哪個(gè)對(duì)應(yīng)啊,我得把原數(shù)據(jù)也加上:
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'])
這樣,原數(shù)據(jù)也有,打包的部分也有了,結(jié)果如下:
(4)總結(jié)
雖然具體的問題千奇百怪,但核心的方法都是一樣的,正則表達(dá)式函數(shù)+迭代 = Pandas數(shù)據(jù)的處理??简?yàn)的還是Python技巧的綜合運(yùn)用。
做個(gè)小游戲,您覺得本【統(tǒng)計(jì)師的Python日記】系列如何?
1、不好——跳轉(zhuǎn)至A
2、好——跳轉(zhuǎn)至2.1
2.1 打賞嗎?
打賞——跳轉(zhuǎn)至B
不打賞——跳轉(zhuǎn)至2.2
2.2 點(diǎn)擊文末廣告?
點(diǎn)擊——跳轉(zhuǎn)至C
不點(diǎn)擊——跳轉(zhuǎn)至A
A 有什么建議意見呢?您可以在文末評(píng)論區(qū)留言,幫我做的越來越好!
B 謝謝爺~!勞駕您在文末打賞,我會(huì)再接再厲噠!
C 謝謝小哥,謝謝美女~!廣告商會(huì)給我打賞噠!