pandas資料規整化:清理、轉換、合併、重塑
許多人選擇使用通用程式語言或unix文字處理工具(如sed或awk)對資料格式進行專門處理。
幸運的是,pandas和python標準庫提供了一組高階的、靈活的、高效的核心函式和演算法,將資料規整化正確的形式。
1. 合併資料集:
pandas物件中的資料可以通過一些內建的方式進行合併:
(1)pandas.merge可根據一個名多個鍵將不同DataFrame中的行連線起來。sql關係型資料庫也有merge法,它是用merge實現的就是資料庫的連線操作(inner內連線交集,outer外連線,左連線,右連線=>並集)。
(2)pandas.concat可以沿著一條軸將多個物件堆疊到一起。
(3)例項方法combine_first可以將重複資料編接在一起,用一個物件中的值填充另一個物件中的缺失值。
下面分別講解這些方法:
資料庫風格的DataFrame合併:
資料集的合併(merge)或連線(join)運算是通過一個或多個鍵將行連線起來的。這些運算是關係型資料庫的核心。
pandas的merge函式是對資料應用這些演算法的主要切入點。
(1)merge的用法
import pandas as pd import numpy as np from pandas import Series,DataFrame df1=DataFrame({'key':['b','b','a','c','a','a','b'], 'data1':range(7)}) df2=DataFrame({'key':['a','b','d'], 'data2':range(3)}) print df1 #輸出結果如下: # data1 key # 0 0 b # 1 1 b # 2 2 a # 3 3 c # 4 4 a # 5 5 a # 6 6 b # print df2 # #輸出結果如下: # data2 key # 0 0 a # 1 1 b # 2 2 d (1)這是一種多對一的合併:df1中的資料有多個被標記為a和b行,而df2中key列的每個值則僅對應一行。 這些物件呼叫merge即可得到 print pd.merge(df1,df2) #輸出結果如下: # data1 key data2 # 0 0 b 1 # 1 1 b 1 # 2 6 b 1 # 3 2 a 0 # 4 4 a 0 # 5 5 a 0 # #注意:上面的例子並沒有指明要用哪個列進行連線。如果沒有指定,merge就會將重疊列的列名當做鍵。 (2)下面指定列:用能數on= # print pd.merge(df1,df2,on='key') #輸出結果與上面一樣 (3)如果兩個物件的列名不同,也可以分別進行指定:left_on,right_on df3=DataFrame({'lkey':['b','b','a','c','a','a','b'], 'data1':range(7)}) df4=DataFrame({'rkey':['a','b','d'], 'data2':range(3)}) print pd.merge(df3,df4,left_on='lkey',right_on='rkey') #輸出結果如下: # data1 lkey data2 rkey # 0 0 b 1 b # 1 1 b 1 b # 2 6 b 1 b # 3 2 a 0 a # 4 4 a 0 a # 5 5 a 0 a # #注意:結果裡面c和d以及與之相關的資料消失了。預設情況下,merge做的是"inner"連線:結果中的鍵是交集。 # 而其它外連線left,right,outer求取的是鍵的並集,組合了左連線和右連線的效果。 (4) outer外連線:並集,兩個資料Key都要 # print pd.merge(df1,df2,how='outer') #外連線,兩邊的key都要傳入 # #輸出結果如下:並集,df1,df2所有的值都放入 # # data1 key data2 # # 0 0.0 b 1.0 # # 1 1.0 b 1.0 # # 2 6.0 b 1.0 # # 3 2.0 a 0.0 # # 4 4.0 a 0.0 # # 5 5.0 a 0.0 # # 6 3.0 c NaN # # 7 NaN d 2.0 (5) left左連線:並集,左連線是以左邊的為主 # print pd.merge(df1,df2,on='key',how='left') #左連線 # #輸出結果如下: 左連線是以左邊的為主,即df1為主,顧只有df1的key # # data1 key data2 # # 0 0 b 1.0 # # 1 1 b 1.0 # # 2 2 a 0.0 # # 3 3 c NaN # # 4 4 a 0.0 # # 5 5 a 0.0 # # 6 6 b 1.0 (6)right右連線:並集,右連線是以右邊的為主 # print pd.merge(df1,df2,how='right') #右連線 # #輸出結果如下:右連線,以df2的為主,顧只有df2的key,其它的值是全部寫入 # # data1 key data2 # # 0 0.0 b 1 # # 1 1.0 b 1 # # 2 6.0 b 1 # # 3 2.0 a 0 # # 4 4.0 a 0 # # 5 5.0 a 0 # # 6 NaN d 2 (7)inner內連線:交集,預設就是內連線 # print pd.merge(df1,df2,how='inner') #即merge預設情況下是inner,inner是交集 # #輸出結果如下:取df1,df2都有key # # data1 key data2 # # 0 0 b 1 # # 1 1 b 1 # # 2 6 b 1 # # 3 2 a 0 # # 4 4 a 0 # # 5 5 a 0 (8)若多個鍵要進行合併,傳入一個由列名組成的列表即可 left=DataFrame({'key1':['foo','foo','bar'], 'key2':['one','two','one'], 'lval':[1,2,3]}) right=DataFrame({'key1':['foo','foo','bar','bar'], 'key2':['one','one','one','two'], 'rval':[4,5,6,7]}) print left #輸出結果如下: # key1 key2 lval # 0 foo one 1 # 1 foo two 2 # 2 bar one 3 print right #輸出結果如下: # key1 key2 rval # 0 foo one 4 # 1 foo one 5 # 2 bar one 6 # 3 bar two 7 print pd.merge(left,right,on=['key1','key2'],how='outer') #輸出結果如下:看key1,key2兩個鍵對應的lval的值。外連線,即所有的key都要。 #right中的key組合[foo,one],[foo,one],[foo,two]...用right的key看left有沒有。 # key1 key2 lval rval # 0 foo one 1.0 4.0 # 1 foo one 1.0 5.0 # 2 foo two 2.0 NaN # 3 bar one 3.0 6.0 # 4 bar two NaN 7.0 (9)合併運算需要考慮對重複列名的處理,merge有一個實用的suffixes選項,用於指定附加到左右兩個DataFrame物件的重疊列名上的字串。 print pd.merge(left,right,on='key1') #只用單個key,它就有單個key1分別和left,right的key2的組合 #輸出結果如下: # key1 key2_x lval key2_y rval # 0 foo one 1 one 4 # 1 foo one 1 one 5 # 2 foo two 2 one 4 # 3 foo two 2 one 5 # 4 bar one 3 one 6 # 5 bar one 3 two 7 print pd.merge(left,right,on='key1',suffixes=('_left','_right')) #輸出結果如下: # key1 key2_left lval key2_right rval # 0 foo one 1 one 4 # 1 foo one 1 one 5 # 2 foo two 2 one 4 # 3 foo two 2 one 5 # 4 bar one 3 one 6 # 5 bar one 3 two 7
merger函式的引數
引數 說明
left 參與合併的左側DataFrame
right 參與合併的右側DataFrame
how “inner”、“outer”、“left”、“right”其中之一。預設為"inner"(交集),其它為並集
on 用於連線的列名。必須存在於左右兩個DataFrame物件中。如果未指定,且其它連線鍵也未指定,由以
left和right列名的交集作為連線鍵
left_on 左側DataFrame中用作連線鍵的列
right_on 右側DataFrame中用作連線鍵的列
left_index 將左側的行索引用作其連線鍵
righgt_index 將右側的行索引用作其連線鍵
sort
suffixes 字串值元組,用於追加到重疊列名的末尾,預設為('_x','_y').
copy 設定為False,可以在某些特殊情況下避免將資料複製到結果資料結構中。預設總是複製。
索引上DataFrame的合併:
DataFrame中的連線鍵位於其索引中。在這種情況下,可以傳入left_index=True或right_index=True(或兩個都傳)
以說明索引應該被用作連線鍵。
(1)merge的用法:索引上合併用left_index=True、right_index=True
import pandas as pd
import numpy as np
from pandas import Series,DataFrame
(1)DataFrame中的連線鍵位於其索引中:left_index=True或right_index=True(或兩個都傳)來說明索引應該被用作連線鍵。
left1=DataFrame({'key':['a','b','a','a','b','c'],
'value':range(6)})
right1=DataFrame({'group_val':[3.5,7]},index=['a','b'])
print left1
#輸出結果如下:
# key value
# 0 a 0
# 1 b 1
# 2 a 2
# 3 a 3
# 4 b 4
# 5 c 5
print right1
#輸出結果如下:
# group_val
# a 3.5
# b 7.0
print pd.merge(left1,right1,left_on='key',right_index=True)
#輸出結果如下:
# key value group_val
# 0 a 0 3.5
# 2 a 2 3.5
# 3 a 3 3.5
# 1 b 1 7.0
# 4 b 4 7.0
(2)由於預設的merge方法是求取連線鍵的交集,因此可以通過外連線的方式得到它們的並集。how='outer'
print pd.merge(left1,right1,left_on='key',right_index=True,how='outer')
#輸出結果如下:
# key value group_val
# 0 a 0 3.5
# 2 a 2 3.5
# 3 a 3 3.5
# 1 b 1 7.0
# 4 b 4 7.0
# 5 c 5 NaN
(3)層次化索引,多層行/列索引
lefth=DataFrame({'key1':['Ohio','Ohio','Ohio','Nevada','Nevada'],
'key2':[2000,2001,2002,2001,2002],
'data':np.arange(5.)})
righth=DataFrame(np.arange(12).reshape(6,2),
index=[['Nevada','Nevada','Ohio','Ohio','Ohio','Ohio'],
[2001,2000,2000,2000,2001,2002]],
columns=['event1','event2'])
print lefth
#輸出結果如下:
# data key1 key2
# 0 0.0 Ohio 2000
# 1 1.0 Ohio 2001
# 2 2.0 Ohio 2002
# 3 3.0 Nevada 2001
# 4 4.0 Nevada 2002
print righth
#輸出結果如下:
# event1 event2
# Nevada 2001 0 1
# 2000 2 3
# Ohio 2000 4 5
# 2000 6 7
# 2001 8 9
# 2002 10 11
#必須以列表的形式指明用作合併鍵的多個列(注意對重複索引值的處理)
#left_on左側DataFrame作為連線鍵,即lefth,而索引是right_index,即右側資料為行索引即righth的行索引為行索引
print pd.merge(lefth,righth,left_on=['key1','key2'],right_index=True)
#沒有how引數,預設是inner連線,即交集,取lefth的['key1','key2']的資料與righth的行有交集,即相同的取出
#輸出結果如下:
# data key1 key2 event1 event2
# 0 0.0 Ohio 2000 4 5
# 0 0.0 Ohio 2000 6 7
# 1 1.0 Ohio 2001 8 9
# 2 2.0 Ohio 2002 10 11
# 3 3.0 Nevada 2001 0 1
#(4)外連線:how='outer' 並集
print pd.merge(lefth,righth,left_on=['key1','key2'],right_index=True,how='outer')
#輸出結果如下:
# data key1 key2 event1 event2
# 0 0.0 Ohio 2000 4.0 5.0
# 0 0.0 Ohio 2000 6.0 7.0
# 1 1.0 Ohio 2001 8.0 9.0
# 2 2.0 Ohio 2002 10.0 11.0
# 3 3.0 Nevada 2001 0.0 1.0
# 4 4.0 Nevada 2002 NaN NaN
# 4 NaN Nevada 2000 2.0 3.0
#(5)用merge同時使用合併雙方的索引
left2=DataFrame([[1.,2.],[3.,4.],[5.,6.]],index=['a','c','e'],
columns=['Ohio','Nevada'])
right2=DataFrame([[7.,8.],[9.,10.],[11.,12.],[13,14]],
index=['b','c','d','e'],columns=['Missouri','Alabama'])
print left2
#輸出結果如下:
# Ohio Nevada
# a 1.0 2.0
# c 3.0 4.0
# e 5.0 6.0
print right2
#輸出結果如下:
# Missouri Alabama
# b 7.0 8.0
# c 9.0 10.0
# d 11.0 12.0
# e 13.0 14.0
print pd.merge(left2,right2,how='outer',left_index=True,right_index=True)
#同時使用了left_index,right_index同時合併雙方索引,且outer外連線交集
#輸出結果如下:
# Ohio Nevada Missouri Alabama
# a 1.0 2.0 NaN NaN
# b NaN NaN 7.0 8.0
# c 3.0 4.0 9.0 10.0
# d NaN NaN 11.0 12.0
# e 5.0 6.0 13.0 14.0
(2)join的用法:索引上合併用left_index=True、right_index=True
DataFrame的一個例項方法Join:更為方便地實現按索引合併。它還可以用於合併多個帶有相同或相似索引的DataFrame物件,而不管它們之間有沒有重疊的列。
#Join:在連線鍵上是做左連線
# (1)DataFrame的一個例項方法Join:更為方便地實現按索引合併。不管它們之間有沒有重疊的列。
#上面的例子可以編寫如下:
print left2.join(right2,how='outer') #類似python的字串連線Join
#輸出結果如下:
# Ohio Nevada Missouri Alabama
# a 1.0 2.0 NaN NaN
# b NaN NaN 7.0 8.0
# c 3.0 4.0 9.0 10.0
# d NaN NaN 11.0 12.0
# e 5.0 6.0 13.0 14.0
# (2)它還支援DataFrame的索引跟呼叫者DataFrame的某個列之間的連線(即第一個DataFrame的行索引與第二個DataFrame的列連線)
print left1.join(right1,on='key')
#輸出結果如下:
# key value group_val
# 0 a 0 3.5
# 1 b 1 7.0
# 2 a 2 3.5
# 3 a 3 3.5
# 4 b 4 7.0
# 5 c 5 NaN
#(3)可以向join傳入一組DataFrame
another=DataFrame([[7.,8.],[9.,10.],[11.,12.],[16.,17.]],
index=['a','c','e','f'],columns=['New York','Oregon'])
print another
#輸出結果如下:
# New York Oregon
# a 7.0 8.0
# c 9.0 10.0
# e 11.0 12.0
# f 16.0 17.0
print left2.join([right2,another]) #left2左分別連線right2,another
# left2內容如下:
# # Ohio Nevada
# # a 1.0 2.0
# # c 3.0 4.0
# # e 5.0 6.0
# right2內容如下:
# # Missouri Alabama
# # b 7.0 8.0
# # c 9.0 10.0
# # d 11.0 12.0
# # e 13.0 14.0
#left2.join([right2,another])輸出結果如下:
# Ohio Nevada Missouri Alabama New York Oregon
# a 1.0 2.0 NaN NaN 7.0 8.0
# c 3.0 4.0 9.0 10.0 9.0 10.0
# e 5.0 6.0 13.0 14.0 11.0 12.0
軸上連線:
另一種資料合併運算也被稱作連線、繫結或堆疊。numpy有一個用於合併原始Numpy陣列的concatenation函式。concatenate是Numpy的方法,
而concate是pandas的方法。
concatenate連線:
arr=np.arange(12).reshape((3,4))
print arr
#輸出的結果如下:
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
print np.concatenate([arr,arr],axis=1) #axis=1是列
#輸出的結果如下:
# [[ 0 1 2 3 0 1 2 3]
# [ 4 5 6 7 4 5 6 7]
# [ 8 9 10 11 8 9 10 11]]
concate連線:
# 如果各物件其他軸上的索引不同,那些軸應該是做並集還是交集?
# 結果物件中的分組需要各不相同嗎?
# 用於連線的軸axis重要嗎?
concate用於解決上面的三個問題,它與concatenate沒多大區別,只是多了一些引數
(1)三個沒有重疊索引的Series,對這些物件呼叫concat可以將值和索引粘合在一起
s1=Series([0,1],index=['a','b'])
s2=Series([2,3,4],index=['c','d','e'])
s3=Series([5,6],index=['f','g'])
print s1
# a 0
# b 1
print s2
# c 2
# d 3
# e 4
print s3
# f 5
# g 6
print pd.concat([s1,s2,s3])
#輸出結果如下:
# a 0
# b 1
# c 2
# d 3
# e 4
# f 5
# g 6
(2)預設情況下,concat是在axix=0行上作用的,最終產生一個新的Series.如果傳入axis=1,則結果就會變成一個DataFrame(axis=1是列)
#series行向合併,本來series只有行索引,故產生一個新的Series.如果列合併,有幾個陣列,列不一定相同,所以會產生一個DataFrame
print pd.concat([s1,s2,s3],axis=1)
#輸出結果如下:
# 0 1 2
# a 0.0 NaN NaN
# b 1.0 NaN NaN
# c NaN 2.0 NaN
# d NaN 3.0 NaN
# e NaN 4.0 NaN
# f NaN NaN 5.0
# g NaN NaN 6.0
(3)這種情況下,另外一條軸上沒有重疊,從索引的有序並集上就可以看出。。傳入join='inner'即可得到它們的交集
s4=pd.concat([s1*5,s3])
print pd.concat([s1,s4],axis=1)
#輸出結果如下:
# 0 1
# a 0.0 0
# b 1.0 5
# f NaN 5
# g NaN 6
print pd.concat([s1,s4],axis=1,join='inner')
#輸出結果如下:
# 0 1
# a 0 0
# b 1 5
(4)可以通過join_axes指字要在其他軸上使用的索引
print pd.concat([s1,s4],axis=1,join_axes=[['a','c','b','e']])
#輸出結果如下:
# 0 1
# a 0.0 0.0
# c NaN NaN
# b 1.0 5.0
# e NaN NaN
(5)不過有個問題,參與連線的片段在結果中區分不開。假設你想要在連線軸上建立一個層次化索引。使用keys引數即可達到目的
result=pd.concat([s1,s1,s3],keys=['one','two','three'])
print result
#輸出結果如下:
# one a 0
# b 1
# two a 0
# b 1
# three f 5
# g 6
# dtype: int64
print result.unstack()
#輸出結果如下:
# a b f g
# one 0.0 1.0 NaN NaN
# two 0.0 1.0 NaN NaN
# three NaN NaN 5.0 6.0
(6)如果沿著axis=1對Series進行合併,則keys就會成為DataFrame的列頭
print pd.concat([s1,s2,s3],axis=1,keys=['one','two','three'])
#輸出結果如下:
# one two three
# a 0.0 NaN NaN
# b 1.0 NaN NaN
# c NaN 2.0 NaN
# d NaN 3.0 NaN
# e NaN 4.0 NaN
# f NaN NaN 5.0
# g NaN NaN 6.0
(7)同樣的邏輯對DataFrame物件也是一樣:
df1=DataFrame(np.arange(6).reshape(3,2),index=['a','b','c'],
columns=['one','two'])
df2=DataFrame(5+np.arange(4).reshape(2,2),index=['a','c'],
columns=['three','four'])
print pd.concat([df1,df2],axis=1,keys=['level1','level2'])
#輸出結果如下:
# level1 level2
# one two three four
# a 0 1 5.0 6.0
# b 2 3 NaN NaN
# c 4 5 7.0 8.0
(8)如果傳入的不是列表而是一個字典,則字黃的鍵就會被做Keys選項的值。
print pd.concat({'level1':df1,'level2':df2},axis=1)
# level1 level2
# one two three four
# a 0 1 5.0 6.0
# b 2 3 NaN NaN
# c 4 5 7.0 8.0
(9)此外還有兩個用於管理層次化索引建立方式的引數:
a:key,names
print pd.concat([df1,df2],axis=1,keys=['level1','level2'],
names=['upper','lower'])
#輸出的結果如下:
# upper level1 level2
# lower one two three four
# a 0 1 5.0 6.0
# b 2 3 NaN NaN
# c 4 5 7.0 8.0
b:ignore_index=True
df1=DataFrame(np.random.randn(3,4),columns=['a','b','c','d'])
df2=DataFrame(np.random.randn(2,3),columns=['b','d','a'])
print df1
print df2
#在這種情況下,傳入ignore_index=True即可:
print pd.concat([df1,df2],ignore_index=True)
#輸出結果如下:
# a b c d
# 0 -0.540071 -0.225394 -0.851317 -1.638987
# 1 0.163700 1.375732 0.255257 1.290061
# 2 0.787074 0.137692 0.929145 -0.793582
# 3 -0.502263 0.717226 NaN -1.224583
# 4 -0.023067 -0.556030 NaN -0.819273
concat函式的引數
引數 說明
objs 參與連線的pandas物件的列表或字典。唯一必需的引數
axis 指明連線的軸向,預設為0
join “inner”、“outer”其中之一,預設為"outer".指明其他軸向上的索引是按交集(inner)
還是並集(outer)進行合併
join_axes 指明用於其他n-1條軸的索引,不執行並集/交集運算
keys 與連線物件有關的值,用於形成連線軸向上的層次化索引。可以是任意值的列表或陣列、元組陣列、
陣列列表(如果將levels設定成多級陣列的話)
levels 指定用作層次化索引各級別上的索引,如果設定了keys的話
names 用於建立分層級別的名稱,如果設定了keys和levels的話
verify_integrity 檢查結果物件新軸上的重複情況,如果發現則引發異常。預設(False)允許重複
ignore_index 不保留連線軸上的索引,產生一組新索引
合併重疊資料:numpy.where, pd.combine_first
還有一種資料組合問題不能用簡單的合併(merge)或連線(concatenation)運算來處理。
比如,可能有索引全部或部分重疊的兩個資料集。我們使用Numpy的where函式,它用於表達一種向量化的if-else:
(1)Numpy的where
import pandas as pd
import numpy as np
from pandas import Series,DataFrame
(1)Numpy的where
a=Series([np.nan,2.5,np.nan,3.5,4.5,np.nan],
index=['f','e','d','c','b','a'])
b=Series(np.arange(len(a),dtype=np.float64),
index=['f','e','d','c','b','a'])
b[-1]=np.nan
print a
#輸出結果如下:
# f NaN
# e 2.5
# d NaN
# c 3.5
# b 4.5
# a NaN
print b
#輸出結果如下:
# f 0.0
# e 1.0
# d 2.0
# c 3.0
# b 4.0
# a NaN
print np.where(pd.isnull(a),b,a) #a如果是Nan,則用b的資料代替,否則還是a的值
#輸出結果如下:
# [ 0. 2.5 2. 3.5 4.5 nan]
(2)Series有一個combine_first方法,可以實現上面一樣的功能,而且會時行資料對齊
print b[:-2].combine_first(a[2:]) #b[:-2]的值若有nan則用a[2:]的值代替。但是b[:-2]是不包括右邊的倒數第二個b的,
# 則b的值用第一個4.5
#輸出結果如下:
# a NaN
# b 4.5
# c 3.0
# d 2.0
# e 1.0
# f 0.0
#Series的combine_first方法例項:DataFrame也有combine_first方法
s1=pd.Series([1,np.nan])
s2=pd.Series([3,4])
print s1
# 0 1.0
# 1 NaN
print s2
# 0 3
# 1 4
print s1.combine_first(s2) #s1裡有nan就換成s2的值
# 0 1.0
# 1 4.0
(3)DataFrame的combine_first也是同上,因此可以將其看做:用引數物件中的資料為呼叫者物件的缺失資料"打補丁"
df1=DataFrame({'a':[1.,np.nan,5.,np.nan],
'b':[np.nan,2.,np.nan,6.],
'c':range(2,18,4)})
df2=DataFrame({'a':[5.,4.,np.nan,3.,7.],
'b':[np.nan,3.,4.,6.,8.]})
print df1
#輸出結果如下:
# a b c
# 0 1.0 NaN 2
# 1 NaN 2.0 6
# 2 5.0 NaN 10
# 3 NaN 6.0 14
print df2
#輸出結果如下:
# a b
# 0 5.0 NaN
# 1 4.0 3.0
# 2 NaN 4.0
# 3 3.0 6.0
# 4 7.0 8.0
print df1.combine_first(df2) #用df2裡的資料為df1中的資料為nan的資料打補丁
#輸出結果如下:
# a b c
# 0 1.0 NaN 2.0
# 1 4.0 2.0 6.0
# 2 5.0 4.0 10.0
# 3 3.0 6.0 14.0
# 4 7.0 8.0 NaN
2. 重塑和軸向旋轉:
pandas物件中有許多用於重新排列表格型資料的基礎運算。這些函式也稱作重塑或軸向旋轉運算。
(1)stack:將資料的列"旋轉"為行。(將DataFrame轉換成Series,而Series無stack方法)
(2)unstack:與stack相反的操作,將資料的行“旋轉”為列。(將Series轉換成DataFrame,DataFrame還是DataFrame)
重塑層次化索引:
層次化索引為DataFrame資料的重排任務提供了一個具有良好一致性的方式。主要功能有以下二個方法:
層次化索引為DataFrame資料的
stack:將資料的列"旋轉"為行,將DataFrame轉換成Series,Series無stack()方法
unstack:將資料的行"旋轉"為列,將Series轉換成DataFrame了,DataFrame還是DataFrame
(1)將DataFrame的列旋轉為行:DataFrame.stack()
data=DataFrame(np.arange(6).reshape((2,3)),
index=pd.Index(['Ohio','Colorado'],name='state'),
columns=pd.Index(['one','two','three'],name='number'))
print data
#輸出結果如下:
# number one two three
# state
# Ohio 0 1 2
# Colorado 3 4 5
result=data.stack()
print result
# #輸出結果如下:
# # state number
# # Ohio one 0
# # two 1
# # three 2
# # Colorado one 3
# # two 4
# # three 5
(2)對於一個層次化索引的Series,可以用unstack將其重排為一個DataFrame.
print result.unstack()
#輸出結果如下:
# number one two three
# state
# Ohio 0 1 2
# Colorado 3 4 5
預設情況下,unstack操作的是最內層(stack也是如此)。傳入分層級別的編號或名稱即可對其他級別進行unstack操作
print result.unstack(0)
#輸出結果如下:
# state Ohio Colorado
# number
# one 0 3
# two 1 4
# three 2 5
print result.unstack('state')
#輸出結果如下:
# state Ohio Colorado
# number
# one 0 3
# two 1 4
# three 2 5
(3)如果不是所有級別值都能在各分組中找到的話,則unstack操作可能會引入缺失資料:
s1=Series([0,1,2,3],index=['a','b','c','d'])
s2=Series([4,5,6],index=['c','d','e'])
data2=pd.concat([s1,s2],keys=['one','two'])
print data2
#輸出結果如下:
# one a 0
# b 1
# c 2
# d 3
# two c 4
# d 5
# e 6
# dtype: int64
print data2.unstack()
#輸出結果如下:
# a b c d e
# one 0.0 1.0 2.0 3.0 NaN
# two NaN NaN 4.0 5.0 6.0
(4)stack預設會濾除缺失資料,因此該運算是可逆的:
# print data2.stack() #報錯'Series' object has no attribute 'stack'
#可以對data2.unstack()後的DataFrame進行stack()
print data2.unstack().stack(dropna=False) #DataFrame轉換成Series了,且列變成行了
#輸出結果如下:
# one a 0.0
# b 1.0
# c 2.0
# d 3.0
# e NaN
# two a NaN
# b NaN
# c 4.0
# d 5.0
# e 6.0
print data2.unstack().unstack() #data2.unstack()的行變成列
#輸出結果如下:
# a one 0.0
# two NaN
# b one 1.0
# two NaN
# c one 2.0
# two 4.0
# d one 3.0
# two 5.0
# e one NaN
# two 6.0
(5)對DataFrame進行unstack操作時,作為旋轉軸的級別將會成為結果中的最低級別
df=DataFrame({'left':result,'right':result+5},
columns=pd.Index(['left','right'],name='side'))
print df
#result的內容如下:
# # state number
# # Ohio one 0
# # two 1
# # three 2
# # Colorado one 3
# # two 4
# # three 5
#df的結果如下:
# side left right
# state number
# Ohio one 0 5
# two 1 6
# three 2 7
# Colorado one 3 8
# two 4 9
# three 5 10
print df.unstack('state') #將state的行旋轉為列
#輸出結果如下:
# side left right
# state Ohio Colorado Ohio Colorado
# number
# one 0 3 5 8
# two 1 4 6 9
# three 2 5 7 10
print df.unstack('state').stack('side') #將上面的再stack將列為side的旋轉為行(即left,right)
#輸出結果如下:
# state Colorado Ohio
# number side
# one left 3 0
# right 8 5
# two left 4 1
# right 9 6
# three left 5 2
# right 10 7
將"長格式"轉換為"寬格式":
時間序列資料通常是以所謂的"長格式"(long)或"堆疊格式"(stacked)儲存在資料庫和CSV中的。
關係型資料庫通常是主鍵形式提供了關係的完整性,而且提供了更為簡單的查詢支援。但對於長格式的資料操作起來就不那麼輕鬆了。
而DataFrame可以把主鍵中不同的Item值分別形成一列,date列中的時間值則用作索引,可以用DataFrame的pivot方法完全可以實現這個轉換。
import pandas as pd
import numpy as np
from pandas import Series,DataFrame
ldata=DataFrame({'date':['1959-03-31 00:00:00','1959-03-31 00:00:00','1959-03-31 00:00:00',
'1959-06-30 00:00:00','1959-06-30 00:00:00','1959-06-30 00:00:00',
'1959-09-30 00:00:00','1959-09-30 00:00:00','1959-09-30 00:00:00','1959-12-31 00:00:00'],
'item':['realgdp','infl','unemp','realgdp','infl','unemp','realgdp','infl','unemp','realgdp'],
'value':['2710.349','0.000','5.800','2778.801','2.340','5.100','2775.488','2.740','5.300','2785.204']
})
print ldata
#輸出結果如下:
# date item value
# 0 1959-03-31 00:00:00 realgdp 2710.349
# 1 1959-03-31 00:00:00 infl 0.000
# 2 1959-03-31 00:00:00 unemp 5.800
# 3 1959-06-30 00:00:00 realgdp 2778.801
# 4 1959-06-30 00:00:00 infl 2.340
# 5 1959-06-30 00:00:00 unemp 5.100
# 6 1959-09-30 00:00:00 realgdp 2775.488
# 7 1959-09-30 00:00:00 infl 2.740
# 8 1959-09-30 00:00:00 unemp 5.300
# 9 1959-12-31 00:00:00 realgdp 2785.204
(1)使用DataFrame的pivot的方法
pivoted=ldata.pivot('date','item','value')
print pivoted.head()
#輸出結果如下:
# item infl realgdp unemp
# date
# 1959-03-31 00:00:00 0.000 2710.349 5.800
# 1959-06-30 00:00:00 2.340 2778.801 5.100
# 1959-09-30 00:00:00 2.740 2775.488 5.300
# 1959-12-31 00:00:00 None 2785.204 None
(2)前兩個引數值分別用作行和列索引的列名,最後一個引數值則是用於填充DataFrame的資料列的列名。
#假設有兩個需要參與重塑的資料列:
ldata['value2']=np.random.randn(len(ldata))
print ldata[:10]
#輸出結果如下:
# date item value value2
# 0 1959-03-31 00:00:00 realgdp 2710.349 0.935557
# 1 1959-03-31 00:00:00 infl 0.000 1.609324
# 2 1959-03-31 00:00:00 unemp 5.800 -0.753734
# 3 1959-06-30 00:00:00 realgdp 2778.801 -0.668879
# 4 1959-06-30 00:00:00 infl 2.340 -0.256601
# 5 1959-06-30 00:00:00 unemp 5.100 0.537770
# 6 1959-09-30 00:00:00 realgdp 2775.488 1.073817
# 7 1959-09-30 00:00:00 infl 2.740 -0.027340
# 8 1959-09-30 00:00:00 unemp 5.300 -0.531161
# 9 1959-12-31 00:00:00 realgdp 2785.204 0.706080
(3)如果忽略掉最後一個引數,得到的DataFrame就會帶有層次化的列
pivoted=ldata.pivot('date','item')
print pivoted[:5]
#輸出結果如下:
# value value2
# item infl realgdp unemp infl realgdp unemp
# date
# 1959-03-31 00:00:00 0.000 2710.349 5.800 -0.505690 -1.090732 -2.123859
# 1959-06-30 00:00:00 2.340 2778.801 5.100 -0.940028 -2.204997 -1.195357
# 1959-09-30 00:00:00 2.740 2775.488 5.300 -0.636424 0.510898 1.105585
# 1959-12-31 00:00:00 None 2785.204 None NaN -0.203154 NaN
(4)注意,pivot其實只是一個快捷方式而己:用set_index建立層次化索引,再用unstack重塑。
unstacked=ldata.set_index(['date','item']).unstack('item')
print unstacked[:7]
#輸出結果如下:
# value value2
# item infl realgdp unemp infl realgdp unemp
# date
# 1959-03-31 00:00:00 0.000 2710.349 5.800 -0.380805 -2.499867 -1.398593
# 1959-06-30 00:00:00 2.340 2778.801 5.100 -0.168914 0.435372 -1.454938
# 1959-09-30 00:00:00 2.740 2775.488 5.300 -0.297850 -1.693196 1.615807
# 1959-12-31 00:00:00 None 2785.204 None NaN 1.551105 NaN
import pandas as pd
import numpy as np
from pandas import Series,DataFrame
df = pd.DataFrame({'foo': ['one', 'one', 'one', 'two', 'two',
'two'],
'bar': ['A', 'B', 'C', 'A', 'B', 'C'],
'baz': [1, 2, 3, 4, 5, 6],
'zoo': ['x', 'y', 'z', 'q', 'w', 't']})
print df
#輸出結果如下:
# bar baz foo zoo
# 0 A 1 one x
# 1 B 2 one y
# 2 C 3 one z
# 3 A 4 two q
# 4 B 5 two w
# 5 C 6 two t
print df.pivot(index='foo',columns='bar')['baz'] #行索引foo,列bar,值baz
#輸出結果如下:
# bar A B C
# foo
# one 1 2 3
# two 4 5 6
print df.pivot(index='foo',columns='bar',values='baz') #與上面的一致
# print df.pivot(index='foo',columns='bar',values=['baz', 'zoo'])
#若是兩行的索引一樣的話會報錯,因為值就不知道對應哪個了
#如下例中bar中的兩個索一樣
df=pd.DataFrame({"foo":['one','one','two','two'],
"bar":['A','A','B','C'],
"baz":[1,2,3,4]})
print df
print df.pivot(index='foo',columns='bar',values='baz') #報錯,foo,bar為[one,A]不知道對應哪個值了,兩個一樣的索引
3. 資料轉換:
前面講的都是資料重排,另一類重要操作則是過濾、清理以及其它的轉換工作。
(1)drop_duplicates:刪除重複資料
(2)map:函式或對映進行資料轉換(map,lambda),map用於修改物件的子集
(3)fillna: 用於填充缺失資料,看做值替換的一種特殊情況。或者用pandas的replace方法替換缺失值(與python的str.replace方法一樣)
(4)rename:複製DataFrame並對其行索引和列標籤進行修改。若要就地修改某個資料集,傳入inplace=True即可
(5)cut:將資料劃分階段(即元面)例不同的年齡階段,它返回的Categorical物件。而qcut是根據樣本分位數對資料進行劃分(可以等分)
(6)abs:過濾絕對值,np.abs(data)>3取絕對值>3的數。
(7)np.sign:過濾的區間為-1到1的陣列,表示原始值的符號。
(8)take:選取子集,從DataFrame或Series中選取部分子集
(9)get_dummies:DataFrame的某一列中有多個不同的值,可將該列的值作為列名,其值全為1和0.例:第一行只有a的值,則a為1,其它為0
Series只有panda.str.get_dummies.
刪除重複資料:
import pandas as pd
import numpy as np
from pandas import Series,DataFrame
#前面講的都是資料重排,另一類重要操作則是過濾、清理以及其它的轉換工作。
#刪除重複資料
#DataFrame中常常會出現重複行,舉例如下:
data=DataFrame({'k1':['one']*3+['two']*4,
'k2':[1,1,2,3,3,4,4]})
print data
#輸出結果如下:
# k1 k2
# 0 one 1
# 1 one 1
# 2 one 2
# 3 two 3
# 4 two 3
# 5 two 4
# 6 two 4
(1)DataFrame的duplicated方法返回一個布林型Series,表示各行是否是重複行:
print data.duplicated()
#輸出結果如下:
# 0 False
# 1 True
# 2 False
# 3 False
# 4 True
# 5 False
# 6 True
# dtype: bool
(2)還有一個與此相關的drop_duplicates方法,它用於返回一個移除了重複行的DataFrame
print data.drop_duplicates()
#輸出結果如下:
# k1 k2
# 0 one 1
# 2 one 2
# 3 two 3
# 5 two 4
(3)這兩個方法預設會判斷全部列,你可以指定部分列進行重複項判斷。假設你還有一列值,且只希望根據k1列過濾重複項。
data['v1']=range(7)
print data.drop_duplicates(['k1']) #過濾k1列的重複項
#輸出結果如下:
# k1 k2 v1
# 0 one 1 0
# 3 two 3 3
(4)duplicated和drop_duplicates預設保留的是第一個出現的值組合。傳入last則保留最後一個
#它的引數值有:first是保留第一個過濾,False則刪除全是重複(即k1,k2,v1)
print data.drop_duplicates(['k1','k2'],'last') #即以k2值過濾
#輸出結果如下:
# k1 k2 v1
# 1 one 1 1
# 2 one 2 2
# 4 two 3 4
# 6 two 4 6
利用函式或對映進行資料轉換:
map用於修改物件的子集:
import pandas as pd
import numpy as np
from pandas import Series,DataFrame
data=DataFrame({'food':['bacon','pulled pork','bacon','Pastrami',
'corned beef','Bacon','pastrami','honey ham',
'nova lox'],
'ounces':[4,3,12,6,7.5,8,3,5,6]})
print data
#輸出結果如下:
# food ounces
# 0 bacon 4.0
# 1 pulled pork 3.0
# 2 bacon 12.0
# 3 Pastrami 6.0
# 4 corned beef 7.5
# 5 Bacon 8.0
# 6 pastrami 3.0
# 7 honey ham 5.0
# 8 nova lox 6.0
(1)假如你想要新增一列表示該肉類食物來源的動物型別,我們可以先編寫一個肉類到動物的對映:對映用map
meat_to_animal={'bacon':'pig','pulled pork':'pig','pastrami':'cow',
'corned beef':'cow','honey ham':'pig','nova lox':'salmon'}
#Series的map方法可以接受一個函式或含有對映關係的字典型物件,但是這裡有個小問題,即有些肉類的首字母大寫了,而另一些則沒有。
#因此我們還需要將各個值轉換為小寫:
data['animal']=data['food'].map(str.lower).map(meat_to_animal)
print data
#輸出結果如下:
# food ounces animal
# 0 bacon 4.0 pig
# 1 pulled pork 3.0 pig
# 2 bacon 12.0 pig
# 3 Pastrami 6.0 cow
# 4 corned beef 7.5 cow
# 5 Bacon 8.0 pig
# 6 pastrami 3.0 cow
# 7 honey ham 5.0 pig
# 8 nova lox 6.0 salmon
(2)也可以傳入一個能夠完成全部這些工作的函式:lambda
print '++++++++++++++++++++++++++++++++'
print data['food'].map(lambda x:meat_to_animal[x.lower()])
#輸出結果如下:
# 0 pig
# 1 pig
# 2 pig
# 3 cow
# 4 cow
# 5 pig
# 6 cow
# 7 pig
# 8 salmon
# Name: food, dtype: object
data['animal']=data['food'].map(lambda x:meat_to_animal[x.lower()])
print data
#輸出結果如下:
# food ounces animal
# 0 bacon 4.0 pig
# 1 pulled pork 3.0 pig
# 2 bacon 12.0 pig
# 3 Pastrami 6.0 cow
# 4 corned beef 7.5 cow
# 5 Bacon 8.0 pig
# 6 pastrami 3.0 cow
# 7 honey ham 5.0 pig
# 8 nova lox 6.0 salmon
#map是實現元素級轉換以及其它資料工作的便捷方式
替換值:replace
import pandas as pd
import numpy as np
from pandas import Series,DataFrame
data=Series([1.,-999.,2.,-999.,-1000.,3.])
print data
#輸出結果如下:
# 0 1.0
# 1 -999.0
# 2 2.0
# 3 -999.0
# 4 -1000.0
# 5 3.0
(1)-999這個值可能是一個表示缺失資料的標記值,要將其替換為Pandas能夠理解的NA值、我們可以利用replace來產生一個新的Series.
print data.replace(-999,np.nan)
#輸出結果如下:
# 0 1.0
# 1 NaN
# 2 2.0
# 3 NaN
# 4 -1000.0
# 5 3.0
(2)如果希望一次性替換多個值,可以傳入一個由待替換值組成的列表以及一個替換值。
print data.replace([-999,-1000],np.nan) #將-999,-1000的都替換成NaN
#輸出結果如下:
# 0 1.0
# 1 NaN
# 2 2.0
# 3 NaN
# 4 NaN
# 5 3.0
(3)如果希望對不同的值進行不同的替換,則傳入一個由替換關係組成的列表即可:
print data.replace([-999,-1000],[np.nan,0]) #將-999替換成NaN,-1000替換成0
#輸出的結果如下:
# 0 1.0
# 1 NaN
# 2 2.0
# 3 NaN
# 4 0.0
# 5 3.0
(4)傳入的引數也可以是字典
print data.replace({-999:np.nan,-1000:0})
#輸出的結果如下:
# 0 1.0
# 1 NaN
# 2 2.0
# 3 NaN
# 4 0.0
# 5 3.0
重新命名軸索引:
跟Series中的值一樣,軸標籤也可以通過函式或對映進行轉換,從而得到一個新物件。
軸還可以被就地修改,而無需新建一個數據結構。
import pandas as pd
import numpy as np
from pandas import Series,DataFrame
data=DataFrame(np.arange(12).reshape((3,4)),
index=['Ohio','Colorado','New York'],
columns=['one','two','three','four'])
(1)和軸標籤一樣,軸標籤也有一map方法:
print data.index.map(str.upper) #data的index用map轉成全是大寫
#輸出結果如下:
# Index([u'OHIO', u'COLORADO', u'NEW YORK'], dtype='object')
#可以將其直接賦給index,這樣就達到了就地修改了。
data.index=data.index.map(str.upper)
print data
#輸出結果如下:
# one two three four
# OHIO 0 1 2 3
# COLORADO 4 5 6 7
# NEW YORK 8 9 10 11
(2)如果想要建立資料集的轉換版(而不是修改原始資料),比較實用的方法是rename:
print data.rename(index=str.title,columns=str.upper)
#輸出結果如下:
# ONE TWO THREE FOUR
# Ohio 0 1 2 3
# Colorado 4 5 6 7
# New York 8 9 10 11
(3)rename可以結合字典物件實現對部分軸標籤的更新
print data.rename(index={'OHIO':'INDIANA'},
columns={'three':'peekaboo'})
#輸出結果如下:
# one two peekaboo four
# Ohio 0 1 2 3
# Colorado 4 5 6 7
# New York 8 9 10 11
(4)rename幫我們實現了:複製DataFrame並對其索引和列標籤進行賦值。如果希望就地修改某個資料集,傳入inplace=True即可
_=data.rename(index={'OHIO':'INDIANA'},inplace=True)
print data
離散化和麵元劃分:cut,pcut
為了便於分析,連續資料常常被離散化或拆分為“面元”(bin).假設有一組人員資料,而你希望將它們劃分為不同年齡組:
import pandas as pd
import numpy as np
from pandas import Series,DataFrame
ages=[20,22,25,27,21,23,37,31,61,45,41,32]
(1)接下來將這些資料劃分為"18-25"、"26到35"、"35到60"以及"60以上"幾個面元,要實現該功能,需要使用pandas的cut函式.
cut返回的是一個特殊的Categorical物件。可以看做一組表示面元名稱的字串。
bins=[18,25,35,60,100]
cats=pd.cut(ages,bins)
print cats
#輸出結果如下:
# [(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (25, 35], (60, 100], (35, 60], (35, 60], (25, 35]]
# Length: 12
# Categories (4, interval[int64]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]
(2)實際上,它含有一個表示不同分類名稱的levels陣列以及一個為年齡資料進行標號的labels屬性,現在用codes替換了labels
# print cats.labels
#輸出結果如下:
# [0 0 0 1 0 0 2 1 3 2 2 1]
print cats.codes
#輸出結果如下:
# [0 0 0 1 0 0 2 1 3 2 2 1]
print pd.value_counts(cats) #符合各區間的個數
#輸出結果如下:
# (18, 25] 5
# (35, 60] 3
# (25, 35] 3
# (60, 100] 1
(3)跟"區間"的數學符號一樣,圓括號表示開端,而方括號則表示閉端。哪邊是閉端可以通過right-False進行修改:
print pd.cut(ages,[18,26,36,61,100],right=False)
#輸出結果如下:
# [[18, 26), [18, 26), [18, 26), [26, 36), [18, 26), ..., [26, 36), [61, 100), [36, 61), [36, 61), [26, 36)]
# Length: 12
# Categories (4, interval[int64]): [[18, 26) < [26, 36) < [36, 61) < [61, 100)]
(4)你也可以設定自己的面元名稱,將labels選項設定為一個列表或陣列即可:
group_names=['Youth','YoungAdult','MiddleAged','Senior']
print pd.cut(ages,bins,labels=group_names)
#輸出結果如下:
# [Youth, Youth, Youth, YoungAdult, Youth, ..., YoungAdult, Senior, MiddleAged, MiddleAged, YoungAdult]
# Length: 12
# Categories (4, object): [Youth < YoungAdult < MiddleAged < Senior]
(5)如果向cut傳入的是面元的數量而不是確切的面元邊界,則它會根據資料的最小值和最大值計算等長面元。
#下面這個例子,我們將一些均勻分佈的資料分成四組:
data=np.random.rand(20)
print pd.cut(data,4,precision=2)
#輸出結果如下:
# [(0.046, 0.28], (0.046, 0.28], (0.28, 0.52], (0.28, 0.52], (0.76, 0.99], ..., (0.76, 0.99], (0.76, 0.99], (0.76, 0.99], (0.76, 0.99], (0.28, 0.52]]
# Length: 20
# Categories (4, interval[float64]): [(0.046, 0.28] < (0.28, 0.52] < (0.52, 0.76] < (0.76, 0.99]]
(6)qcut是一個非常類似於cut函式,它可以根據樣本分位數對資料進行面元劃分
#cut可能無法使各個面元中含有相同數量的資料點。而qcut使用的是樣本分位數,因此可以得到大小基本相等的面元。
data=np.random.randn(1000) #randn正態分佈
cats=pd.qcut(data,4) #按4分位進行切割
print cats
#輸出結果如下:
# [(-0.658, 0.0366], (-4.299, -0.658], (-0.658, 0.0366], (-4.299, -0.658], (0.0366, 0.686], ..., (0.686, 3.005], (0.686, 3.005], (0.686, 3.005], (-4.299, -0.658], (0.686, 3.005]]
# Length: 1000
# Categories (4, interval[float64]): [(-4.299, -0.658] < (-0.658, 0.0366] < (0.0366, 0.686] <
# (0.686, 3.005]]
(7)和cut一樣,也可以設定自定義的分位數(0到1之間的數值,包含端點):
print pd.qcut(data,[0,0.1,0.5,0.9,1.])
注意:本章稍後在講解聚合和分組運算時會再次用到cut和qcut,因為這兩個離散化函式對分量和分組分析非常重要。
檢測和過濾異常值:abs(col)>3,np.sign(data)*3
異常值的過濾或變換運算在很大程度上其實就是陣列運算。來看一個含有正態分佈資料的DataFrame:
import pandas as pd
import numpy as np
from pandas import Series,DataFrame
np.random.seed(12345)
data=DataFrame(np.random.randn(1000,4))
print data.describe()
#輸出結果如下:
# 0 1 2 3
# count 1000.000000 1000.000000 1000.000000 1000.000000
# mean -0.067684 0.067924 0.025598 -0.002298
# std 0.998035 0.992106 1.006835 0.996794
# min -3.428254 -3.548824 -3.184377 -3.745356
# 25% -0.774890 -0.591841 -0.641675 -0.644144
# 50% -0.116401 0.101143 0.002073 -0.013611
# 75% 0.616366 0.780282 0.680391 0.654328
# max 3.366626 2.653656 3.260383 3.927528
(1)假如想找出某列中絕對值大小超過3的值:abs(col)>3
col=data[3] #取data[3]列
print col[np.abs(col)>3]
#輸出結果如下:
# 97 3.927528
# 305 -3.399312
# 400 -3.745356
# Name: 3, dtype: float64
(2)要選出全部含有"超過3或-3的值"的行,可以利用布林型DataFrame以及any方法:
print data[(np.abs(data)>3).any(1)]
(3)根據這些條件,可輕鬆地對值進行設定。下面的程式碼可以將值限制在區間-3到3以內:
np.sign這個函式返回的是一個由-1到1組成的陣列,表示原始值的符號
data[np.abs(data)>3]=np.sign(data)*3
print data.describe()
#輸出結果如下:
# 0 1 2 3
# count 1000.000000 1000.000000 1000.000000 1000.000000
# mean -0.067623 0.068473 0.025153 -0.002081
# std 0.995485 0.990253 1.003977 0.989736
# min -3.000000 -3.000000 -3.000000 -3.000000
# 25% -0.774890 -0.591841 -0.641675 -0.644144
# 50% -0.116401 0.101143 0.002073 -0.013611
# 75% 0.616366 0.780282 0.680391 0.654328
# max 3.000000 2.653656 3.000000 3.000000
排列和隨機取樣:permutation(5)需要排列的長度為5,take選取子集
利用numpy.random.permutation函式可以輕鬆實現對Series或DataFrame的列的排列工作(permuting).通過需要排列的軸的長度
呼叫permutation,可產生一個表示新順序的整數陣列:
import numpy as np
from pandas import DataFrame,Series
(1)take:選取子集,df.take[0,3]是選取第0行和第3行,而不是選取行索引為0和3的行
df=DataFrame(np.arange(5*4).reshape(5,4))
sampler=np.random.permutation(5) #排列的軸的長度呼叫permutation,可產生一個整數陣列
print sampler
#輸出結果如下:
# [1 2 3 0 4]
print df
#輸出結果如下:
# 0 1 2 3
# 0 0 1 2 3
# 1 4 5 6 7
# 2 8 9 10 11
# 3 12 13 14 15
# 4 16 17 18 19
print df.take(sampler) #取df的第1,2,3,0,4行。而不是取索行號為1,2,3,0,4的行。
#輸出結果如下:
# 0 1 2 3
# 1 4 5 6 7
# 2 8 9 10 11
# 3 12 13 14 15
# 0 0 1 2 3
# 4 16 17 18 19
(2)如果不想用替換的方式選取隨機子集,則可以使用permutation:從permutation返回的陣列中切下前k個元素,其中k為期望的子集大小。
直接切下選出permutation的值
#雖然有很多高效的演算法可以實現非替換式取樣,但是手邊有的工具為什麼不用呢
print '+++++++++++++++++++++++++'
print np.random.permutation(len(df))
#輸出結果:[2 1 3 0 4]
print df.take(np.random.permutation(len(df))[:3]) #取出[:3]下標從0-3不包括3的行,即的是[2,1,3]行的資料
#輸出結果如下:
# 0 1 2 3
# 1 4 5 6 7
# 3 12 13 14 15
# 0 0 1 2 3
(3)要通過替換的方式產生樣本,最快的方式是通過np.random.randint得到一組隨機整數:
bag=np.array([5,7,-1,6,4])
sampler=np.random.randint(0,len(bag),size=10)
print sampler
#輸出結果如下:
# [2 2 4 3 0 4 4 3 0 4]
draw=bag.take(sampler)
print draw
#輸出結果如下:按上面sampler的值取第幾個,如sample為2,則取第二個值
# [-1 -1 4 6 5 4 4 6 5 4]
計算指標/啞變數:get_dummies
用於統計建模或機器學習的轉換方式是:將分類變數(categorical variable)轉換為"啞變數矩陣"(dummy matrix)或
"指標矩陣"(indicator matrix).如果DataFrame的某一列中含有K個不同的值,則可以派生出一個k列矩陣或DataFrame
(其值全為1和0)。pandas有一個get_dummies函式可以實現該功能.
import numpy as np
import pandas as pd
from pandas import DataFrame,Series
(1) get_dummies某一列中有多個不同的值,可將該列的值作為列名,其值全為1和0。例:第一行只有a的值,則a為1,其它為0
df=DataFrame({'key':['b','b','a','c','a','b'],
'data1':range(6)})
print df
#輸出結果如下:
# data1 key
# 0 0 b
# 1 1 b
# 2 2 a
# 3 3 c
# 4 4 a
# 5 5 b
print pd.get_dummies(df['key'])
#輸出結果如下:
# a b c
# 0 0 1 0
# 1 0 1 0
# 2 1 0 0
# 3 0 0 1
# 4 1 0 0
# 5 0 1 0
(2)若想給DataFrame的列加上一個字首,以便能夠跟其他資料進行合併。可以用prefix='key'
dummies=pd.get_dummies(df['key'],prefix='key') #prefix是將列分成好幾個子列的表示如:key_a,key_b,key_c...
print dummies
#輸出結果如下:
# key_a key_b key_c
# 0 0 1 0
# 1 0 1 0
# 2 1 0 0
# 3 0 0 1
# 4 1 0 0
# 5 0 1 0
df_with_dummy=df[['data1']].join(dummies) #data1列和dummies合併
print df_with_dummy
#輸出結果如下:
# data1 key_a key_b key_c
# 0 0 0 1 0
# 1 1 0 1 0
# 2 2 1 0 0
# 3 3 0 0 1
# 4 4 1 0 0
# 5 5 0 1 0
(3)如果DataFrame中某行同屬於多個分類,則事情就會有點複雜,具體的操作如下
#movies.dat的內容如下:movies的資料就是同一行的資料是多個分類
# 1::Toy Story(1995)::Animation|Children's|Comedy
# 2::Jumanji(1995)::Adventure|Children's|Fantasy
# 3::Grumpier Old Men(1995)::Comedy|Romance
# 4::Waiting to Exhale(1995)::Comedy|Drama
# 5::Father of the Bride Part II(1995)::Comedy
# 6::Heat(1995)::Action|Crime|Thriller
mnames=['movie_id','title','genres']
#下面的engine不是read_table的引數,它是為了消除pycharm產生的warning
movies=pd.read_table('movies.dat',sep='::',header=None,names=mnames,engine='python')
print '++++++++++++++++++'
print len(movies) #輸出結果為:6
print movies[:3]
#輸出結果如下:
# movie_id title genres
# 0 1 Toy Story(1995) Animation|Children's|Comedy
# 1 2 Jumanji(1995) Adventure|Children's|Fantasy
# 2 3 Grumpier Old Men(1995) Comedy|Romance
(a)要為每個genre新增指標變數就需要做一些資料的規整。首先,我們從資料集中抽取出不同的genre值(巧用set.union),set.union集合的並集
genre_iter=(set(x.split('|')) for x in movies.genres) #取出movies.genres的每個資料,以'|'分開後放入set集合
print genre_iter
#輸出結果:<generator object <genexpr> at 0x103e7c0f0> 是個集合物件
genres=sorted(set.union(*genre_iter)) #接收的引數是一個元組,並且排序
print genres
#輸出結果如下:
# ['Action', 'Adventure', 'Animation', "Children's", 'Comedy', 'Crime', 'Drama', 'Fantasy', 'Romance', 'Thriller']
(b)現在,我們從一個全零DataFrame開始構建指標DataFrame:
dummies=DataFrame(np.zeros((len(movies),len(genres))),columns=genres)#構建movies的行數,genres的個數為列的全為0的資料
# print dummies #6行10列的全為0的資料
(c)接下來,迭代每一部電影並將dummies各行的項設定為1:
for i,gen in enumerate(movies.genres): #enumerate會自動加上索引,故要兩個變數接收,i是索引,gen是值
dummies.ix[i,gen.split('|')]=1 #ix取行,ix[i,gen.split('|')]若i=0,則表示取第一行,gen的'|'分開的值都賦1;
# 即假如這一行有該值,若有Action則該值為1.
print dummies
#輸出結果如下:
# Action Adventure Animation Children's Comedy Crime Drama Fantasy \
# 0 0.0 0.0 1.0 1.0 1.0 0.0 0.0 0.0
# 1 0.0 1.0 0.0 1.0 0.0 0.0 0.0 1.0
# 2 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0
# 3 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0
# 4 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0
# 5 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0
(d)genres列處理好了,再將其與moivies合併起來
movies_windic=movies.join(dummies.add_prefix('Genre')) #
print movies_windic
# print movies_windic.ix[0]#取第一行的資料
#輸出結果如下:
# movie_id 1
# title Toy Story(1995)
# genres Animation|Children's|Comedy
# GenreAction 0
# GenreAdventure 0
# GenreAnimation 1
# GenreChildren's 1
# GenreComedy 1
# GenreCrime 0
# GenreDrama 0
# GenreFantasy 0
# GenreRomance 0
# GenreThriller 0
下面簡單介紹一下get_dummies函式
import pandas as pd
import numpy as np
from pandas import DataFrame,Series
#get_dummies 某列的有多個值(a,b,c),沒有值的用0表示
s=pd.Series(list('abca'))
print s
#輸出結果如下:
# 0 a
# 1 b
# 2 c
# 3 a
print pd.get_dummies(s) #轉換categoriacl variable為dummy/indicator 變數
#輸出結果如下:
# a b c
# 0 1 0 0
# 1 0 1 0
# 2 0 0 1
# 3 1 0 0
s1=['a','b',np.nan]
print pd.get_dummies(s1)
#輸出結果如下:
# a b
# 0 1 0
# 1 0 1
# 2 0 0
print pd.get_dummies(s1,dummy_na=True) #包含NaN值
#輸出結果如下:
# a b NaN
# 0 1 0 0
# 1 0 1 0
# 2 0 0 1
df=pd.DataFrame({'A':['a','b','a'],'B':['b','a','c'],
'C':[1,2,3]})
print pd.get_dummies(df,prefix=['col1','col2'])
4. 字串操作:
對於更為複雜的模式匹配和文字操作,則可能需要用到正由表示式。pandas對些進行了加強,它使你能夠對陣列資料應用字串表示式和正則表示式,
而且能處理煩人的缺失資料。
python內建的字串方法
(1)split: 拆分,用於以一種格式(,)之類的可以拆分成資料的資料.它常與strip一起使用,用於去除空格
(2)+/join:連線字串,join用的多
(3)in/index/find/count:python子串的定位,in返回True/False;index與find區別是,若沒找到由報異常,find返回-1,找到都返回1.
count用返回子串出現的次數(非重疊),若沒有出現則為0.
(4)replace:用於指字模式替換另一模式
(5)startswith/endswith:如果字串以某個字首/字尾開頭/結尾,則返回True.
(6)rfind:如果在子符串找到子串,則返回第一個發現的子串的第一個字元所在的位置 。如沒找到則返回-1.
(7)strip,rstrip,lstrip:去除空白符
(8)lower,upper:轉換為小寫/大寫
(9)ljust,rjust:用空格(或其它字元)填充字串的空白側以返回符合最低寬度的字串
字串物件方法:
對於大部分字串處理應用而言,內建的字串方法已經夠滿足要求了。例如,以逗號分隔的字串可以用split拆分成數段。
內建python字串的方法舉例:
val='a,b, guido'
print val.split(',')
(1)split常結合strip(用於去除空白)一起使用:
pieces=[x.strip() for x in val.split(',')]
print pieces
#輸出結果如下:
['a', 'b', 'guido']
(2)python:'+'連線各字串(用的少) 或者 join連線字串(join常用)
first,second,third=pieces #將pieces的值分別賦給一個個變數
print first+"::"+second+"::"+third
#輸出結果如下:
# a::b::guido
#可用join達到一樣的效果
print "::".join(pieces)
(3)python子串的定位:in方法,index,find查詢.index未找到會拋異常,而find未找到返回-1。
# count函式用於返回子串出現的次數,若為0未找到
print 'guido' in val #輸出:True
print val.index(',') #輸出:1
print val.find(':') #輸出:-1
print val.count(',') #輸出:2
(4)replace用於指定模式替換為另一個模式
print val.replace(',','::')
#輸出結果如下:
# a::b:: guido
print val.replace(',','')
#輸出結果如下:
# ab guido
正則表示式:
import re
#正則表示式。python內建的re模組負責對字串應用正則表示式
text='foo bar\t baz \tqux'
print re.split('\s+',text)
#輸出結果如下:
# ['foo', 'bar', 'baz', 'qux']
(1)呼叫re.split('\s+',text)時,正則表示式會先被編譯,然後再在text上呼叫其split方法。
你可以用re.compile自己編譯regex以得到一個可重用的regex物件
regex=re.compile('\s+')
print regex.split(text)
#輸出結果如下:
# ['foo', 'bar', 'baz', 'qux']
#如果只希望得到匹配regex的所有模式,則可以使用findall方法:
print regex.findall(text)
#輸出結果如下:
# [' ', '\t ', ' \t']
#如果打算對許多字串應用同一條正則表示式,強列建議通過re.compile建立regex物件,這樣可以節省大量的CPU時間。
(2)match和search跟findall功能類似。findall返回的是字串所有的匹配項,而search則只返回第一個匹配項。match更加嚴格,
#它只匹配字串的首部。
#如果想避免正則表示式中不需要的轉義(\),則可以使用原始字串字面r'
text="""
Dava [email protected]
[email protected]
Rob [email protected]
Ryan [email protected]
"""
pattern=r'[A-Z0-9._%+-]@[A-Z0-9.-]+\.[A-Z]{2,4}'
#re.IGNORECASE的作用是使用正則表示式對大小寫不敏感
regex=re.compile(pattern,flags=re.IGNORECASE)
#對text使用findall將得到一組電子