1. 程式人生 > >pandas資料規整化:清理、轉換、合併、重塑

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

根據連線鍵對合並後的資料進行排序,預設為True.有時處理大資料集時,禁用該選項可獲得更好的效能
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將得到一組電子