第7章 文本數據
import pandas as pd
import numpy as np
一、string類型的性質
1. string與object的區別
string類型和object不同之處有三:
① 字符存取方法(string accessor methods,如str.count)會返回相應數據的Nullable類型,而object會隨缺失值的存在而改變返回類型
② 某些Series方法不能在string上使用,例如: Series.str.decode(),因爲存儲的是字符串而不是字節
③ string類型在缺失值存儲或運算時,類型會廣播爲pd.NA,而不是浮點型np.nan
其餘全部內容在當前版本下完全一致,但迎合Pandas的發展模式,我們仍然全部用string來操作字符串
2. string類型的轉換
如果將一個其他類型的容器直接轉換string類型可能會出錯:
當下正確的方法是分兩部轉換,先轉爲str型object,在轉爲string類型:
pd. Series( [ 1 , '1.' ] ) . astype( 'str' ) . astype( 'string' )
0 1
1 1.
dtype: string
pd. Series( [ 1 , 2 ] ) . astype( 'str' ) . astype( 'string' )
0 1
1 2
dtype: string
pd. Series( [ True , False ] ) . astype( 'str' ) . astype( 'string' )
0 True
1 False
dtype: string
二、拆分與拼接
1. str.split方法
(a)分割符與str的位置元素選取
s = pd. Series( [ 'a_b_c' , 'c_d_e' , np. nan, 'f_g_h' ] , dtype= "string" )
s
0 a_b_c
1 c_d_e
2 <NA>
3 f_g_h
dtype: string
根據某一個元素分割,默認爲空格
s. str . split( '_' )
0 [a, b, c]
1 [c, d, e]
2 <NA>
3 [f, g, h]
dtype: object
這裏需要注意split後的類型是object,因爲現在Series中的元素已經不是string,而包含了list,且string類型只能含有字符串
對於str方法可以進行元素的選擇,如果該單元格元素是列表,那麼str[i]表示取出第i個元素,如果是單個元素,則先把元素轉爲列表在取出
s. str . split( '_' ) . str [ 1 ]
0 b
1 d
2 <NA>
3 g
dtype: object
pd. Series( [ 'a_b_c' , [ 'a' , 'b' , 'c' ] ] , dtype= "object" ) . str [ 1 ]
0 _
1 b
dtype: object
(b)其他參數
expand參數控制了是否將列拆開,n參數代表最多分割多少次
s. str . split( '_' , expand= True )
0
1
2
0
a
b
c
1
c
d
e
2
<NA>
<NA>
<NA>
3
f
g
h
s. str . split( '_' , n= 1 )
0 [a, b_c]
1 [c, d_e]
2 <NA>
3 [f, g_h]
dtype: object
s. str . split( '_' , expand= True , n= 1 )
0
1
0
a
b_c
1
c
d_e
2
<NA>
<NA>
3
f
g_h
2. str.cat方法
(a)不同對象的拼接模式
cat方法對於不同對象的作用結果並不相同,其中的對象包括:單列、雙列、多列
① 對於單個Series而言,就是指所有的元素進行字符合併爲一個字符串
s = pd. Series( [ 'ab' , None , 'd' ] , dtype= 'string' )
s
0 ab
1 <NA>
2 d
dtype: string
s. str . cat( )
'abd'
其中可選sep分隔符參數,和缺失值替代字符na_rep參數
s. str . cat( sep= ',' )
'ab,d'
s. str . cat( sep= ',' , na_rep= '*' )
'ab,*,d'
② 對於兩個Series合併而言,是對應索引的元素進行合併
s2 = pd. Series( [ '24' , None , None ] , dtype= 'string' )
s2
0 24
1 <NA>
2 <NA>
dtype: string
s. str . cat( s2)
0 ab24
1 <NA>
2 <NA>
dtype: string
同樣也有相應參數,需要注意的是兩個缺失值會被同時替換
s. str . cat( s2, sep= ',' , na_rep= '*' )
0 ab,24
1 *,*
2 d,*
dtype: string
③ 多列拼接可以分爲表的拼接和多Series拼接
表的拼接
s. str . cat( pd. DataFrame( { 0 : [ '1' , '3' , '5' ] , 1 : [ '5' , 'b' , None ] } , dtype= 'string' ) , na_rep= '*' )
0 ab15
1 *3b
2 d5*
dtype: string
多個Series拼接
s. str . cat( [ s+ '0' , s* 2 ] )
0 abab0abab
1 <NA>
2 dd0dd
dtype: string
(b)cat中的索引對齊
當前版本中,如果兩邊合併的索引不相同且未指定join參數,默認爲左連接,設置join=‘left’
s2 = pd. Series( list ( 'abc' ) , index= [ 1 , 2 , 3 ] , dtype= 'string' )
s2
1 a
2 b
3 c
dtype: string
s. str . cat( s2, na_rep= '*' )
0 ab*
1 *a
2 db
dtype: string
三、替換
廣義上的替換,就是指str.replace函數的應用,fillna是針對缺失值的替換,上一章已經提及
提到替換,就不可避免地接觸到正則表達式,這裏默認讀者已掌握常見正則表達式知識點,若對其還不瞭解的,可以通過這份資料 來熟悉
1. str.replace的常見用法
s = pd. Series( [ 'A' , 'B' , 'C' , 'Aaba' , 'Baca' , '' , np. nan, 'CABA' , 'dog' , 'cat' ] , dtype= "string" )
s
0 A
1 B
2 C
3 Aaba
4 Baca
5
6 <NA>
7 CABA
8 dog
9 cat
dtype: string
第一個值寫r開頭的正則表達式,後一個寫替換的字符串
s. str . replace( r'^[AB]' , '***' )
0 ***
1 ***
2 C
3 ***aba
4 ***aca
5
6 <NA>
7 CABA
8 dog
9 cat
dtype: string
2. 子組與函數替換
通過正整數調用子組(0返回字符本身,從1開始纔是子組)
https://blog.csdn.net/yg_2012/article/details/75426842
group的意思是你的正則表達式是由好多組組成的,然後用字符串去匹配這個表達式,group(1)指的是匹配到了正則表達式第一組的子串是什麼,group(2)是指匹配到了正則表達式第二組的子串是什麼, groups()就是由所有子串組成的集合。
PS:下面看下正則表達式 \w \s \d \b
. 匹配除換行符以外的任意字符
\w 匹配字母或數字或下劃線或漢字 等價於 ‘[^A-Za-z0-9_]’。
\s 匹配任意的空白符
\d 匹配數字
\b 匹配單詞的開始或結束
^ 匹配字符串的開始
$ 匹配字符串的結束
\w能不能匹配漢字要視你的操作系統和你的應用環境而定
s. str . replace( r'([ABC])(\w+)' , lambda x: x. group( 2 ) [ 1 : ] + '*' )
0 A
1 B
2 C
3 ba*
4 ca*
5
6 <NA>
7 BA*
8 dog
9 cat
dtype: string
s. str . replace( r'([ABC])(\w+)' , lambda x: print ( x, ' ' , x. group( 2 ) ) )
<_sre.SRE_Match object; span=(0, 4), match='Aaba'> aba
<_sre.SRE_Match object; span=(0, 4), match='Baca'> aca
<_sre.SRE_Match object; span=(0, 4), match='CABA'> ABA
0 A
1 B
2 C
3
4
5
6 <NA>
7
8 dog
9 cat
dtype: string
利用?P<…>表達式可以對子組命名調用
s. str . replace( r'(?P<one>[ABC])(?P<two>\w+)' , lambda x: x. group( 'two' ) [ 1 : ] + '*' )
0 A
1 B
2 C
3 ba*
4 ca*
5
6 <NA>
7 BA*
8 dog
9 cat
dtype: string
3. 關於str.replace的注意事項
首先,要明確str.replace和replace並不是一個東西:
str.replace針對的是object類型或string類型,默認是以正則表達式爲操作,目前暫時不支持DataFrame上使用
replace針對的是任意類型的序列或數據框,如果要以正則表達式替換,需要設置regex=True,該方法通過字典可支持多列替換
但現在由於string類型的初步引入,用法上出現了一些問題,這些issue有望在以後的版本中修復
(a)str.replace賦值參數不得爲pd.NA
這聽上去非常不合理,例如對滿足某些正則條件的字符串替換爲缺失值,直接更改爲缺失值在當下版本就會報錯
此時,可以先轉爲object類型再轉換回來,曲線救國:
pd. Series( [ 'A' , 'B' ] , dtype= 'string' ) . astype( 'O' ) . replace( r'[A]' , pd. NA, regex= True ) . astype( 'string' )
0 <NA>
1 B
dtype: string
至於爲什麼不用replace函數的regex替換(但string類型replace的非正則替換是可以的),原因在下面一條
(b)對於string類型Series,在使用replace函數時不能使用正則表達式替換
該bug現在還未修復
pd. Series( [ 'A' , 'B' ] , dtype= 'string' ) . replace( r'[A]' , 'C' , regex= True )
0 A
1 B
dtype: string
pd. Series( [ 'A' , 'B' ] , dtype= 'O' ) . replace( r'[A]' , 'C' , regex= True )
0 C
1 B
dtype: object
(c)string類型序列如果存在缺失值,不能使用replace替換
pd. Series( [ 'A' , np. nan] , dtype= 'string' ) . str . replace( 'A' , 'B' )
0 B
1 <NA>
dtype: string
綜上,概況的說,除非需要賦值元素爲缺失值(轉爲object再轉回來),否則請使用str.replace方法
四、子串匹配與提取
1. str.extract方法
(a)常見用法
pd. Series( [ '10-87' , '10-88' , '10-89' ] , dtype= "string" ) . str . extract( r'([\d]{2})-([\d]{2})' )
0
1
0
10
87
1
10
88
2
10
89
使用子組名作爲列名
pd. Series( [ '10-87' , '10-88' , '-89' ] , dtype= "string" ) . str . extract( r'(?P<name_1>[\d]{2})-(?P<name_2>[\d]{2})' )
name_1
name_2
0
10
87
1
10
88
2
<NA>
<NA>
利用?正則標記選擇部分提取
pd. Series( [ '10-87' , '10-88' , '-89' ] , dtype= "string" ) . str . extract( r'(?P<name_1>[\d]{2})?-(?P<name_2>[\d]{2})' )
name_1
name_2
0
10
87
1
10
88
2
<NA>
89
pd. Series( [ '10-87' , '10-88' , '10-' ] , dtype= "string" ) . str . extract( r'(?P<name_1>[\d]{2})-(?P<name_2>[\d]{2})?' )
name_1
name_2
0
10
87
1
10
88
2
10
<NA>
(b)expand參數(默認爲True)
對於一個子組的Series,如果expand設置爲False,則返回Series,若大於一個子組,則expand參數無效,全部返回DataFrame
對於一個子組的Index,如果expand設置爲False,則返回提取後的Index,若大於一個子組且expand爲False,報錯
s = pd. Series( [ "a1" , "b2" , "c3" ] , [ "A11" , "B22" , "C33" ] , dtype= "string" )
s. index
Index(['A11', 'B22', 'C33'], dtype='object')
s. str . extract( r'([\w])' )
s. str . extract( r'([\w])' , expand= False )
A11 a
B22 b
C33 c
dtype: string
s. index. str . extract( r'([\w])' )
s. index. str . extract( r'([\w])' , expand= False )
Index(['A', 'B', 'C'], dtype='object')
s. index. str . extract( r'([\w])([\d])' )
2. str.extractall方法
與extract只匹配第一個符合條件的表達式不同,extractall會找出所有符合條件的字符串,並建立多級索引(即使只找到一個)
s = pd. Series( [ "a1a2" , "b1" , "c1" ] , index= [ "A" , "B" , "C" ] , dtype= "string" )
two_groups = '(?P<letter>[a-z])(?P<digit>[0-9])'
s. str . extract( two_groups, expand= True )
letter
digit
A
a
1
B
b
1
C
c
1
s. str . extractall( two_groups)
letter
digit
match
A
0
a
1
1
a
2
B
0
b
1
C
0
c
1
s[ 'A' ] = 'a1'
s. str . extractall( two_groups)
letter
digit
match
A
0
a
1
B
0
b
1
C
0
c
1
如果想查看第i層匹配,可使用xs方法
s = pd. Series( [ "a1a2" , "b1b2" , "c1c2" ] , index= [ "A" , "B" , "C" ] , dtype= "string" )
s. str . extractall( two_groups) . xs( 1 , level= 'match' )
letter
digit
A
a
2
B
b
2
C
c
2
3. str.contains和str.match
前者的作用爲檢測是否包含某種正則模式
pd. Series( [ '1' , None , '3a' , '3b' , '03c' ] , dtype= "string" ) . str . contains( r'[0-9][a-z]' )
0 False
1 <NA>
2 True
3 True
4 True
dtype: boolean
可選參數爲na
pd. Series( [ '1' , None , '3a' , '3b' , '03c' ] , dtype= "string" ) . str . contains( 'a' , na= False )
0 False
1 False
2 True
3 False
4 False
dtype: boolean
str.match與其區別在於,match依賴於python的re.match,檢測內容爲是否從頭開始包含該正則模式
pd. Series( [ '1' , None , '3a_' , '3b' , '03c' ] , dtype= "string" ) . str . match( r'[0-9][a-z]' , na= False )
0 False
1 False
2 True
3 True
4 False
dtype: boolean
pd. Series( [ '1' , None , '_3a' , '3b' , '03c' ] , dtype= "string" ) . str . match( r'[0-9][a-z]' , na= False )
0 False
1 False
2 False
3 True
4 False
dtype: boolean
五、常用字符串方法
1. 過濾型方法
(a)str.strip
常用於過濾空格
pd. Series( list ( 'abc' ) , index= [ ' space1 ' , 'space2 ' , ' space3' ] , dtype= "string" ) . index. str . strip( )
Index(['space1', 'space2', 'space3'], dtype='object')
(b)str.lower和str.upper
pd. Series( 'A' , dtype= "string" ) . str . lower( )
0 a
dtype: string
pd. Series( 'a' , dtype= "string" ) . str . upper( )
0 A
dtype: string
(c)str.swapcase和str.capitalize
分別表示交換字母大小寫和大寫首字母
pd. Series( 'abCD' , dtype= "string" ) . str . swapcase( )
0 ABcd
dtype: string
pd. Series( 'abCD' , dtype= "string" ) . str . capitalize( )
0 Abcd
dtype: string
2. isnumeric方法
檢查每一位是否都是數字,請問如何判斷是否是數值?(問題二)
pd. Series( [ '1.2' , '1' , '-0.3' , 'a' , np. nan] , dtype= "string" ) . str . isnumeric( )
0 False
1 True
2 False
3 False
4 <NA>
dtype: boolean
pd. Series( [ '1.2' , '1' , '-0.3' , 'a' , np. nan, 1 ] ) . apply ( lambda x: True if type ( x) in [ float , int ] and x== x else False )
0 False
1 False
2 False
3 False
4 False
5 True
dtype: bool
六、問題與練習
1. 問題
【問題一】 str對象方法和df/Series對象方法有什麼區別?
Pandas 爲 Series 提供了 str 屬性,通過它可以方便的對每個元素進行操作。
在對 Series 中每個元素處理時,我們可以使用 map 或 apply 方法。
比如,我想要將每個城市都轉爲小寫,可以使用如下的方式。
user_info.city.map(lambda x: x.lower())#報錯
錯誤原因是因爲 float 類型的對象沒有 lower 屬性。這是因爲缺失值(np.nan)屬於float 類型。
這時候我們的 str 屬性操作來了
user_info.city.str.lower()
可以看到,通過 str 屬性來訪問之後用到的方法名與 Python 內置的字符串的方法名一樣。並且能夠自動排除缺失值。
from https://zhuanlan.zhihu.com/p/38603837
個人理解str對象方法更多的是對於Series中的每個小的元素進行處理的時候,把每個小元素當成字符串進行處理,並且方法名也跟python內置的字符串的方法名一樣。
【問題二】 給出一列string類型,如何判斷單元格是否是數值型數據?
pd. Series( [ '1.2' , '1' , '-0.3' , 'a' , np. nan, 1 ] ) . apply ( lambda x: True if type ( x) in [ float , int ] and x== x else False )
0 False
1 False
2 False
3 False
4 False
5 True
dtype: bool
【問題三】 rsplit方法的作用是什麼?它在什麼場合下適用?
split()正序分割列;rsplit()逆序分割列
s= pd. Series( [ ' 11 111 111 第一大類' , ' 12 第一大類' , ' 21 第二大類' , ' 22 第二大類' ] )
s. str . rsplit( expand= True , n= 1 )
0
1
0
11 111 111
第一大類
1
12
第一大類
2
21
第二大類
3
22
第二大類
rsplit()是逆序分割,當我們最後面有幾列需要單獨分割出來,而且由於列數比較多從正着數不知道第幾列或者是每一行列數都不一樣而我們只需要最後一列,再這種情況下感覺是需要用到rsplit()的。
【問題四】 在本章的第二到第四節分別介紹了字符串類型的5類操作,請思考它們各自應用於什麼場景?
拆分.str.split()
可能應用於將單獨的一列分成好幾列,比如講日期中的年份一列單獨拿出來方便後面做分析
拼接.str.cat()
將幾列拼接起來生成新的一列,有的時候可能根據幾列的信息整理出來一個新的重要的信息單獨成一列
替換.str.replace()
在每個元素中查找對應的元素,然後進行替換,可能在對於數據整體進行更新的時候可能會用到替換
匹配.str.contains() .str.match()
提取.str.extract() .str.extractall()
查找每個元素中特定的正則表達式的格式的內容並提取出來
2. 練習
【練習一】 現有一份關於字符串的數據集,請解決以下問題:
(a)現對字符串編碼存儲人員信息(在編號後添加ID列),使用如下格式:“×××(名字):×國人,性別×,生於×年×月×日”
pd. read_csv( 'data/String_data_one.csv' , index_col= '人員編號' ) . head( )
姓名
國籍
性別
出生年
出生月
出生日
人員編號
1
aesfd
2
男
1942
8
10
2
fasefa
5
女
1985
10
4
3
aeagd
4
女
1946
10
15
4
aef
4
男
1999
5
13
5
eaf
1
女
2010
6
24
a= pd. read_csv( 'data/String_data_one.csv' , index_col= '人員編號' )
a[ 'ID' ] = a[ '姓名' ]
a[ 'ID' ] = a[ 'ID' ] . str . cat( [ "(名字)" + ':' + a[ '國籍' ] . astype( str ) . astype( 'string' ) + '國人,性別' + a[ '性別' ] + ',生於' + a[ '出生年' ] . astype( str ) . astype( 'string' ) + '年' + a[ '出生月' ] . astype( str ) . astype( 'string' ) + '月' + a[ '出生日' ] . astype( str ) . astype( 'string' ) + '日' ] )
a. head( )
姓名
國籍
性別
出生年
出生月
出生日
ID
人員編號
1
aesfd
2
男
1942
8
10
aesfd(名字):2國人,性別男,生於1942年8月10日
2
fasefa
5
女
1985
10
4
fasefa(名字):5國人,性別女,生於1985年10月4日
3
aeagd
4
女
1946
10
15
aeagd(名字):4國人,性別女,生於1946年10月15日
4
aef
4
男
1999
5
13
aef(名字):4國人,性別男,生於1999年5月13日
5
eaf
1
女
2010
6
24
eaf(名字):1國人,性別女,生於2010年6月24日
參考答案
df = pd. read_csv( 'data/String_data_one.csv' , index_col= '人員編號' ) . astype( 'str' )
( df[ '姓名' ] + ':' + df[ '國籍' ] + '國人,性別'
+ df[ '性別' ] + ',生於'
+ df[ '出生年' ] + '年'
+ df[ '出生月' ] + '月' + df[ '出生日' ] + '日' ) . to_frame( ) . rename( columns= { 0 : 'ID' } ) . head( )
ID
人員編號
1
aesfd:2國人,性別男,生於1942年8月10日
2
fasefa:5國人,性別女,生於1985年10月4日
3
aeagd:4國人,性別女,生於1946年10月15日
4
aef:4國人,性別男,生於1999年5月13日
5
eaf:1國人,性別女,生於2010年6月24日
(b)將(a)中的人員生日信息部分修改爲用中文表示(如一九七四年十月二十三日),其餘返回格式不變。
def f ( s) :
map = { '0' : '零' , '1' : '一' , '2' : '二' , '3' : '三' , '4' : '四' , '5' : '五' , '6' : '六' , '7' : '七' , '8' : '八' , '9' : '九' , '10' : '十' , '11' : '十一' , '12' : '十二' }
re= ''
for i in s:
re+= map [ i]
return re
def f2 ( s) :
map = { '0' : '零' , '1' : '一' , '2' : '二' , '3' : '三' , '4' : '四' , '5' : '五' , '6' : '六' , '7' : '七' , '8' : '八' , '9' : '九' , '10' : '十' , '11' : '十一' , '12' : '十二' }
return map [ s]
def f3 ( s) :
map = { '0' : '零' , '1' : '一' , '2' : '二' , '3' : '三' , '4' : '四' , '5' : '五' , '6' : '六' , '7' : '七' , '8' : '八' , '9' : '九' , '10' : '十' , '11' : '十一' , '12' : '十二' }
if len ( s) == 1 :
return map [ s]
elif s[ 1 ] == '0' :
return map [ s[ 0 ] ] + '十'
else :
return map [ s[ 0 ] ] + '十' + map [ s[ 1 ] ]
a= pd. read_csv( 'data/String_data_one.csv' , index_col= '人員編號' )
a[ 'ID' ] = a[ '姓名' ]
a[ 'ID' ] = a[ 'ID' ] . str . cat( [ ':' + a[ '國籍' ] . astype( str ) . astype( 'string' ) + '國人,性別' + a[ '性別' ] + ',生於' + a[ '出生年' ] . astype( str ) . astype( 'string' ) . apply ( lambda x: f( x) ) + '年' + a[ '出生月' ] . astype( str ) . astype( 'string' ) . apply ( lambda x: f2( x) ) + '月' + a[ '出生日' ] . astype( str ) . astype( 'string' ) . apply ( lambda x: f3( x) ) + '日' ] )
a. head( )
姓名
國籍
性別
出生年
出生月
出生日
ID
人員編號
1
aesfd
2
男
1942
8
10
aesfd:2國人,性別男,生於一九四二年八月一十日
2
fasefa
5
女
1985
10
4
fasefa:5國人,性別女,生於一九八五年十月四日
3
aeagd
4
女
1946
10
15
aeagd:4國人,性別女,生於一九四六年十月一十五日
4
aef
4
男
1999
5
13
aef:4國人,性別男,生於一九九九年五月一十三日
5
eaf
1
女
2010
6
24
eaf:1國人,性別女,生於二零一零年六月二十四日
參考答案
L_year = list ( '零一二三四五六七八九' )
L_one = [ s. strip( ) for s in list ( ' 二三四五六七八九' ) ]
L_two = [ s. strip( ) for s in list ( ' 一二三四五六七八九' ) ]
df_new = ( df[ '姓名' ] + ':' + df[ '國籍' ] + '國人,性別' + df[ '性別' ] + ',生於'
+ df[ '出生年' ] . str . replace( r'\d' , lambda x: L_year[ int ( x. group( 0 ) ) ] ) + '年'
+ df[ '出生月' ] . apply ( lambda x: x if len ( x) == 2 else '0' + x) \
. str . replace( r'(?P<one>[\d])(?P<two>\d?)' , lambda x: L_one[ int ( x. group( 'one' ) ) ]
+ bool ( int ( x. group( 'one' ) ) ) * '十' + L_two[ int ( x. group( 'two' ) ) ] ) + '月'
+ df[ '出生日' ] . apply ( lambda x: x if len ( x) == 2 else '0' + x) \
. str . replace( r'(?P<one>[\d])(?P<two>\d?)' , lambda x: L_one[ int ( x. group( 'one' ) ) ]
+ bool ( int ( x. group( 'one' ) ) ) * '十' + L_two[ int ( x. group( 'two' ) ) ] ) + '日' ) \
. to_frame( ) . rename( columns= { 0 : 'ID' } )
df_new. head( )
ID
人員編號
1
aesfd:2國人,性別男,生於一九四二年八月十日
2
fasefa:5國人,性別女,生於一九八五年十月四日
3
aeagd:4國人,性別女,生於一九四六年十月十五日
4
aef:4國人,性別男,生於一九九九年五月十三日
5
eaf:1國人,性別女,生於二零一零年六月二十四日
(c)將(b)中的ID列結果拆分爲原列表相應的5列,並使用equals檢驗是否一致。
re= a[ 'ID' ] . str . extract( r'(?P<姓名>[a-zA-Z]{1,}):(?P<國籍>[\d])國人,性別(?P<性別>[\w]),生於(?P<出生年>[\w]{4})年(?P<出生月>[\w]+)月(?P<出生日>[\w]+)日' )
def f11 ( s) :
map = { '零' : '0' , '一' : '1' , '二' : '2' , '三' : '3' , '四' : '4' , '五' : '5' , '六' : '6' , '七' : '7' , '八' : '8' , '九' : '9' , '十' : '10' }
re= ''
for i in s:
re+= map [ i]
return re
def f22 ( s) :
map = { '零' : '0' , '一' : '1' , '二' : '2' , '三' : '3' , '四' : '4' , '五' : '5' , '六' : '6' , '七' : '7' , '八' : '8' , '九' : '9' , '十' : '10' , '十一' : '11' , '十二' : '12' }
return map [ s]
def f33 ( s) :
re= ''
if len ( s) >= 2 and s[ - 2 ] == '十' :
map = { '零' : '0' , '一' : '1' , '二' : '2' , '三' : '3' , '四' : '4' , '五' : '5' , '六' : '6' , '七' : '7' , '八' : '8' , '九' : '9' , '十' : '' }
for i in s:
re+= map [ i]
return re
elif s[ - 1 ] == '十' :
map = { '零' : '0' , '一' : '1' , '二' : '2' , '三' : '3' , '四' : '4' , '五' : '5' , '六' : '6' , '七' : '7' , '八' : '8' , '九' : '9' , '十' : '0' }
for i in s:
re+= map [ i]
return re
else :
map = { '零' : '0' , '一' : '1' , '二' : '2' , '三' : '3' , '四' : '4' , '五' : '5' , '六' : '6' , '七' : '7' , '八' : '8' , '九' : '9' , '十' : '10' }
re= ''
for i in s:
re+= map [ i]
return re
re[ '出生年' ] = re[ '出生年' ] . apply ( lambda x: f11( x) )
re[ '出生月' ] = re[ '出生月' ] . apply ( lambda x: f22( x) )
re[ '出生日' ] = re[ '出生日' ] . apply ( lambda x: f33( x) )
re. head( )
姓名
國籍
性別
出生年
出生月
出生日
人員編號
1
aesfd
2
男
1942
8
10
2
fasefa
5
女
1985
10
4
3
aeagd
4
女
1946
10
15
4
aef
4
男
1999
5
13
5
eaf
1
女
2010
6
24
test= pd. read_csv( 'data/String_data_one.csv' , index_col= '人員編號' ) . astype( str )
re. equals( test)
True
test. info( )
<class 'pandas.core.frame.DataFrame'>
Int64Index: 2000 entries, 1 to 2000
Data columns (total 6 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 姓名 2000 non-null object
1 國籍 2000 non-null object
2 性別 2000 non-null object
3 出生年 2000 non-null object
4 出生月 2000 non-null object
5 出生日 2000 non-null object
dtypes: object(6)
memory usage: 109.4+ KB
re. info( )
<class 'pandas.core.frame.DataFrame'>
Int64Index: 2000 entries, 1 to 2000
Data columns (total 6 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 姓名 2000 non-null object
1 國籍 2000 non-null object
2 性別 2000 non-null object
3 出生年 2000 non-null object
4 出生月 2000 non-null object
5 出生日 2000 non-null object
dtypes: object(6)
memory usage: 109.4+ KB
參考答案
dic_year = { i[ 0 ] : i[ 1 ] for i in zip ( list ( '零一二三四五六七八九' ) , list ( '0123456789' ) ) }
dic_two = { i[ 0 ] : i[ 1 ] for i in zip ( list ( '十一二三四五六七八九' ) , list ( '0123456789' ) ) }
dic_one = { '十' : '1' , '二十' : '2' , '三十' : '3' , None : '' }
df_res = df_new[ 'ID' ] . str . extract( r'(?P<姓名>[a-zA-Z]+):(?P<國籍>[\d])國人,性別(?P<性別>[\w]),生於(?P<出生年>[\w]{4})年(?P<出生月>[\w]+)月(?P<出生日>[\w]+)日' )
df_res[ '出生年' ] = df_res[ '出生年' ] . str . replace( r'(\w)+' , lambda x: '' . join( [ dic_year[ x. group( 0 ) [ i] ] for i in range ( 4 ) ] ) )
df_res[ '出生月' ] = df_res[ '出生月' ] . str . replace( r'(?P<one>\w?十)?(?P<two>[\w])' , lambda x: dic_one[ x. group( 'one' ) ] + dic_two[ x. group( 'two' ) ] ) . str . replace( r'0' , '10' )
df_res[ '出生日' ] = df_res[ '出生日' ] . str . replace( r'(?P<one>\w?十)?(?P<two>[\w])' , lambda x: dic_one[ x. group( 'one' ) ] + dic_two[ x. group( 'two' ) ] ) . str . replace( r'^0' , '10' )
df_res. head( )
姓名
國籍
性別
出生年
出生月
出生日
人員編號
1
aesfd
2
男
1942
8
10
2
fasefa
5
女
1985
10
4
3
aeagd
4
女
1946
10
15
4
aef
4
男
1999
5
13
5
eaf
1
女
2010
6
24
【練習二】 現有一份半虛擬的數據集,第一列包含了新型冠狀病毒的一些新聞標題,請解決以下問題:
(a)選出所有關於北京市和上海市新聞標題的所在行。
pd. read_csv( 'data/String_data_two.csv' ) . head( )
col1
col2
col3
0
鄂爾多斯市第2例確診患者治癒出院
19
363.6923
1
雲南新增2例,累計124例
-67
-152.281
2
武漢協和醫院14名感染醫護出院
-86
325.6221
3
山東新增9例,累計307例
-74
-204.9313
4
上海開學日期延至3月
-95
4.05
a= pd. read_csv( 'data/String_data_two.csv' ) . convert_dtypes( )
a[ a[ 'col1' ] . str . contains( r'上海' ) | a[ 'col1' ] . str . contains( r'北京' ) ] . head( )
col1
col2
col3
4
上海開學日期延至3月
-95
4.05
5
北京新增25例確診病例,累計確診253例
-4
-289.1719
6
上海新增10例,累計243例
2
-73.7105
36
上海新增14例累計233例
-55
-83
40
上海新增14例累計233例
-88
-99
參考答案
df = pd. read_csv( 'data/String_data_two.csv' )
df. head( )
df[ df[ 'col1' ] . str . contains( r'[北京]{2}|[上海]{2}' ) ] . head( )
col1
col2
col3
4
上海開學日期延至3月
-95
4.05
5
北京新增25例確診病例,累計確診253例
-4
-289.1719
6
上海新增10例,累計243例
2
-73.7105
36
上海新增14例累計233例
-55
-83
40
上海新增14例累計233例
-88
-99
(b)求col2的均值。
b= pd. read_csv( 'data/String_data_two.csv' ) . convert_dtypes( )
b[ 'col2' ] [ ~ b[ 'col2' ] . str . contains( r'^-?\d+$' ) ]
309 0-
396 9`
485 /7
Name: col2, dtype: string
b[ 'col2' ] [ ~ b[ 'col2' ] . str . contains( r'^-?\d+$' ) ] = [ '0' , '9' , '7' ]
b[ 'col2' ] . apply ( lambda x: int ( x) ) . mean( )
-0.984
參考答案
df[ 'col2' ] [ ~ ( df[ 'col2' ] . str . replace( r'-?\d+' , 'True' ) == 'True' ) ]
309 0-
396 9`
485 /7
Name: col2, dtype: object
df. loc[ [ 309 , 396 , 485 ] , 'col2' ] = [ 0 , 9 , 7 ]
df[ 'col2' ] . astype( 'int' ) . mean( )
-0.984
(c)求col3的均值。
c= pd. read_csv( 'data/String_data_two.csv' ) . convert_dtypes( )
c. columns
Index(['col1', 'col2', 'col3 '], dtype='object')
c. info( )
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 500 entries, 0 to 499
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 col1 500 non-null string
1 col2 500 non-null string
2 col3 500 non-null string
dtypes: string(3)
memory usage: 11.8 KB
c[ 'col3 ' ] [ ~ c[ 'col3 ' ] . str . contains( r'^-?\d+(.)?(\d+)?$' ) ] = [ '355.3567' , '9056.2253' , '3534.6554' ]
c[ 'col3 ' ] . astype( 'float' ) . mean( )
F:\dev\anaconda\envs\python35\lib\site-packages\pandas\core\strings.py:1954: UserWarning: This pattern has match groups. To actually get the groups, use str.extract.
return func(self, *args, **kwargs)
24.707484999999988
參考答案
df. columns = df. columns. str . strip( )
df. columns
Index(['col1', 'col2', 'col3'], dtype='object')
df[ 'col3' ] [ ~ ( df[ 'col3' ] . str . replace( r'-?\d+\.?\d+' , 'True' ) == 'True' ) ]
28 355`.3567
37 -5
73 1
122 9056.\2253
332 3534.6554{
370 7
Name: col3, dtype: object
df. loc[ [ 28 , 122 , 332 ] , 'col3' ] = [ 355.3567 , 9056.2253 , 3534.6554 ]
df[ 'col3' ] . astype( 'float' ) . mean( )
24.707484999999988