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()
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
該方法主要用於添加列,列名直接由參數指定:
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
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來返回數值。
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
這個方法做用是用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
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
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函數的做用是將兩個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表索引爲基準。
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
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
append:主要是用來添加行,也就是在一個表中下方添加。
assign:主要是用來添加列,也就是在表的右方添加。
combine:這個函數的填充能夠根據某種規則來填充,固然它衍生的combine_first就是一個比較經常使用的函數了,這個函數是直接填充。
update:這個函數是會在前表的基礎之上,將後表填充,不會更改索引,也就是按照前表的索引來操做。
concat:這個函數也是進行直接的拼接,不會管索引,因此會出現多個相同的索引的狀況,主要用於列的拼接。
merge:這個函數就是用於行拼接多一些,能夠指定key來拼接,多用於one_to_one和one_to_many的狀況。
join:這個函數也適用於行拼接,多用於many_to_one的狀況,還能夠應對多層keys的拼接。
例子的話能夠看上面的講解,也是比較詳細的。
做用能夠參考:
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的時候,他會自動計算笛卡爾積,可是最後返回的是否是所有的笛卡爾積,就要看這些鏈接方式了,有時候是左鏈接,那就會根據左表的索引來返回,有時候右鏈接,就會根據右表索引來返回,有時候也會所有返回,這些就要看參數了。
pd.read_csv('joyful-pandas-master/data/Employee1.csv').head()
pd.read_csv('joyful-pandas-master/data/Employee2.csv').head()
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'])
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()
pd.read_csv('joyful-pandas-master/data/Course1.csv').head()
pd.read_csv('joyful-pandas-master/data/Course2.csv').head()
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()
df_a11.append(df_a21, ignore_index=True) # 專業課
df_a12.append(df_a22, ignore_index=True) # 非專業課
df = pd.concat([df1,df2])df
special = df.query('課程類別 in ["學科基礎課","專業必修課","專業選修課"]')common = df.query('課程類別 not in ["學科基礎課","專業必修課","專業選修課"]')
special.head()
common.head()
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()