Merge, Join, Concat大家好,我有回來啦,這周更新的有點(diǎn)慢,主要是因?yàn)槲腋铝藗€人簡歷哈哈,如果感興趣的朋友可以去看看哈: 個人認(rèn)為還是很漂亮的~,不得不說,很多時候老外的設(shè)計(jì)能力還是很強(qiáng)。 好了,有點(diǎn)扯遠(yuǎn)了,這一期我想和大家分享的是pandas中最常見的幾種方法,這些方法如果你學(xué)會了,某種程度上可以很好的替代Excel,這篇文章是pandas之旅的第三篇,主要會從以下幾個方面和大家分享我的心得體會: Merge Join Concat 源碼及GitHub地址
話不多說,讓我們開始今天的Pandas之旅吧! 1. Merge首先merge的操作非常類似sql里面的join,實(shí)現(xiàn)將兩個Dataframe根據(jù)一些共有的列連接起來,當(dāng)然,在實(shí)際場景中,這些共有列一般是Id, 連接方式也豐富多樣,可以選擇inner(默認(rèn)),left,right,outer 這幾種模式,分別對應(yīng)的是內(nèi)連接,左連接,右連接 1.1 InnerMerge (內(nèi)連接)首先讓我們簡單的創(chuàng)建兩個DF,分別為DataFrame1,DataFrame2,他們的公有列是key import numpy as np
import pandas as pd
from pandas import Series, DataFrame
# Let's make a dframe
dframe1 = DataFrame({'key':['X','Z','Y','Z','X','X'],'value_df1': np.arange(6)})
dframe1
| key | value_df1 |
---|
0 | X | 0 |
---|
1 | Z | 1 |
---|
2 | Y | 2 |
---|
3 | Z | 3 |
---|
4 | X | 4 |
---|
5 | X | 5 |
---|
#Now lets make another dframe
dframe2 = DataFrame({'key':['Q','Y','Z'],'value_df2':[1,2,3]})
dframe2
我們現(xiàn)在可以簡單地使用pd.merge(dframe1,dframe2)來實(shí)現(xiàn)Merge功能 pd.merge(dframe1,dframe2)
| key | value_df1 | value_df2 |
---|
0 | Z | 1 | 3 |
---|
1 | Z | 3 | 3 |
---|
2 | Y | 2 | 2 |
---|
我們現(xiàn)在需要注意一點(diǎn),X僅僅是存在于dframe1的key,在dframe2中不存在,因此大家可以發(fā)現(xiàn),當(dāng)我們調(diào)用pd.merge的時候,會自動默認(rèn)為inner join, 我們再換一種方式寫一下,大家就明白了: pd.merge(dframe1,dframe2,on='key',how='inner')
| key | value_df1 | value_df2 |
---|
0 | Z | 1 | 3 |
---|
1 | Z | 3 | 3 |
---|
2 | Y | 2 | 2 |
---|
大家可以發(fā)現(xiàn)結(jié)果是一樣的,看到這里,對sql熟悉的朋友們已經(jīng)有感覺了估計(jì),因?yàn)閷?shí)在是太像了,如果我們不通過on和how來指定
想要merge的公有列或者方式,那么pd.merge就會自動尋找到兩個DataFrame的相同列并自動默認(rèn)為inner join,至此,
估計(jì)大家也可以猜出其他幾種模式的merge啦
1.2 LeftMerge (左連接)現(xiàn)在同樣的,讓我們看一下how='left'的情況,這是一個左連接
pd.merge(dframe1,dframe2,on='key',how='left')
| key | value_df1 | value_df2 |
---|
0 | X | 0 | NaN |
---|
1 | Z | 1 | 3.0 |
---|
2 | Y | 2 | 2.0 |
---|
3 | Z | 3 | 3.0 |
---|
4 | X | 4 | NaN |
---|
5 | X | 5 | NaN |
---|
我們可以看到返回的是dframe1的所有key值對應(yīng)的結(jié)果,如果在dframe2中不存在,顯示為Nan空值 1.3 RightMerge (右連接)右連接的原理和左連接正相反 pd.merge(dframe1,dframe2,on='key',how='right')
| key | value_df1 | value_df2 |
---|
0 | Z | 1.0 | 3 |
---|
1 | Z | 3.0 | 3 |
---|
2 | Y | 2.0 | 2 |
---|
3 | Q | NaN | 1 |
---|
這里Q只存在于drame2的key中 1.4 OuterMerge (全連接)#Choosing the "outer" method selects the union of both keys
pd.merge(dframe1,dframe2,on='key',how='outer')
| key | value_df1 | value_df2 |
---|
0 | X | 0.0 | NaN |
---|
1 | X | 4.0 | NaN |
---|
2 | X | 5.0 | NaN |
---|
3 | Z | 1.0 | 3.0 |
---|
4 | Z | 3.0 | 3.0 |
---|
5 | Y | 2.0 | 2.0 |
---|
6 | Q | NaN | 1.0 |
---|
這里就是一個并集的形式啦,其實(shí)就是一個union的結(jié)果,會把key這一列在兩個Dataframe出現(xiàn)的所有值全部顯示出來,如果有空值顯示為Nan
1.5 MultipleKey Merge (基于多個key上的merge)剛才我們都是僅僅實(shí)現(xiàn)的在一個key上的merge,當(dāng)然我們也可以實(shí)現(xiàn)基于多個keys的merge # Dframe on left
df_left = DataFrame({'key1': ['SF', 'SF', 'LA'],
'key2': ['one', 'two', 'one'],
'left_data': [10,20,30]})
df_left
| key1 | key2 | left_data |
---|
0 | SF | one | 10 |
---|
1 | SF | two | 20 |
---|
2 | LA | one | 30 |
---|
#Dframe on right
df_right = DataFrame({'key1': ['SF', 'SF', 'LA', 'LA'],
'key2': ['one', 'one', 'one', 'two'],
'right_data': [40,50,60,70]})
df_right
| key1 | key2 | right_data |
---|
0 | SF | one | 40 |
---|
1 | SF | one | 50 |
---|
2 | LA | one | 60 |
---|
3 | LA | two | 70 |
---|
這是內(nèi)連接(交集)的結(jié)果
#Merge, Inner
pd.merge(df_left, df_right, on=['key1', 'key2'])
| key1 | key2 | left_data | right_data |
---|
0 | SF | one | 10 | 40 |
---|
1 | SF | one | 10 | 50 |
---|
2 | LA | one | 30 | 60 |
---|
這是外連接(并集)的結(jié)果
#Merge, Outer
pd.merge(df_left, df_right, on=['key1', 'key2'],how='outer')
| key1 | key2 | left_data | right_data |
---|
0 | SF | one | 10.0 | 40.0 |
---|
1 | SF | one | 10.0 | 50.0 |
---|
2 | SF | two | 20.0 | NaN |
---|
3 | LA | one | 30.0 | 60.0 |
---|
4 | LA | two | NaN | 70.0 |
---|
這里還有一個地方非常有意思,大家可以發(fā)現(xiàn)現(xiàn)在df_left,df_right作為key的兩列分別是key1和key2,它們的名字是相同的,剛剛我們是通過制定on=['key1', 'key2'],那如果我們只指定一列會怎么樣呢? pd.merge(df_left,df_right,on='key1')
| key1 | key2_x | left_data | key2_y | right_data |
---|
0 | SF | one | 10 | one | 40 |
---|
1 | SF | one | 10 | one | 50 |
---|
2 | SF | two | 20 | one | 40 |
---|
3 | SF | two | 20 | one | 50 |
---|
4 | LA | one | 30 | one | 60 |
---|
5 | LA | one | 30 | two | 70 |
---|
大家可以看到pandas自動把key2這一列拆分成了key2_x和key2_y,都會顯示在最后的merge結(jié)果里,如果我們想要給這兩列重新命名,也是很容易的: # We can also specify what the suffix becomes
pd.merge(df_left,df_right, on='key1',suffixes=('_lefty','_righty'))
| key1 | key2_lefty | left_data | key2_righty | right_data |
---|
0 | SF | one | 10 | one | 40 |
---|
1 | SF | one | 10 | one | 50 |
---|
2 | SF | two | 20 | one | 40 |
---|
3 | SF | two | 20 | one | 50 |
---|
4 | LA | one | 30 | one | 60 |
---|
5 | LA | one | 30 | two | 70 |
---|
像這樣,我們可以通過suffixes參數(shù)來指定拆分的列的名字。 1.6 Merge on Index (基于index上的merge)我們還可以實(shí)現(xiàn)幾個Dataframe基于Index的merge,還是老樣子,先讓我們創(chuàng)建兩個Dataframe
df_left = DataFrame({'key': ['X','Y','Z','X','Y'],
'data': range(5)})
df_right = DataFrame({'group_data': [10, 20]}, index=['X', 'Y'])
df_left
df_right
好了,現(xiàn)在我們想要實(shí)現(xiàn)兩個Dataframe的merge,但是條件是通過df_left的Key和df_right的Index pd.merge(df_left,df_right,left_on='key',right_index=True)
| key | data | group_data |
---|
0 | X | 0 | 10 |
---|
3 | X | 3 | 10 |
---|
1 | Y | 1 | 20 |
---|
4 | Y | 4 | 20 |
---|
這樣我們也可以得到結(jié)果。 # We can also get a union by using outer
pd.merge(df_left,df_right,left_on='key',right_index=True,how='outer')
| key | data | group_data |
---|
0 | X | 0 | 10.0 |
---|
3 | X | 3 | 10.0 |
---|
1 | Y | 1 | 20.0 |
---|
4 | Y | 4 | 20.0 |
---|
2 | Z | 2 | NaN |
---|
其他的merge方式就類似啦,這里就不一一說了,只是舉一個outer join的例子 # 通過outer實(shí)現(xiàn)外連接,union并集
pd.merge(df_left,df_right,left_on='key',right_index=True,how='outer')
| key | data | group_data |
---|
0 | X | 0 | 10.0 |
---|
3 | X | 3 | 10.0 |
---|
1 | Y | 1 | 20.0 |
---|
4 | Y | 4 | 20.0 |
---|
2 | Z | 2 | NaN |
---|
我們也可以嘗試一些有意思的merge,比如,如果一個dataframe的index是多層嵌套的情況:
df_left_hr = DataFrame({'key1': ['SF','SF','SF','LA','LA'],
'key2': [10, 20, 30, 20, 30],
'data_set': np.arange(5.)})
df_right_hr = DataFrame(np.arange(10).reshape((5, 2)),
index=[['LA','LA','SF','SF','SF'],
[20, 10, 10, 10, 20]],
columns=['col_1', 'col_2'])
df_left_hr
| key1 | key2 | data_set |
---|
0 | SF | 10 | 0.0 |
---|
1 | SF | 20 | 1.0 |
---|
2 | SF | 30 | 2.0 |
---|
3 | LA | 20 | 3.0 |
---|
4 | LA | 30 | 4.0 |
---|
df_right_hr
|
| col_1 | col_2 |
---|
LA | 20 | 0 | 1 |
---|
10 | 2 | 3 |
---|
SF | 10 | 4 | 5 |
---|
10 | 6 | 7 |
---|
20 | 8 | 9 |
---|
現(xiàn)在我們穿建了兩個Dataframe 分別是df_left_hr和df_right_hr(Index兩層),如果我們想通過使用df_left_hr的key1,key2 及df_right_hr的Index作為merge 的列,也是沒有問題的 # Now we can merge the left by using keys and the right by its index
pd.merge(df_left_hr,df_right_hr,left_on=['key1','key2'],right_index=True)
| key1 | key2 | data_set | col_1 | col_2 |
---|
0 | SF | 10 | 0.0 | 4 | 5 |
---|
0 | SF | 10 | 0.0 | 6 | 7 |
---|
1 | SF | 20 | 1.0 | 8 | 9 |
---|
3 | LA | 20 | 3.0 | 0 | 1 |
---|
基本到這里,我已經(jīng)和大家分享了基礎(chǔ)的Merge有關(guān)的所有操作,如果你平時生活工作中經(jīng)常使用Excel執(zhí)行類似操作的話,可以學(xué)習(xí)一下Merge哈,它會大幅度 減輕你的工作強(qiáng)度的! 2.Join現(xiàn)在我們可以接著來看join相關(guān)的操作,先讓我們看一個小例子 left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']},
index = ['K0', 'K1', 'K2', 'K3'])
right = pd.DataFrame({'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']},
index = ['K0', 'K1', 'K2', 'K3'])
left
| A | B |
---|
K0 | A0 | B0 |
---|
K1 | A1 | B1 |
---|
K2 | A2 | B2 |
---|
K3 | A3 | B3 |
---|
right
| C | D |
---|
K0 | C0 | D0 |
---|
K1 | C1 | D1 |
---|
K2 | C2 | D2 |
---|
K3 | C3 | D3 |
---|
left.join(right)
| A | B | C | D |
---|
K0 | A0 | B0 | C0 | D0 |
---|
K1 | A1 | B1 | C1 | D1 |
---|
K2 | A2 | B2 | C2 | D2 |
---|
K3 | A3 | B3 | C3 | D3 |
---|
其實(shí)通過這一個小例子大家也就明白了,join無非就是合并,默認(rèn)是橫向,還有一個點(diǎn)需要注意的是,我們其實(shí)可以通過join實(shí)現(xiàn)和merge一樣的效果,但是為了 避免混淆,我不會多舉其他的例子了,因?yàn)槲覀€人認(rèn)為一般情況下還是用merge函數(shù)好一些 3. Concat為了更加全面徹底地了解Concat函數(shù),大家可以先從一維的Numpy Array開始,首先讓我們簡單的創(chuàng)建一個矩陣: # Create a matrix
arr1 = np.arange(9).reshape((3,3))
arr1
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
接著讓我們通過concatenate函數(shù)進(jìn)行橫向拼接: np.concatenate([arr1,arr1],axis=1)
array([[0, 1, 2, 0, 1, 2],
[3, 4, 5, 3, 4, 5],
[6, 7, 8, 6, 7, 8]])
再讓我們進(jìn)行縱向拼接: # Let's see other axis options
np.concatenate([arr1,arr1],axis=0)
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
有了基礎(chǔ)的印象之后,現(xiàn)在讓我們看看在pandas中是如何操作的: # Lets create two Series with no overlap
ser1 = Series([0,1,2],index=['T','U','V'])
ser2 = Series([3,4],index=['X','Y'])
#Now let use concat (default is axis=0)
pd.concat([ser1,ser2])
T 0
U 1
V 2
X 3
Y 4
dtype: int64
在上面的例子中,我們分別創(chuàng)建了兩個沒有重復(fù)Index的Series,然后用concat默認(rèn)的把它們合并在一起,這時生成的依然是Series類型,如果我們把a(bǔ)xis換成1,那生成的就是Dataframe,像下面一樣 pd.concat([ser1,ser2],axis=1,sort =True) # sort=Ture是默認(rèn)的,pandas總是默認(rèn)index排序
| 0 | 1 |
---|
T | 0.0 | NaN |
---|
U | 1.0 | NaN |
---|
V | 2.0 | NaN |
---|
X | NaN | 3.0 |
---|
Y | NaN | 4.0 |
---|
我們還可以指定在哪些index上進(jìn)行concat: pd.concat([ser1,ser2],axis=1,join_axes=[['U','V','Y']])
也可以給不同組的index加一層標(biāo)簽 pd.concat([ser1,ser2],keys=['cat1','cat2'])
cat1 T 0
U 1
V 2
cat2 X 3
Y 4
dtype: int64
如果把a(bǔ)xis換成是1,那么keys就會變成column的名字: pd.concat([ser1,ser2],axis=1,keys=['cat1','cat2'],sort=True)
| cat1 | cat2 |
---|
T | 0.0 | NaN |
---|
U | 1.0 | NaN |
---|
V | 2.0 | NaN |
---|
X | NaN | 3.0 |
---|
Y | NaN | 4.0 |
---|
如果是兩個現(xiàn)成的dataframe直接進(jìn)行concat也是一樣: dframe1 = DataFrame(np.random.randn(4,3), columns=['X', 'Y', 'Z'])
dframe2 = DataFrame(np.random.randn(3, 3), columns=['Y', 'Q', 'X'])
dframe1
| X | Y | Z |
---|
0 | 1.119976 | -0.853960 | 0.027451 |
---|
1 | -0.536831 | 0.982092 | -0.157650 |
---|
2 | -0.219322 | -1.489809 | 1.607735 |
---|
3 | 0.767249 | -1.661912 | 0.038837 |
---|
dframe2
| Y | Q | X |
---|
0 | -0.035560 | 0.875282 | -1.630508 |
---|
1 | -0.439484 | 0.096247 | 1.335693 |
---|
2 | 0.746299 | 0.568684 | 1.197015 |
---|
#如果沒有對應(yīng)的值,默認(rèn)為NaN, 空值
pd.concat([dframe1,dframe2],sort=True)
| Q | X | Y | Z |
---|
0 | NaN | 1.119976 | -0.853960 | 0.027451 |
---|
1 | NaN | -0.536831 | 0.982092 | -0.157650 |
---|
2 | NaN | -0.219322 | -1.489809 | 1.607735 |
---|
3 | NaN | 0.767249 | -1.661912 | 0.038837 |
---|
0 | 0.875282 | -1.630508 | -0.035560 | NaN |
---|
1 | 0.096247 | 1.335693 | -0.439484 | NaN |
---|
2 | 0.568684 | 1.197015 | 0.746299 | NaN |
---|
4. 源碼及Github地址今天我為大家主要總結(jié)了pandas中非常常見的三種方法: 大家可以根據(jù)自己的實(shí)際需要來決定使用哪一種 我把這一期的ipynb文件和py文件放到了Github上,大家如果想要下載可以點(diǎn)擊下面的鏈接: 這一期就到這里啦,希望大家能夠繼續(xù)支持我,完結(jié),撒花
|