數據分析之Pandas合併操做總結

 
 

pandas 是一個強大的分析結構化數據的工具集,它的使用基礎是Numpy(提供高性能的矩陣運算),用於數據挖掘和數據分析,同時也提供數據清洗功能。html

Pandas作分析數據,能夠分爲索引、分組、變形及合併四種操做。前邊已經介紹過索引操做分組操做變形操做,最後對Pandas中的合併操做進行介紹,涉及知識點提綱以下圖:api

圖片

     本文目錄
app

              1. append與assignide

                  1.1. append方法函數

                  1.2. assign方法工具

2. combine與update性能

    2.1. combine方法學習

    2.2. update方法網站

              3. concat方法 spa

               4. merge與join

    4.1. merge函數

    4.2. join函數

在詳細講解每一個模塊以前,首先讀入數據:





import numpy as npimport pandas as pddf = pd.read_csv('joyful-pandas-master/data/table.csv')df.head()

append與assign

1. append方法(通常用來添加行)

(1)利用序列添加行(必須指定name)



df_append = df.loc[:3,['Gender','Height']].copy()  # 默認深拷貝df_append



s = pd.Series({'Gender':'F','Height':188},name='new_row')  # name定義增長的這一行索引名。df_append.append(s)

(2)用DataFrame添加表



df_temp = pd.DataFrame({'Gender':['F','M'],'Height':[188,176]})df_append.append(df_temp)

從上面能夠看到這個索引是不會自動往下續的,由於咱們新建的df_temp以下:


pd.DataFrame({'Gender':['F','M'],'Height':[188,176]})

能夠看到這個索引就是0和1,若是你直接append而不加參數則就會直接將上面的DataFrame直接和df_append粘在一塊兒而不會改變索引,那麼怎麼改變索引使得這個索引順着前面的索引呢?看下面的例子:


df_append.append(df_temp, ignore_index=True)


下面是這個append函數的原形式:

DataFrame.append(self,other,ignore_index=False,verify_integrity=False,sort=False)

其中的ignore_index就是表示是否要跟着前面的索引來定義後面的索引,通常來講是默認False,也就是像咱們的第一個例子這樣。如今咱們將這個參數改爲True,就能夠順着索引了,就像上面的這個例子同樣。

固然這裏也能夠自行改變索引名:



df_temp = pd.DataFrame({'Gender':['F','M'],'Height':[188,176]},index=['new_1','new_2'])df_append.append(df_temp)

其餘的參數學習能夠參考這個網站:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.append.html?highlight=append#pandas.DataFrame.append

2. assign方法(通常用來添加列)

該方法主要用於添加列,列名直接由參數指定:



s = pd.Series(list('abcd'),index=range(4))df_append.assign(Letter=s)  # 這裏定義列名就直接在assign參數定義。

這個通常定義要添加的列Series是沒有列索引名的:



s = pd.Series(list('abcd'),index=range(4))s

能夠一次添加多個列:


df_append.assign(col1=lambda x:x['Gender']*2, col2=s)

能夠看出這個能夠添加任意多個列,可是都是要在參數中依次定義的。

參考學習:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.assign.html?highlight=assign#pandas.DataFrame.assign

combine與update

1. combine方法

combine和update都是用於表的填充函數,能夠根據某種規則填充。

(1)填充對象

能夠看出combine方法是按照表的順序輪流進行逐列循環的,並且自動索引對齊,缺失值爲NaN,理解這一點很重要。




df_combine_1 = df.loc[:1,['Gender','Height']].copy()df_combine_2 = df.loc[10:11,['Gender','Height']].copy()df_combine_1.combine(df_combine_2,lambda x,y:print(x))

由於lambda函數是輸出x和y,沒有返回值因此都爲NaN。




df1 = pd.DataFrame({'A': [5, 0], 'B': [2, 4]})df2 = pd.DataFrame({'A': [1, 1], 'B': [3, 3]})df1.combine(df2, np.minimum)

圖片

combine函數原型:

DataFrame.combine(self,other:'DataFrame',func,fill_value = None,overwrite = True)

這裏經過多個例子嘗試能夠發現,func函數是必不可少的,也就是咱們必須有一個func來返回數值。

(2)一些例子

例①:根據列均值的大小填充




df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})df2 = pd.DataFrame({'A': [8, 7], 'B': [6, 5]})df1.combine(df2,lambda x,y:x if x.mean()>y.mean() else y)

圖片


例②:索引對齊特性(默認狀態下,後面的表沒有的行列都會設置爲NaN)



df2 = pd.DataFrame({'B': [8, 7], 'C': [6, 5]},index=[1,2])df1.combine(df2,lambda x,y:x if x.mean()>y.mean() else y)

圖片


例③:使得df1原來符合條件的值不會被覆蓋


df1.combine(df2,lambda x,y:x if x.mean()>y.mean() else y,overwrite=False)


例④:在新增匹配df2的元素位置填充-1



df1.combine(df2,lambda x,y:x if x.mean()>y.mean() else y,fill_value=-1)# 也就是將NaN位置補成-1

參考學習:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.combine.html?highlight=combine#pandas.DataFrame.combine

(3)combine_first方法

這個方法做用是用df2填補df1的缺失值,功能比較簡單,但不少時候會比combine更經常使用,下面舉兩個例子:




df1 = pd.DataFrame({'A': [None, 0], 'B': [None, 4]})df2 = pd.DataFrame({'A': [1, 1], 'B': [3, 3]})df1.combine_first(df2)

也就是要在df1的基礎之上,若是df1有缺失值,就在df2的對應位置補上去,固然若是df1沒有缺失值,則這個填充也就至關於沒填充,也就意義不大了。




df1 = pd.DataFrame({'A': [None, 0], 'B': [4, None]})df2 = pd.DataFrame({'B': [3, 3], 'C': [1, 1]}, index=[1, 2])df1.combine_first(df2)

固然,若是df1的缺失值位置在df2中也是NaN,那也是不會填充的。

這裏也涉及到不少參數問題,能夠參考這個:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.combine_first.html#pandas.DataFrame.combine_first

2. update方法

(1)三個特色

①返回的框索引只會與被調用框的一致(默認使用左鏈接,下一節會介紹)

②第二個框中的nan元素不會起做用

③沒有返回值,直接在df上操做

(2)例子

例①:索引徹底對齊狀況下的操做





df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [400, 500, 600]})df2 = pd.DataFrame({'B': [4, 5, 6], 'C': [7, 8, 9]})df1.update(df2)df1

這裏須要注意:這個也是在df1的基礎之上進行改變,而這個update是連行列索引都不改變,不增長,就是在這個基礎上,對df1中對應位置的元素改爲df2中對應位置的元素。看上面的例子也很好理解了。


例②:部分填充





df1 = pd.DataFrame({'A': ['a', 'b', 'c'], 'B': ['x', 'y', 'z']})df2 = pd.DataFrame({'B': ['d', 'e']}, index=[1,2])df1.update(df2)df1

這個例子就是說明了,咱們這個操做能夠對df1的某幾個元素進行改變,不必定是要整行整列改變。


例③:缺失值不會填充





df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [400, 500, 600]})df2 = pd.DataFrame({'B': [4, np.nan, 6]})df1.update(df2)df1

這個例子就是,咱們若是update了缺失值NaN,則就不會在原df1中把對應元素改爲NaN了,這個缺失值是不會被填充的。

更多參數參考:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.update.html?highlight=update#pandas.DataFrame.update

concat方法

concat方法能夠在兩個維度上拼接,默認縱向憑藉(axis=0),拼接方式默認外鏈接

所謂外鏈接,就是取拼接方向的並集,而'inner'時取拼接方向(若使用默認的縱向拼接,則爲列的交集)的交集

下面舉一些例子說明其參數:




df1 = pd.DataFrame({'A': ['A0', 'A1'], 'B': ['B0', 'B1']}, index = [0,1])df2 = pd.DataFrame({'A': ['A2', 'A3'], 'B': ['B2', 'B3']}, index = [2,3])df3 = pd.DataFrame({'A': ['A1', 'A3'], 'D': ['D1', 'D3'], 'E': ['E1', 'E3']},index = [1,3])

df1


df2


df3

默認狀態拼接:


pd.concat([df1,df2])

axis=1時沿列方向拼接:


pd.concat([df1,df2],axis=1)

join設置爲內鏈接(因爲axis=0,所以列取交集):


pd.concat([df3,df1])


pd.concat([df3,df1],join='inner')  # 對索引取交集

join設置爲外連接:


pd.concat([df3,df1],join='outer')


pd.concat([df3,df1],join='outer',sort=True) #sort設置列排序,默認爲False

其實就是對列索引進行排序。verify_integrity檢查列是否惟一:



pd.concat([df2,df1],verify_integrity=True,sort=True)# pd.concat([df3,df1],verify_integrity=True,sort=True) 報錯

這裏由於df1和df2的列索引相同,因此能夠正常返回。而df1和df3的列索引不一樣,因此會報錯。

這個verify_integrity就是爲了保證只有在索引相同時纔會進行操做的函數,而能夠拿來檢查函數列是否惟一。一樣,能夠添加Series:



s = pd.Series(['X0', 'X1'], name='X')pd.concat([df1,s],axis=1)

key參數用於對不一樣的數據框增長一個標號,便於索引:


pd.concat([df1,df2], keys=['x', 'y'])


pd.concat([df1,df2], keys=['x', 'y']).index

這裏至關於對原索引的基礎上,又設定了行索引,針對這個df1和df2。然這裏也能夠解決行索引雜亂無章的問題,和append同樣,都是經過ignore_index參數來完成:


pd.concat([df3,df1], ignore_index=True)

更多參數參考:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.concat.html?highlight=concat#pandas.concat

merge與join

1. merge函數

merge函數的做用是將兩個pandas對象橫向合併,遇到重複的索引項時會使用笛卡爾積,默認inner鏈接,可選left、outer、right鏈接。所謂左鏈接,就是指以第一個表索引爲基準,右邊的表中若是再也不左邊的則不加入,若是在左邊的就以笛卡爾積的方式加入。

merge/join與concat的不一樣之處在於on參數,能夠指定某一個對象爲key來進行鏈接

一樣的,下面舉一些例子:







left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'], 'key2': ['K0', 'K1', 'K0', 'K1'],                      'A': ['A0', 'A1', 'A2', 'A3'], 'B': ['B0', 'B1', 'B2', 'B3']})right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],'key2': ['K0', 'K0', 'K0', 'K0'],                      'C': ['C0', 'C1', 'C2', 'C3'],'D': ['D0', 'D1', 'D2', 'D3']})right2 = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],'key2': ['K0', 'K0', 'K0', 'K0'],                      'C': ['C0', 'C1', 'C2', 'C3']})

left


right

圖片


right2

圖片

以key1爲準則鏈接,若是具備相同的列,則默認suffixes=('_x','_y'):


pd.merge(left, right, on='key1')

圖片

這個函數相對有點複雜,能夠多看例子來想一想。以多組鍵鏈接:


pd.merge(left, right, on=['key1','key2'])

圖片

默認使用inner鏈接,由於merge只能橫向拼接,因此取行向上keys的交集,下面看若是使用how=outer參數。注意:這裏的how就是concat的join


pd.merge(left, right, how='outer', on=['key1','key2'])

使用了how='outer',那麼若是行中帶有缺失值也會被返回。

左鏈接:


pd.merge(left, right, how='left', on=['key1', 'key2'])

以左邊的left表的索引爲基準。

右鏈接:


pd.merge(left, right, how='right', on=['key1', 'key2'])

以右邊的right表索引爲基準。

若是仍是對笛卡爾積不太瞭解,請務必理解下面這個例子,因爲B的全部元素爲2,所以須要6行:





left = pd.DataFrame({'A': [1, 2], 'B': [2, 2]})right = pd.DataFrame({'A': [4, 5, 6], 'B': [2, 2, 2]})print(left)print(right)


pd.merge(left, right, on='B', how='outer')

validate檢驗的是到底哪一邊出現了重複索引,若是是「one_to_one」則兩側索引都是惟一,若是"one_to_many"則左側惟一





left = pd.DataFrame({'A': [1, 2], 'B': [2, 2]})right = pd.DataFrame({'A': [4, 5, 6], 'B': [2, 3, 4]})print(left)print(right)



# pd.merge(left, right, on='B', how='outer',validate='one_to_one')  # 報錯# MergeError: Merge keys are not unique in left dataset; not a one-to-one merge

這裏由於left中索引不惟一,因此報錯了。因此咱們改一下left,使得它索引惟一。以下例:



left = pd.DataFrame({'A': [1, 2], 'B': [2, 1]})pd.merge(left, right, on='B', how='outer',validate='one_to_one')

indicator參數指示了,合併後該行索引的來源




df1 = pd.DataFrame({'col1': [0, 1], 'col_left': ['a', 'b']})df2 = pd.DataFrame({'col1': [1, 2, 2], 'col_right': [2, 2, 2]})pd.merge(df1, df2, on='col1', how='outer', indicator=True)  # indicator='indicator_column'也是能夠的

這裏就是新增一列代表每行索引的來源。

更多參數:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.merge.html?highlight=merge#pandas.DataFrame.merge

2. join函數

join函數做用是將多個pandas對象橫向拼接,遇到重複的索引項時會使用笛卡爾積,默認左鏈接,可選inner、outer、right鏈接。




left = pd.DataFrame({'A': ['A0', 'A1', 'A2'], 'B': ['B0', 'B1', 'B2']}, index=['K0', 'K1', 'K2'])right = pd.DataFrame({'C': ['C0', 'C2', 'C3'], 'D': ['D0', 'D2', 'D3']}, index=['K0', 'K2', 'K3'])left.join(right)

這裏是默認左鏈接,也就是按照left索引的基礎上來填充。對於many_to_one模式下的合併,每每join更爲方便。一樣能夠指定key:





left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'], 'B': ['B0', 'B1', 'B2', 'B3'], 'key': ['K0', 'K1', 'K0', 'K1']})right = pd.DataFrame({'C': ['C0', 'C1'], 'D': ['D0', 'D1']}, index=['K0', 'K1'])print(left)print(right)


left.join(right, on='key')

多層key:













left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],                     'B': ['B0', 'B1', 'B2', 'B3'],                     'key1': ['K0', 'K0', 'K1', 'K2'],                     'key2': ['K0', 'K1', 'K0', 'K1']})index = pd.MultiIndex.from_tuples([('K0', 'K0'), ('K1', 'K0'),                                   ('K2', 'K0'), ('K2', 'K1')],names=['key1','key2'])right = pd.DataFrame({'C': ['C0', 'C1', 'C2', 'C3'],                      'D': ['D0', 'D1', 'D2', 'D3']},                     index=index)print(left)print(index)print(right)


left.join(right, on=['key1','key2'])

參考:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.join.html?highlight=join#pandas.DataFrame.join

問題與練習

1. 問題

【問題一】請思考什麼是append/assign/combine/update/concat/merge/join各自最適合使用的場景,並舉出相應的例子。

  • append:主要是用來添加行,也就是在一個表中下方添加。

  • assign:主要是用來添加列,也就是在表的右方添加。

  • combine:這個函數的填充能夠根據某種規則來填充,固然它衍生的combine_first就是一個比較經常使用的函數了,這個函數是直接填充。

  • update:這個函數是會在前表的基礎之上,將後表填充,不會更改索引,也就是按照前表的索引來操做。

  • concat:這個函數也是進行直接的拼接,不會管索引,因此會出現多個相同的索引的狀況,主要用於列的拼接。

  • merge:這個函數就是用於行拼接多一些,能夠指定key來拼接,多用於one_to_one和one_to_many的狀況。

  • join:這個函數也適用於行拼接,多用於many_to_one的狀況,還能夠應對多層keys的拼接。

例子的話能夠看上面的講解,也是比較詳細的。

【問題二】merge_ordered和merge_asof的做用是什麼?和merge是什麼關係?

做用能夠參考:

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.merge_ordered.html?highlight=merge_ordered#pandas.merge_ordered https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.merge_asof.html#pandas.merge_asof應該是merge的衍生出來的函數,能夠完善merge函數的一切缺陷。

【問題三】請構造一個多級索引與多級索引合併的例子,嘗試使用不一樣的合併函數。

下面創建兩個多級索引。



df1=pd.DataFrame(np.arange(12).reshape(4,3),index=[list("AABB"),[1,2,1,2]],columns=[list("XXY"),[10,11,10]])df1



df2=pd.DataFrame(np.arange(9).reshape(3,3),index=[list("CCD"),[3,3,4]],columns=[list("ZZK"),[9,7,9]])df2

下面是幾個合併的例子。


df1.append(df2)


df1.combine_first(df2)



df2.update(df1)df2


pd.concat([df1,df2])
【問題四】 上文提到了鏈接的笛卡爾積,那麼當鏈接方式變化時(inner/outer/left/right),這種笛卡爾積規則會相應變化嗎?請構造相應例子。

答:就是咱們用merge的時候,他會自動計算笛卡爾積,可是最後返回的是否是所有的笛卡爾積,就要看這些鏈接方式了,有時候是左鏈接,那就會根據左表的索引來返回,有時候右鏈接,就會根據右表索引來返回,有時候也會所有返回,這些就要看參數了。

2. 練習

【練習一】有2張公司的員工信息表,每一個公司共有16名員工,共有五個公司,請解決以下問題:


pd.read_csv('joyful-pandas-master/data/Employee1.csv').head()

圖片


pd.read_csv('joyful-pandas-master/data/Employee2.csv').head()

圖片

(a) 每一個公司有多少員工知足以下條件:既出現第一張表,又出如今第二張表。



df1=pd.read_csv('joyful-pandas-master/data/Employee1.csv')df2=pd.read_csv('joyful-pandas-master/data/Employee2.csv')

df1.head()

圖片


df2.head()

圖片


pd.merge(df1['Name'],df2['Name'])

(b) 將全部不符合(a)中條件的行篩選出來,合併爲一張新表,列名與原表一致。






L = list(set(df1['Name']).intersection(set(df2['Name'])))df_b1 = df1[~df1['Name'].isin(L)]df_b2 = df2[~df2['Name'].isin(L)]df_b = pd.concat([df_b1,df_b2]).set_index('Name')df_b.head()

(c) 如今須要編制全部80位員工的信息表,對於(b)中的員工要求不變,對於知足(a)條件員工,它們在某個指標的數值,取偏離它所屬公司中知足(b)員工的均值數較小的哪個,例如:P公司在兩張表的交集爲{p1},並集扣除交集爲{p2,p3,p4},那麼若是後者集合的工資均值爲1萬元,且p1在表1的工資爲13000元,在表2的工資爲9000元,那麼應該最後取9000元做爲p1的工資,最後對於沒有信息的員工,利用缺失值填充。

























df1['重複'] = ['Y_1' if df1.loc[i,'Name'] in L else 'N' for i in range(df1.shape[0])]df2['重複'] = ['Y_2' if df2.loc[i,'Name'] in L else 'N' for i in range(df2.shape[0])]df1 = df1.set_index(['Name','重複'])df2 = df2.set_index(['Name','重複'])df_c = pd.concat([df1,df2])result = pd.DataFrame({'Company':[],'Name':[],'Age':[],'Height':[],'Weight':[],'Salary':[]})group = df_c.groupby(['Company','重複'])for i in L:    first = group.get_group((i[0].upper(),'Y_1')).reset_index(level=1).loc[i,:][-4:]    second = group.get_group((i[0].upper(),'Y_2')).reset_index(level=1).loc[i,:][-4:]    mean = group.get_group((i[0].upper(),'N')).reset_index(level=1).mean()    final = [i[0].upper(),i]    for j in range(4):        final.append(first[j] if abs(first[j]-mean[j])<abs(second[j]-mean[j]) else second[j])    result = pd.concat([result,pd.DataFrame({result.columns.tolist()[k]:[final[k]] for k in range(6)})])result = pd.concat([result.set_index('Name'),df_b])for i in list('abcde'):    for j in range(1,17):        item = i+str(j)        if item not in result.index:            result = pd.concat([result,pd.DataFrame({'Company':[i.upper()],'Name':[item]                 ,'Age':[np.nan],'Height':[np.nan],'Weight':[np.nan],'Salary':[np.nan]}).set_index('Name')])result['Number'] = [int(i[1:]) for i in result.index]result.reset_index().drop(columns='Name').set_index(['Company','Number']).sort_index()

【練習二】有2張課程的分數表(分數隨機生成),但專業課(學科基礎課、專業必修課、專業選修課)與其餘課程混在一塊兒,請解決以下問題:


pd.read_csv('joyful-pandas-master/data/Course1.csv').head()

圖片


pd.read_csv('joyful-pandas-master/data/Course2.csv').head()

圖片

(a) 將兩張表分別拆分爲專業課與非專業課(結果爲四張表)。



df1=pd.read_csv('joyful-pandas-master/data/Course1.csv')df1.head()

圖片



df2=pd.read_csv('joyful-pandas-master/data/Course2.csv')df2.head()

圖片





df_a11= df1.query('課程類別 in ["學科基礎課","專業必修課","專業選修課"]')df_a12= df1.query('課程類別 not in ["學科基礎課","專業必修課","專業選修課"]')df_a21= df2.query('課程類別 in ["學科基礎課","專業必修課","專業選修課"]')df_a22= df2.query('課程類別 not in ["學科基礎課","專業必修課","專業選修課"]')

df_a11.head()


df_a12.head()


df_a21.head()

圖片


df_a22.head()

圖片

(b) 將兩張專業課的分數表和兩張非專業課的分數表分別合併。


df_a11.append(df_a21, ignore_index=True)  # 專業課

圖片


df_a12.append(df_a22, ignore_index=True)  # 非專業課

(c) 不使用(a)中的步驟,請直接讀取兩張表合併後拆分。



df = pd.concat([df1,df2])df



special = df.query('課程類別 in ["學科基礎課","專業必修課","專業選修課"]')common = df.query('課程類別 not in ["學科基礎課","專業必修課","專業選修課"]')

special.head()


common.head()

圖片

(d) 專業課程中有缺失值嗎,若是有的話請在完成(3)的同時,用組內(3種類型的專業課)均值填充缺失值後拆分。


df.isnull().any()

圖片

說明「分數」列是存在缺失值的,因此咱們須要將「分數」列的缺失值補上。



df['分數'] = df.groupby('課程類別').transform(lambda x: x.fillna(x.mean()))['分數']df['分數'].isnull().any()
False

出現False說明缺失值已經被填補完成了。下一步就開始拆分:



special2 = df.query('課程類別 in ["學科基礎課","專業必修課","專業選修課"]')common2 = df.query('課程類別 not in ["學科基礎課","專業必修課","專業選修課"]')

special2.head()

圖片


common2.head()

圖片

相關文章
相關標籤/搜索