ch8_01 資料規整:聚合、合併、重塑
阿新 • • 發佈:2018-12-20
- 在許多應用中,資料可能分散在許多檔案或資料庫中,儲存的形式也不利於分析。本章關注可以聚合、合併、重塑資料的方法。
import pandas as pd
import numpy as np
8.1 層次化索引
- 層次化索引(hierarchical indexing)能在一個軸上擁有多個(兩個以上)索引級別。抽象點說,它使你能以低維度形式處理高維度資料。
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 1.851062
2 0.498509
3 1.552038
b 1 0.839059
3 -0.765026
c 1 -1.431162
2 -1.587057
d 2 -1.012728
3 1.318710
dtype: float64
data.index
MultiIndex(levels=[['a', 'b', 'c', 'd'], [1, 2, 3]], labels=[[0, 0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 2, 0, 1, 1, 2]])
data['a']
1 1.851062
2 0.498509
3 1.552038
dtype: float64
data['b':'c']
b 1 0.839059
3 -0.765026
c 1 -1.431162
2 -1.587057
dtype: float64
data.loc[['b','d']]
b 1 0.839059
3 -0.765026
d 2 -1.012728
3 1.318710
dtype: float64
- 也可以進行內層選取
data[:,2]
a 0.498509 c -1.587057 d -1.012728 dtype: float64
data['a'][2]
0.4985087352895496
- 層次化索引在資料重塑和基於分組的操作(如透視表生成)中扮演著重要的角色。例如,可以通過unstack方法將這段資料重新安排到一個DataFrame中:
data.unstack()
1 | 2 | 3 | |
---|---|---|---|
a | 1.851062 | 0.498509 | 1.552038 |
b | 0.839059 | NaN | -0.765026 |
c | -1.431162 | -1.587057 | NaN |
d | NaN | -1.012728 | 1.318710 |
- unstack()的逆運算是stack()
data.unstack().stack()
a 1 1.851062
2 0.498509
3 1.552038
b 1 0.839059
3 -0.765026
c 1 -1.431162
2 -1.587057
d 2 -1.012728
3 1.318710
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 |
frame['Ohio']
color | Green | Red | |
---|---|---|---|
key1 | key2 | ||
a | 1 | 0 | 1 |
2 | 3 | 4 | |
b | 1 | 6 | 7 |
2 | 9 | 10 |
重排與分級排序
- 需要重新調整某條軸上各級別的順序,或根據指定級別上的值對資料進行排序。swaplevel接受兩個級別編號或名稱,並返回一個互換了級別的新物件(但資料不會發生變化):
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 |
frame.swaplevel('key1', 'key2')
state | Ohio | Colorado | ||
---|---|---|---|---|
color | Green | Red | Green | |
key2 | key1 | |||
1 | a | 0 | 1 | 2 |
2 | a | 3 | 4 | 5 |
1 | b | 6 | 7 | 8 |
2 | b | 9 | 10 | 11 |
- sort_index則根據單個級別中的值對資料進行排序。交換級別時,常常也會用到sort_index,這樣最終結果就是按照指定順序進行字母排序了:
frame.sort_index(level=1)
state | Ohio | Colorado | ||
---|---|---|---|---|
color | Green | Red | Green | |
key1 | key2 | |||
a | 1 | 0 | 1 | 2 |
b | 1 | 6 | 7 | 8 |
a | 2 | 3 | 4 | 5 |
b | 2 | 9 | 10 | 11 |
frame.swaplevel(0,1).sort_index(level=0)
state | Ohio | Colorado | ||
---|---|---|---|---|
color | Green | Red | Green | |
key2 | key1 | |||
1 | a | 0 | 1 | 2 |
b | 6 | 7 | 8 | |
2 | a | 3 | 4 | 5 |
b | 9 | 10 | 11 |
根據級別彙總統計
- 許多對DataFrame和Series的描述和彙總統計都有一個level選項,它用於指定在某條軸上求和的級別
frame.sum(level=1)
state | Ohio | Colorado | |
---|---|---|---|
color | Green | Red | Green |
key2 | |||
1 | 6 | 8 | 10 |
2 | 12 | 14 | 16 |
frame.sum(level=0)
state | Ohio | Colorado | |
---|---|---|---|
color | Green | Red | Green |
key1 | |||
a | 3 | 5 | 7 |
b | 15 | 17 | 19 |
frame.sum(level=1,axis=1)
color | Green | Red | |
---|---|---|---|
key1 | key2 | ||
a | 1 | 2 | 1 |
2 | 8 | 4 | |
b | 1 | 14 | 7 |
2 | 20 | 10 |
利用DataFrame的列進行索引
- 想要將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 |
#set_index函式會將其一個或多個列轉換為行索引,並建立一個新的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中移除,但也可以將其保留下來:
frame.set_index(['c','d'],drop=False)
a | b | c | d | ||
---|---|---|---|---|---|
c | d | ||||
one | 0 | 0 | 7 | one | 0 |
1 | 1 | 6 | one | 1 | |
2 | 2 | 5 | one | 2 | |
two | 0 | 3 | 4 | two | 0 |
1 | 4 | 3 | two | 1 | |
2 | 5 | 2 | two | 2 | |
3 | 6 | 1 | two | 3 |
- reset_index()正好與set_index()相反,層次化的索引會被轉移到列裡面
frame2.reset_index()
c | d | a | b | |
---|---|---|---|---|
0 | one | 0 | 0 | 7 |
1 | one | 1 | 1 | 6 |
2 | one | 2 | 2 | 5 |
3 | two | 0 | 3 | 4 |
4 | two | 1 | 4 | 3 |
5 | two | 2 | 5 | 2 |
6 | two | 3 | 6 | 1 |
8.2 合併資料集
pandas物件中的資料可以通過一些方式進行合併:
- pandas.merge可根據一個或多個鍵將不同DataFrame中的行連線起來。它實現的就是資料庫的join操作。
- pandas.concat可以沿著一條軸將多個物件堆疊到一起。
- 例項方法combine_first可以將重複資料拼接在一起,用一個物件中的值填充另一個物件中的缺失值。
pandas.merge()
- 資料集的合併(merge)或連線(join)運算是通過一個或多個鍵將行連線起來的。這些運算是關係型資料庫(基於SQL)的核心.
df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],'data1': range(7)})
df2 = pd.DataFrame({'key':['a','b','c'],'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 | c | 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 |
6 | c | 3 | 2 |
- 上述程式碼並沒有指明使用哪個列進行連線,這時候預設是將重疊的列名當作鍵。不過最好指明
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 |
6 | c | 3 | 2 |
- 如果兩個物件的列名不同,也可以分別進行指定:
df3 = pd.DataFrame({'lkey': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1': range(7)})
df4 = pd.DataFrame({'rkey': ['a', 'b', 'd'], 'data2': range(3)})
df3
lkey | data1 | |
---|---|---|
0 | b | 0 |
1 | b | 1 |
2 | a | 2 |
3 | c | 3 |
4 | a | 4 |
5 | a | 5 |
6 | b | 6 |
df4
rkey | data2 | |
---|---|---|
0 | a | 0 |
1 | b | 1 |
2 | d | 2 |
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 | 1 |
1 | b | 1 | 1 |
2 | b | 6 | 1 |
3 | a | 2 | 0 |
4 | a | 4 | 0 |
5 | a | 5 | 0 |
6 | c | 3 | 2 |
- 要根據多個鍵進行合併,傳入一個由列名組成的list 即可
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 |
left
key1 | key2 | lval | |
---|---|---|---|
0 | foo | one | 1 |
1 | foo | two | 2 |
2 | bar | one | 3 |
right
key1 | key2 | rval | |
---|---|---|---|
0 | foo | one | 4 |
1 | foo | one | 5 |
2 | bar | one | 6 |
3 | bar | two | 7 |
- 對於合併運算需要考慮的對重複列名的處理。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的引數如下:
索引上的合併
- 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 |
- 預設的merge方法是求取連線鍵的交集,因此你可以通過外連線的方式得到它們的並集:
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.