Python之數據規整:連接、聯合和重塑

本博客爲《利用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_indexset_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 innerouterleftright之一;默認是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_onlyright_onlyboth

2.2 根據索引合併

在某些情況下,DataFrame中用於合併的鍵是它的索引。在這種情況下,你可以傳遞left_index=Trueright_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
在這裏插入圖片描述

表:concat函數的參數
參數 描述
objs 需要連接的pandas對象列表或字典;這是必選參數
axis 連接的軸向;默認是0(沿着行方向)
join 可以是innerouter(默認是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的使用也可以無須任何分組指標:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章