本博客爲《利用Python進行數據分析》的讀書筆記,請勿轉載用於其他商業用途。
文章目錄
1. 分層索引
分層索引是pandas的重要特性,允許你再一個軸向上擁有多個(兩個或兩個以上)索引層級。籠統地說,分層索引提供了一種更低維度的形式中處理更高維度數據的方式。例:
data = pd.Series(np.random.randn(9),
index=[['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'd'],
[1, 2, 3, 1, 3, 1, 2, 2, 3]])
data
#
a 1 0.084340
2 1.252705
3 -1.305060
b 1 0.629035
3 -1.099427
c 1 -0.785977
2 -0.524298
d 2 0.144326
3 0.945895
dtype: float64
我們看到的是一個以MultiIndex
作爲索引的Series的美化視圖。索引中的“間隙”表示“直接使用上面的標籤”:
data.index
#
MultiIndex(levels=[['a', 'b', 'c', 'd'], [1, 2, 3]],
codes=[[0, 0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 2, 0, 1, 1, 2]])
通過分層索引對象,也可以成爲部分索引,允許你簡潔地選擇出數據的子集:
data['b']
#
1 0.629035
3 -1.099427
dtype: float64
data['b': 'c']
#
b 1 0.629035
3 -1.099427
c 1 -0.785977
2 -0.524298
dtype: float64
data.loc[['b', 'd']]
#
b 1 0.629035
3 -1.099427
d 2 0.144326
3 0.945895
dtype: float64
在“內部”層級中進行選擇也是可以的:
data.loc[:, 2]
#
a 1.252705
c -0.524298
d 0.144326
dtype: float64
分層索引在重塑數據和數組透視表等分組操作中扮演了重要角色。例如,你可以使用unstack
方法將數據在DataFrame中重新排列:
data.unstack()
#
1 2 3
a 0.084340 1.252705 -1.305060
b 0.629035 NaN -1.099427
c -0.785977 -0.524298 NaN
d NaN 0.144326 0.945895
unstack
的反操作是stack
:
data.unstack().stack()
#
a 1 0.084340
2 1.252705
3 -1.305060
b 1 0.629035
3 -1.099427
c 1 -0.785977
2 -0.524298
d 2 0.144326
3 0.945895
dtype: float64
在DataFrame中,每個軸都可以擁有分層索引:
frame = pd.DataFrame(np.arange(12).reshape((4, 3)),
index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]],
columns=[['Ohio', 'Ohio', 'Colorado'],
['Green', 'Red', 'Green']])
frame
#
Ohio Colorado
Green Red Green
a 1 0 1 2
2 3 4 5
b 1 6 7 8
2 9 10 11
分層的層級可以有名稱(可以是字符串或Python對象)。如果層級有名稱,這些名稱會在控制檯輸出中顯示:
frame.index.names = ['key1', 'key2']
frame.columns.names = ['state', 'color']
frame
#
state Ohio Colorado
color Green Red Green
key1 key2
a 1 0 1 2
2 3 4 5
b 1 6 7 8
2 9 10 11
注意區分行標籤中的索引名稱’state’和’color’。
通過部分列索引,你可以選出列中的組:
frame['Ohio']
#
color Green Red
key1 key2
a 1 0 1
2 3 4
b 1 6 7
2 9 10
1.1 重排序和層級排序
有時,我們需要重新排列軸上的層級順序,或者按照特定層級的值對數據進行排序。swaplevel
接收兩個層級序號或層級名稱,返回一個進行了層級變更的新對象(但是數據是不變的):
frame.swaplevel('key1', 'key2')
#
state Ohio Colorado
color Green Red Green
key1 key2
a 1 0 1 2
2 3 4 5
b 1 6 7 8
2 9 10 11
另一方面,sort_index
只能在單一層級上對數據進行排序。在進行層級變換時,使用sort_index
以是的結果按照層級進行字典排序也很常見:
frame.sort_index(level=1)
frame.swaplevel(0, 1).sort_index(level=0)
如果索引按照字典順序從最外層開始排序,那麼數據選擇性能會更好——調用sort_index(level=0)
或sort_index
可以得到這樣的結果。
1.2 按層級進行彙總統計
通常我們不會使用DataFrame中的一個或多個列作爲行索引;反而你可能想要將行索引移動到DataFrame的列中。下面是一個示例:
frame = pd.DataFrame({'a': range(7), 'b': range(7, 0, -1),
'c': ['one', 'one', 'one', 'two', 'two',
'two', 'two'],
'd': [0, 1, 2, 0, 1, 2, 3]})
frame
#
a b c d
0 0 7 one 0
1 1 6 one 1
2 2 5 one 2
3 3 4 two 0
4 4 3 two 1
5 5 2 two 2
6 6 1 two 3
DataFrame的set_index
函數會生成一個新的DataFrame,新的DataFrame使用一個或多個列作爲索引:
frame2 = frame.set_index(['c', 'd'])
frame2
#
a b
c d
one 0 0 7
1 1 6
2 2 5
two 0 3 4
1 4 3
2 5 2
3 6 1
默認情況下這些列會從DataFrame中移除,你也可以將它們留在DataFrame中:
frame.set_index(['c', 'd'], drop=False)
#
另一方面,reset_index
是set_index
的反操作,分層索引的索引層級會被移動到列中:
2. 聯合與合併數據集
包含在pandas對象的數據可以通過多種方式聯合在一起:
pandas.merge
根據一個或多個鍵將行進行連接。對於SQL或其他關係數據庫的用戶來說,這種方式比較熟悉,它實現的是數據庫的連接操作。pandas.concat
使對象在軸向上進行黏合或“堆疊”。combine_first
實例方法允許將重疊的數據拼接在一起,以使用一個對象中的值填充一個對象中的缺失值。
2.1 數據庫風格的DataFrame連接
合併 或 連接 操作通過一個或多個鍵連接行來聯合數據集。這些操作時關係型數據庫的核心內容(例如基於SQL的數據庫)。pandas中的merge
函數主要用於將各種join操作算法運用在數據上:
df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data1': range(7)})
df2 = pd.DataFrame({'key': ['a', 'b', 'd'],
'data2': range(3)})
df1
#
key data1
0 b 0
1 b 1
2 a 2
3 c 3
4 a 4
5 a 5
6 b 6
df2
#
key data2
0 a 0
1 b 1
2 d 2
這是一個 多對一 的例子;df1的數據有多個行的標籤爲a和b,而df2在key列中每個值僅有一行。調用merge
處理我們獲得的對象:
pd.merge(df1, df2)
#
key data1 data2
0 b 0 1
1 b 1 1
2 b 6 1
3 a 2 0
4 a 4 0
5 a 5 0
請注意,我們並沒有指定在哪一列上進行連接。如果連接的鍵信息沒有指定,merge
會自動將重疊列名作爲連接的鍵。但是,顯式地指定連接鍵纔是好的實現:
pd.merge(df1, df2, on='key')
結果與之前相同。
如果每個對象的列名是不同的,可以分別爲它們指定列名:
df3 = pd.DataFrame({'lkey': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data1': range(7)})
df4 = pd.DataFrame({'rkey': ['a', 'b', 'd'],
'data2': range(3)})
pd.merge(df3, df4, left_on='lkey', right_on='rkey')
#
lkey data1 rkey data2
0 b 0 b 1
1 b 1 b 1
2 b 6 b 1
3 a 2 a 0
4 a 4 a 0
5 a 5 a 0
我們可以發現結果中缺少‘c’和‘d’的值以及相關的數據。默認情況下merge
做的是內連接,結果中的鍵是兩張表的交集。其他可選的選項有‘left’、‘right’和‘outer’。外連接是鍵的交集,聯合了左連接和右連接的效果:
pd.merge(df1, df2, how='outer')
#
key data1 data2
0 b 0.0 1.0
1 b 1.0 1.0
2 b 6.0 1.0
3 a 2.0 0.0
4 a 4.0 0.0
5 a 5.0 0.0
6 c 3.0 NaN
7 d NaN 2.0
表: how參數的不同連接類型
選項 | 行爲 |
---|---|
inner |
只對兩張表都有的鍵的交集進行聯合 |
left |
對所有左表的鍵進行聯合 |
right |
對所有右表的鍵進行聯合 |
outer |
對兩張表都有的鍵的並集進行聯合 |
儘管不是很直觀,但 多對多 的合併有明確的行爲。下面是一個例子:
df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data1': range(7)})
df2 = pd.DataFrame({'key': ['a', 'b', 'd'],
'data2': range(3)})
df1
#
key data1
0 b 0
1 b 1
2 a 2
3 c 3
4 a 4
5 a 5
6 b 6
df2
#
key data2
0 a 0
1 b 1
2 d 2
pd.merge(df1, df2)
#
key data1 data2
0 b 0 1
1 b 1 1
2 b 6 1
3 a 2 0
4 a 4 0
5 a 5 0
多對多連接是行的笛卡爾積。由於在左邊的DataFrame中有三個’b’行,而在右邊有兩行,因此在結果中有6個’b’行。連接方法僅影響結果中顯示的不同鍵值:
pd.merge(df1, df2, on='key')
#
key data1 data2
0 b 0 1
1 b 1 1
2 b 6 1
3 a 2 0
4 a 4 0
5 a 5 0
使用多個鍵進行合併時,傳入一個列名的列表:
left = pd.DataFrame({'key1': ['foo', 'foo', 'bar'],
'key2': ['one', 'two', 'one'],
'lval':[1, 2, 3]})
right = pd.DataFrame({'key1': ['foo', 'foo', 'bar', 'bar'],
'key2': ['one', 'one', 'one', 'two'],
'rval':[4, 5, 6, 7]})
pd.merge(left, right, on=['key1', 'key2'], how='outer')
結果如下:
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
決定哪些鍵聯合出現在結果中,取決於合併方法的選擇,把多個鍵看作一個元組數據來作爲單個連接鍵使用(儘管實際上並不是以這種方法來實現的)。
警告:當你再進行列—列連接時,傳遞的DataFrame索引對象會被丟棄。
合併操作中最後一個要考慮的問題是如何處理重疊的列名。雖然你可以手動解決重疊問題,但是merge
有一個suffixes
後綴選項,用於在左右兩邊DataFrame對象的重疊列名後指定需要添加的字符串:
pd.merge(left, right, on='key1')
#
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
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
表:merge函數參數
參數 | 描述 |
---|---|
left | 合併時操作中左邊的DataFrame |
right | 合併時操作中右邊的DataFrame |
how | inner 、outer 、left 、right 之一;默認是inner |
on | 需要連接的列名。必須是在兩邊的DataFrame對象都有的列名,並以left和right中的列名的交集作爲連接鍵 |
left_on | left DataFrame中用作連接鍵的列 |
right_on | right DataFrame中用作連接鍵的列 |
left_index | 使用left行索引作爲它的連接鍵(如果是MultiIndex,則是多個鍵) |
right_index | 使用right行索引作爲它的連接鍵(如果是MultiIndex,則是多個鍵) |
sort | 通過連接鍵字母順序合併的數據進行排序;在默認情況下爲True(在大數據集上某些情況下禁用該功能可以獲得更好的性能) |
suffixes | 在重疊情況下,添加到列名後的字符串元組;默認是(’_x’,’_y’)(例如如果待合併的DataFrame中都含有’data’列,那麼結果中會出現’data_x’、‘data_y’) |
copy | 如果爲False,則在某些特殊情況下避免將數據複製到結果數據結構中;默認情況下總是複製 |
indicator | 添加一個特殊的列_merge,只是每一行的來源;值將根據每行中連接數據的來源分別爲left_only 、right_only 或both |
2.2 根據索引合併
在某些情況下,DataFrame中用於合併的鍵是它的索引。在這種情況下,你可以傳遞left_index=True
或right_index=True
(或者都傳)來表示索引需要用來作爲合併的鍵:
left1 = pd.DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'],
'value': range(6)})
right1 = pd.DataFrame({'group_val': [3.5, 7]}, index=['a', 'b'])
left1
#
key value
0 a 0
1 b 1
2 a 2
3 a 3
4 b 4
5 c 5
right1
#
group_val
a 3.5
b 7.0
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
由於默認的合併方法是連接鍵相交,可以使用外連接來進行合併:
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
在多層索引數據的情況下,事情會更復雜,在索引上連接是一個隱式的多鍵合併:
lefth = pd.DataFrame({'key1': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
'key2': [2000, 2001, 2002, 2001, 2002],
'data': np.arange(5.)})
righth = pd.DataFrame(np.arange(12).reshape((6, 2)),
index=[['Nevada', 'Nevada', 'Ohio','Ohio','Ohio','Ohio'],
[2001, 2000, 2000, 2000, 2001, 2002]],
columns=['event1', 'event2'])
lefth
#
key1 key2 data
0 Ohio 2000 0.0
1 Ohio 2001 1.0
2 Ohio 2002 2.0
3 Nevada 2001 3.0
4 Nevada 2002 4.0
righth
#
event1 event2
Nevada 2001 0 1
2000 2 3
Ohio 2000 4 5
2000 6 7
2001 8 9
2002 10 11
這種情況下,你必須以列表的方式指明合併所需多個列(請注意使用how='outer'
處理重複的索引值):
pd.merge(lefth, righth, left_on=['key1', 'key2'],right_index=True)
#
key1 key2 data event1 event2
0 Ohio 2000 0.0 4 5
0 Ohio 2000 0.0 6 7
1 Ohio 2001 1.0 8 9
2 Ohio 2002 2.0 10 11
3 Nevada 2001 3.0 0 1
pd.merge(lefth, righth, left_on=['key1', 'key2'],right_index=True, how='outer')
#
key1 key2 data event1 event2
0 Ohio 2000 0.0 4.0 5.0
0 Ohio 2000 0.0 6.0 7.0
1 Ohio 2001 1.0 8.0 9.0
2 Ohio 2002 2.0 10.0 11.0
3 Nevada 2001 3.0 0.0 1.0
4 Nevada 2002 4.0 NaN NaN
4 Nevada 2000 NaN 2.0 3.0
使用兩邊的索引進行合併也是可以的:
left2 = pd.DataFrame([[1., 2.], [3., 4.], [5., 6.]],
index=['a', 'c', 'e'],
columns=['Ohio', 'Nevada'])
right2 = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [13, 14]],
index=['b', 'c', 'd', 'e'],
columns=['Missouri', 'Alabama'])
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
pd.merge(left2, right2, how='outer', left_index=True, right_index=True)
#
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
DataFrame有一個方便的join
實例方法,用於按照索引合併。該方法也可以用於合併多個索引相同或相似但沒有重疊列的DataFrame對象:
left2.join(right2, how='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
最後,對於一些簡單索引—索引合併,你可以向join
方法傳入一個DataFrame列表,這個方法可以替代使用更爲通用的concat
方法:
another = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [16, 17]],
index=['a', 'c', 'e', 'f'],
columns=['New York', 'Oregon'])
another
#
New York Oregon
a 7.0 8.0
c 9.0 10.0
e 11.0 12.0
f 16.0 17.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
left2.join([right2, another], sort=True, how='outer')
#
Ohio Nevada Missouri Alabama New York Oregon
a 1.0 2.0 NaN NaN 7.0 8.0
b NaN NaN 7.0 8.0 NaN NaN
c 3.0 4.0 9.0 10.0 9.0 10.0
d NaN NaN 11.0 12.0 NaN NaN
e 5.0 6.0 13.0 14.0 11.0 12.0
f NaN NaN NaN NaN 16.0 17.0
由於輸出結果列表調整對齊實在是太複雜了,下一節開始,對於數據內容較多的表格,我們就用圖片的模式來展示代碼執行的結果。
2.3 沿軸向連接
另一種數據組合操作可互換地稱爲拼接、綁定或堆疊。Numpy的concatenate
函數可以在Numpy數組上實現該功能:
arr = np.arange(12).reshape((3, 4))
arr
#
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
np.concatenate([arr, arr], axis=1)
#
array([[ 0, 1, 2, 3, 0, 1, 2, 3],
[ 4, 5, 6, 7, 4, 5, 6, 7],
[ 8, 9, 10, 11, 8, 9, 10, 11]])
在Series和DataFrame等pandas對象的上下文中,使用標記的軸可以進一步泛化數組連接。尤其是你還有許多需要考慮的事情:
- 如果對象在其他軸上的索引不同,我們是否應該將不同的元素組合在這些軸上,還是隻使用共享的值(交集)?
- 連接的數據塊是否需要在結果對象中被識別?
- “連接軸”是否包含需要保存的數據?在許多情況下,DataFrame中的默認整數標籤在連接期間最好丟棄。
pandas的concat
函數提供了一種一致性的方式來解決以上問題。我們將給出一些例子來表明它的工作機制。假設我們有三個索引不存在重疊的Series:
s1 = pd.Series([0, 1], index=['a', 'b'])
s2 = pd.Series([2, 3, 4], index=['c', 'd', 'e'])
s3 = pd.Series([5, 6], index=['f', 'g'])
用列表中的這些對象調用concat
方法會將值和索引粘在一起:
pd.concat([s1, s2, s3])
#
a 0
b 1
c 2
d 3
e 4
f 5
g 6
dtype: int64
默認情況下,concat
方法是沿着axis=0
的軸向生效的,生成另一個Series。如果你傳遞axis=1
,返回的結果則是一個DataFrame(axis=1
時是列):
在運行的時候,Anaconda提示我們需要加上sort=True
。
在這個案例中另一個軸向上並沒有重疊,你可以看到排序後的索引合集('outer'join
外連接)。你也可以傳入join='inner'
:
s4 = pd.concat([s1, s3])
s4
#
a 0
b 1
f 5
g 6
dtype: int64
pd.concat([s1, s4], axis=1, sort=True)
#
0 1
a 0.0 0
b 1.0 1
f NaN 5
g NaN 6
pd.concat([s1, s4], axis=1, join='inner', sort=True)
#
0 1
a 0 0
b 1 1
在這個例子中,由於join='inner'
的選項,'f’和’g’的標籤消失了。
你甚至可以使用join_axes
來指定用於連接其他軸向的軸:
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 1.0
e NaN NaN
拼接在一起的各部分無法在結果中區分是一個潛在的問題。假設你想在連接軸向上創建一個多層索引,可以使用keys
參數來實現:
沿着軸向axis=1
連接Series的時候,keys
則成爲DataFrame的列頭:
將相同的邏輯拓展到DataFrame對象:
df1 = pd.DataFrame(np.arange(6).reshape((3, 2)),index=['a', 'b', 'c'],
columns=['one', 'two'])
df2 = pd.DataFrame(5 + np.arange(4).reshape((2, 2)),index=['a', 'c'],
columns=['three', 'four'])
df1
#
one two
a 0 1
b 2 3
c 4 5
df2
#
three four
a 5 6
c 7 8
如果你傳遞的是對象的字典而不是列表的話,則字典的鍵會用於keys
選項:
還有一些額外的參數負責多層索引生成。例如,我們可以使用names
參數命名生成的軸層級:
最後需要考慮的是行索引中不包含任何相關數據的DataFrame:
在這個實例中,我們傳入ignore_index=True
:
參數 | 描述 |
---|---|
objs | 需要連接的pandas對象列表或字典;這是必選參數 |
axis | 連接的軸向;默認是0(沿着行方向) |
join | 可以是inner 或outer (默認是outer );用於指定連接方式是內連接(inner)還是外連接(outer) |
join_axes | 用於指定其他n-1軸的特定索引,可以替代內/外連接的邏輯 |
keys | 與要連接的對象關聯的值,沿着連接軸形成分層索引;可以是任意值的列表或數組,也可以是元組的數組,也可以是數組的列表(如果向levels參數傳入多層數組) |
leels | 在鍵值傳遞時,該參數用於指定多層索引的層級 |
names | 如果傳入了keys和/或levels參數,該參數用於多層索引的層級名稱 |
verify_intergrity | 檢查連接對象中的新軸是否重複,如果是,則引發異常;默認(False)允許重複 |
ingore_index | 不沿着連接軸保留索引,而產生一段新的(長度爲total_length)索引 |
2.4 聯合重疊數據
還有另一個數據聯合場景,既不是合併操作,也不是連接操作。你可能有兩個數據集,這兩個數據集的索引全部或部分重疊。作爲一個示例,考慮Numpy的where
函數,這個函數可以進行面向數組的if-else
等價操作:
a = pd.Series([np.nan, 2.5, 0.0, 3.5, 4.5, np.nan],
index=['f', 'e', 'd', 'c', 'b', 'a'])
b = pd.Series([0, np.nan, 2, np.nan, np.nan, 5.],
index=['a', 'b', 'c', 'd', 'e', 'f'])
a
#
f NaN
e 2.5
d 0.0
c 3.5
b 4.5
a NaN
dtype: float64
b
#
a 0.0
b NaN
c 2.0
d NaN
e NaN
f 5.0
dtype: float64
np.where(pd.isnull(a), b, a)
#
array([0. , 2.5, 0. , 3.5, 4.5, 5. ])
Series有一個combine_first
方法,該方法可以等價於下面這種使用pandas常見數據對齊邏輯的軸向操作:
b.combine_first(a)
#
a 0.0
b 4.5
c 2.0
d 0.0
e 2.5
f 5.0
dtype: float64
我們可以發現,上述代碼是將a中的數據傳入b中,如果b的行中數據缺失,則使用a中的數據。如果已存在,則保留原來的數據。
在DataFrame中,combine_first
逐列做相同的操作,因此你可以認爲它是根據你傳入的對象來“修補”調用對象的缺失值:
df1 = pd.DataFrame({'a': [1., np.nan, 5., np.nan],
'b': [np.nan, 2., np.nan, 6.],
'c': range(2, 18, 4)})
df2 = pd.DataFrame({'a': [5., 4., np.nan, 3., 7.],
'b': [np.nan, 3., 4., 6., 8.],})
8.3 重塑和透視
重排列表格類型數據有多種基礎操作。這些操作被稱爲重塑或透視。
3.1 使用多層索引進行重塑
多層索引在DataFrame中提供了一種一致性方式用於重排列數據。以下是兩個基礎操作:
stack(堆疊) 該操作會“旋轉”或將列中的數據透視到行
unstack(拆堆) 該操作會將行中的數據透視到列
下面考慮一個帶有字符串數組作爲行和列索引的小型DataFrame:
在這份數據上使用stack
方法會將列透視到行,產生一個新的Series:
從一個多層索引序列中,你可以使用unstack
方法將數據重新排列後放入一個DataFrame中:
默認情況下,最內層是已拆堆的(與unstack
方法一樣)。你可以傳入一個層級序列號或名稱來拆分一個不同的層級:
如果層級中的所有值並未包含於每個子分組中時,拆分可能會引入缺失值:
s1 = pd.Series([0, 1, 2, 3], index=['a', 'b', 'c', 'd'])
s2 = pd.Series([4, 5, 6], index=['c', 'd', 'e'])
data2 = pd.concat([s1, s2], keys=['one', 'two'])
默認情況下,堆疊會過濾出缺失值,因此堆疊拆堆的操作時可逆的:
當你再DataFrame中拆堆時,被拆堆的層級會變爲結果中最低的層級:
df = pd.DataFrame({'left': result, 'right': result + 5},
columns=pd.Index(['left', 'right'], name='side'))
在調用stack
方法時,我們可以指明需要堆疊的軸向名稱:
3.2 將“長”透視爲“寬”
在數據庫和CSV中存儲多時間序列的方式就是所謂的長格式或堆疊格式。讓我們載入一些實例數據,然後做少量的時間序列規整和其他的數據清洗操作:
data = pd.read_csv('macrodata.csv')
我們將在之後更深入講解PeriodIndex
。簡單地說,PeriodIndex
將year和quarter等列進行聯合並生成了一種時間間隔類型。這種數據即所謂的多時間序列的長格式,或稱爲具有兩個或更多個鍵的其他觀測數據(這裏,我們的鍵是data和item)。表中的每一行表示一個時間點上的單個觀測值。
數據通常以這種方式存儲在關係型數據庫中,比如MySQL,因爲固定模式(列名稱和數據類型)允許item列中不同值的數量隨着數據被添加到表中而改變。在之前的例子中,data和item通常是主鍵(使用關係型數據庫的說法),提供了關係完整性和更簡單的連接。在某些情況下,處理這種格式的數據更爲困難,你可能更傾向於獲取一個按date列時間戳索引的每個不同的item獨立一列的DataFrame。DataFrame的pivot
方法就是進行這種轉換的。
pivoted = ldata.pivot('date', 'item', 'value')
pivoted
傳遞的前兩個值分別用作行和列索引的列,然後是可選的數值列以填充DataFrame。假設你有兩個數列值,你想同時進行重塑:
ldata['value2'] = np.random.randn(len(ldata))
ldata[:10]
如果遺漏最後一個參數,你會得到一個含有多層列的DataFrame:
pivoted = ldata.pivot('date', 'item')
pivoted[:5]
pivoted['value'][:5]
請注意,pivot
方法等價於使用set_index
創建分層索引,然後調用unstack
:
unstacked = ldata.set_index(['date', 'item']).unstack('item')
unstacked[:7]
3.3 將“寬”透視爲“長”
在DataFrame中,pivot
方法的反操作時pandas.melt
。與將一列變換爲新的DataFrame中的多列不同,它將多列合併成一列,產生一個新的DataFrame,其長度比輸入更長。例:
df = pd.DataFrame({'key': ['foo', 'bar', 'baz'],
'A': [1, 2, 3],
'B': [4, 5, 6],
'C': [7, 8, 9]})
df
#
key A B C
0 foo 1 4 7
1 bar 2 5 8
2 baz 3 6 9
‘key’列可以作爲分組標籤,其他列均爲數據值。當使用pandas.melt
時,我們必須指明哪些列是分組指標(如果有的話)。此處,讓我們使用’key’作爲唯一的組指標:
melted = pd.melt(df, ['key'])
melted
使用pivot
方法,我們可以將數據重塑回原先的佈局:
reshaped = melted.pivot('key', 'variable', 'value')
reshaped
由於pivot
的結果根據作爲行標籤的列生成了索引,我們可能會將想要使用reset_index
來將數據回移一列:
reshaped.reset_index()
你也可以指定列的子集作爲列值:
pd.melt(df, id_vars=['key'], value_vars=['A', 'B'])
pandas.melt
的使用也可以無須任何分組指標: