1. 程式人生 > >第八篇2 資料規整:聚合、合併和重塑

第八篇2 資料規整:聚合、合併和重塑

在許多應⽤中,資料可能分散在許多⽂件或資料庫中,儲存的形式也不利於分析。本章關注可以聚合、合併、重塑資料的⽅法。⾸先,介紹pandas的層次化索引,它⼴泛⽤於以上操作。然後,深⼊介紹了⼀些特殊的資料操作。

一、層次化索引
層次化索引hierarchical indexing)是pandas的⼀項重要功能,它使你能在⼀個軸上擁有多個(兩個以上)索引級別。抽象點說,它使你能以低維度形式處理⾼維度資料。我們先來看⼀個簡單的例⼦:建立⼀個Series,並⽤⼀個由列表或陣列組成的列表作為索引:
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.007189
     2   -1.296221
     3    0.274992
b  1    0.228913
     3    1.352917
c  1    0.886429
     2   -2.001637
d  2   -0.371843
     3    1.669025
dtype: float64
看到的結果是經過美化的帶有MultiIndex索引的Series的格式。索引之間的“間隔”表示“直接使⽤上⾯的標籤

”:
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['b']        # 輸出如下:
1    0.228913
3    1.352917
dtype: float64
data['b':'c']    # 輸出如下:
b  1    0.228913
     3    1.352917
c  1    0.886429
     2   -2.001637
dtype: float64
data.loc[['b', 'd']]    # 輸出如下:
b  1    0.228913
     3    1.352917
d  2   -0.371843
     3    1.669025
dtype: float64
有時甚⾄還可以在“內層”
中進⾏選取:
data.loc[:, 2]    # 輸出如下:(選取帶2的標籤)
a   -1.296221
c   -2.001637
d   -0.371843
dtype: float64

層次化索引在資料重塑和基於分組的操作(如透視表⽣成)中扮演著重要的⻆⾊。例如,可以通過unstack⽅法將這段資料重新安排到⼀個DataFrame中:
data.unstack()    # 輸出如下:
                1              2             3
a  1.007189 -1.296221  0.274992
b  0.228913         NaN   1.352917
c  0.886429 -2.001637         NaN
d         NaN  -0.371843  1.669025
unstack的逆運算是stack


data.unstack().stack()    # 輸出如下:
a  1    1.007189
     2   -1.296221
     3    0.274992
b  1    0.228913
     3    1.352917
c  1    0.886429
     2   -2.001637
d  2   -0.371843
     3    1.669025
dtype: float64
stack和unstack將在後⾯詳細講解。

對於⼀個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
可以單獨建立MultiIndex然後復⽤。上⾯那個DataFrame中的(帶有分級名稱)列可以這樣建立:
MultiIndex.from_arrays([['Ohio', 'Ohio', 'Colorado'], ['Green', 'Red', 'Green']],
                                         names=['state', 'color'])

1、重排與分級排序
有時,你需要重新調整某條軸上各級別的順序,或根據指定級別上的值對資料進⾏排序。swaplevel接受兩個級別編號或名稱,並返回⼀個互換了級別的新物件(但資料不會發⽣變化):
frame.swaplevel('key1', 'key2')    # 輸出如下:(互換'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)    # 輸出如下:(對第二個索引排序('key2'))
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


2、根據級別彙總統計
許多對DataFrame和Series的描述和彙總統計都有⼀個level選項,它⽤於指定在某條軸上求和的級別。再以上⾯那個DataFrame為例,我們可以根據⾏或列上的級別來進⾏求和:
frame.sum(level='key2')    # 輸出如下:(注意level引數後面給的是行索引或列索引的屬性名稱)
state      Ohio          Colorado
color   Green Red    Green
key2
1               6     8       10
2            12    14       16
frame.sum(level='color', axis=1)    # 輸出如下:(根據相同的顏色(Green,Red)按行求和)
color             Green  Red
key1    key2
a               1         2      1
                  2         8      4
b              1        14      7
                 2        20     10
這其實是利⽤了pandas的groupby功能,稍後將對其進⾏詳細講解。


3、使⽤DataFrame的列進⾏索引
經常要將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
DataFrame的set_index函式會將其⼀個或多個列轉換為⾏索引,並建立⼀個新的DataFrame:
frame2 = frame.set_index(['c', 'd'])    # 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


二、合併資料集
pandas物件中的資料可以通過⼀些⽅式進⾏合併:
        pandas.merge可根據⼀個或多個鍵將不同DataFrame中的⾏連線起來。SQL或其他關係型資料庫的⽤戶對此應該會⽐較熟悉,因為它實現的就是資料庫的join操作。
       pandas.concat可以沿著⼀條軸將多個物件堆疊到⼀起。例項⽅法combine_first可以將重複資料編接在⼀起,⽤⼀個物件中的值填充另⼀個物件中的缺失值。

接下來對它們進⾏講解,並給出⼀些例⼦。後面部分的示例中將經常⽤到它們。


1、資料庫⻛格的DataFrame合併
資料集的合併(merge)或連線(join)運算是通過⼀個或多個鍵將⾏連結起來的。這些運算是關係型資料庫(基於SQL)的核⼼。pandas的merge函式是對資料應⽤這些演算法的主要切⼊點。

以⼀個簡單的例⼦開始:
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')    # 輸出如下:
   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

如果兩個物件的列名不同,也可以分別進⾏指定:
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

表8-1對這些選項進⾏了總結
選項         說明
'inner'     使用兩個表都有的鍵(預設)
'left'        使用左表中所有的鍵
'right'     使用右表中所有的鍵
'outer'    使用兩個表中所有的鍵

多對多的合併有些不直觀。看下⾯的例⼦:
df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'], 'data1': range(6)})
df2 = pd.DataFrame({'key': ['a', 'b', 'a', 'b', 'd'], 'data2': range(5)})
df1        # 輸出如下:
   key  data1
0   b      0
1   b      1
2   a      2
3   c      3
4   a      4
5   b      5
df2        # 輸出如下:
   key  data2
0   a      0
1   b      1
2   a      2
3   b      3
4   d      4
pd.merge(df1, df2, on='key', how='left')    # 輸出如下:
    key  data1  data2
0    b         0       1.0
1    b         0       3.0
2    b         1       1.0
3    b         1       3.0
4    a         2       0.0
5    a         2       2.0
6    c         3     NaN
7    a         4       0.0
8    a         4       2.0
9    b         5       1.0
10   b        5       3.0
多對多連線產⽣的是⾏的笛卡爾積。由於左邊的DataFrame有3個"b"⾏,右邊的有2個,所以最終結果中就有6個"b"⾏。連線⽅式隻影響出現在結果中的不同的鍵的值:
pd.merge(df1, df2, how='inner')    # 輸出如下:(等同於:pd.merge(df1, df2))
   key  data1  data2
0   b      0      1
1   b      0      3
2   b      1      1
3   b      1      3
4   b      5      1
5   b      5      3
6   a      2      0
7   a      2      2
8   a      4      0
9   a      4      2

要根據多個鍵進⾏合併,傳⼊⼀個由列名組成的列表即可:
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

表8-2 merge函式的引數

image


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
由於預設的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.], [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
因為⼀些歷史版本的遺留原因,DataFrame的join⽅法預設使⽤的是左連線,保留左邊表的⾏索引。它還⽀持在調⽤的DataFrame的列上,連線傳遞的DataFrame索引:
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

最後,對於簡單的索引合併,還可以向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], 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


3、軸向連線
另⼀種資料合併運算也被稱作連線(concatenation)、繫結(binding)或堆疊(stacking)
NumPy的concatenation函式可以⽤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]])

對於pandas物件(如Series和DataFrame),帶有標籤的軸使你能夠進⼀步推⼴陣列的連線運算。
具體點說,你還需要考慮以下這些東⻄:
     如果物件在其它軸上的索引不同,我們應該合併這些軸的不
         同元素還是隻使⽤交集?
     連線的資料集是否需要在結果物件中可識別?
     連線軸中儲存的資料是否需要保留?許多情況下,
         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是列):
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
這種情況下,另外的軸上沒有重疊,從索引的有序並集(外連線)上就可以看出來。
傳⼊join='inner'即可得到它們的交集:
s4 = pd.concat([s1, s3])   
s4            # 輸出如下:
a    0
b    1
f     5
g    6
dtype: int64
pd.concat([s1, s4], axis=1)    # 輸出如下:(並集)
         0   1
a    0.0   0
b    1.0   1
f   NaN  5
g  NaN  6
pd.concat([s1, s4], axis=1, join='inner')    # 輸出如下:(交集)
     0  1
a  0  0
b  1  1
在這個例⼦中,f和g標籤消失了,是因為使⽤的是join='inner'選項。
你可以通過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引數即可達到這個⽬的:
result = pd.concat([s1, s1, s3], keys=['one', 'two', 'three'])    # 層次化索引
result        # 輸出如下:
one    a    0
           b    1
two    a    0
           b    1
three  f     5
           g    6
dtype: int64
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
如果沿著axis=1對Series進⾏合併,則keys就會成為DataFrame的列頭:
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

同樣的邏輯也適⽤於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
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
如果傳⼊的不是列表⽽是⼀個字典,則字典的鍵就會被當做keys選項的值:
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

此外還有兩個⽤於管理層次化索引建立⽅式的引數(參⻅表8-3)。舉個例⼦,我們可以⽤names引數命名建立的軸級別:
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
最後⼀個關於DataFrame的問題是,DataFrame的⾏索引不包含任何相關資料:
df1 = pd.DataFrame(np.random.randn(3, 4), columns=['a', 'b', 'c', 'd'])
df2 = pd.DataFrame(np.random.randn(2, 3), columns=['b', 'd', 'a'])
df1        # 輸出如下
                 a               b                c               d
0  1.721685 -0.160918 -2.419580 -1.634277
1 -1.039395 -0.595996  0.788547  0.455259
2  0.342471 -0.118662  1.402167  0.280451
df2        # 輸出如下
                 b                d             a
0  0.236757 -0.909635  0.986049
1 -0.038411 -0.034186 -0.879236
在這種情況下,傳⼊ignore_index=True即可:
pd.concat([df1, df2], ignore_index=True)    # 輸出如下:
               a             b             c                d
0   1.721685 -0.160918 -2.419580   -1.634277
1  -1.039395 -0.595996  0.788547    0.455259
2   0.342471 -0.118662  1.402167    0.280451
3   0.986049  0.236757     &nbs