1. 程式人生 > 實用技巧 >Pandas資料規整之合併

Pandas資料規整之合併

資料合併

Pandas提供了大量方法,能輕鬆的對Series,DataFrame執行合併操作

  • 按行合併
  1. 追加:append()
  2. 連線:concat() # 行列均可
  • 按列合併
  1. 複雜合併:merge()
  2. 按行索引合併:join()

合併重疊資料(一個表為主,先填充再合併):combine_first()

追加 append()

### series
s = pd.Series([0, 1], index=['a', 'c'])
s1 = pd.Series([2, 3, 4], index=['b', 'd', 'e'])
s.append(s1)

### dataframe
df = pd.DataFrame(np.random.randn(8, 4), columns = ['A','B','C','D'])
s = df.loc[[3, 5]]
# 追加合併
df.append(s)
df.append(s, ignore_index=True)  # 不使用追加表的索引,使用主表的預設索引

###DataFrame和Series追加合併
# Series的行索引和Dataframe的列索引可以匹配上就可以追加一行,否則
series的行索引充當dataframe的列索引
# 必要引數 ignore_index=True
s2 = pd.Series([1,2,3,4], index = ['A','B','C','D'])
df.append(s2, ignore_index=True) 

連線 .concat()

### 多個表按行或列合併
s1 = pd.Series([0, 1], index=['a', 'c'])
s2 = pd.Series([2, 3, 4], index=['b', 'd', 'e'])
s3 = pd.Series([5, 6], index=['f', 'g'])
# 返回series
pd.concat([s1, s2, s3])
# 返回dataframe
pd.concat([s1, s2, s3], axis=1, sort=False)

# 將 s1 和 s3 沿 0軸 連線建立 s4,這樣 s4 和 s1 的 index 是有重複的。
s4 = pd.concat([s1, s3])
# 返回dataframe
pd.concat([s1, s4], axis=1, join='outer', sort=False)  # 預設outer.並集
pd.concat([s1, s4], axis=1, sort=False, join='inner')  # 交集

# 將 n 個 Series 沿 0軸 連線起來,再賦予 n 個 keys 建立多層 Series。
pd.concat([s1, s2, s3], keys=['one', 'two', 'three'])

總結:
series
key來識別資料來源自於哪張表,與ignore_index=True一起用的時候,不在顯示keys
axis=0時候,直接追加加索引和值返回series
axis=1時候,join=outer,series的索引追加到行索引,series的值變為列索引的值,沒有為nan,返回Dataframe
            join=inner,series的索引追加到行索引,series的值變為列索引的值,取交集,返回Dataframe
            
DataFrame
axis=0,追加到行,如果columns不同的時候也追加列
axis=1,追加到列,如果行索引相同在相同行索引追加到不同列;行索引不同,追加行索引和列索引

複雜合併 .merge()和.join()

按列合併

merge()函式用於複雜綜合資料合併,操作複雜,功能強大

join()是merge()的一個特殊用法,用於按索引合併,操作簡單,功能單一

# df1,姓名和分組
df1 = pd.DataFrame({
    'name': ['張三', '李四', '王五', '趙六'],
    'group': ['DBA', 'PM','PM', 'HR']
})
# df2,姓名和入職時間
df2 = pd.DataFrame({
    'name': ['李四', '趙六', '張三', '王五'],
    'date': [2004, 2008, 2012, 2014]
})

兩個表必須有相關性,才有合併的需要
以兩個表相關的列(或行索引)為基準,合併

方法1:使用查詢和新增列進行表合併,比較麻煩,且行列如不完全對應容易出問題
# 查詢新增預設以行索引為基準對齊,所以需要把倆表相關列設為行索引
df11 = df1.set_index('name').copy()
df21 = df2.set_index('name').copy()
df21['group'] = df11['group']
df21.reset_index()

方法2:合併兩個物件,預設匹配相同過的列名,自動對齊合併
要合併的樣本量(行數)不同時,合併後的資料會自動擴充套件,不損失資訊
df3 = pd.merge(df1, df2)


# df4,每個分組的領導,行數少
df4 = pd.DataFrame({
    'group': ['DBA', 'PM', 'HR'],
    'leader': ['錢大', '孫二', '週三']
})
# 樣本量(行數)不同時,合併後的資料會自動擴充套件,不損失資訊
pd.merge(df3, df4)


df5 = pd.DataFrame({
    'group': ['DBA', 'DBA','PM', 'PM', 'HR', 'HR'],
    'skills': ['Linux', '資料庫', 'Axuer RP', '社交','招聘', '組織']
})
pd.merge(df1, df5)


兩個表沒有同名列時,如何合併
兩個物件沒有同名列時,用left_on和right_on強制指定列名對應合併
# df6,姓名2 username 和薪資
df6 = pd.DataFrame({
    'username': ['王五', '張三', '趙六', '李四'],
    'salary': [10000, 160000, 7000, 120000]
})
pd.merge(df1, df6, left_on='name', right_on='username')
pd.merge(df1, df6, left_on='name', right_on='username').drop('username', axis=1)  # 刪除重複列

按照行索引合併

當要合併資料的行索引相關時,指定 merge() 函式的引數 left_index 與 right_index 的值為 True,就可以實現自動依照索引序號合併

join()函式也能實現,寫法更簡單

merge()的優勢在於更靈活,尤其是當資料集索引值差別很大,資料合併又必須以其中一組資料的索引值為依據時

# df1a,將df1的name列設為行索引  df2a,將df2的name列設為行索引
df1a = df1.set_index('name')
df2a = df2.set_index('name')
按索引合併,最簡單的方式 :join()
df1a.join(df2a)
df1a.join(df2a).reset_index()
pd.merge(df1a, df2a, left_index=True, right_index=True)  # merge實現,同上

兩資料索引差異巨大,又必須以一個索引為主合併
# df1a,姓名和分組,姓名為行索引, df6,姓名2和薪資
# 指定一個表的行索引和另一個表的列為基準合併
pd.merge(df1a, df6, left_index=True, right_on='username')


兩個對應列不完全重複的資料集的合併
引數 how
how='inner',交集,兩個表共有的行
how='outer',並集,兩個表所有的行
how='left',表1的行
how='right',表2的行
# df1,姓名和分組 ,df7,姓名和時間,姓名列不完全一致
df7 = pd.DataFrame({'name':['張一', '李二', '趙六'], 'data':[2000,2001,2002]})
pd.merge(df1, df7)
pd.merge(df1, df7, how='inner')  # 預設,交集
pd.merge(df1, df7, how='outer')  # 並集
pd.merge(df1, df7, how='left')  # 以左表為基準
pd.merge(df1, df7, how='right')  # 以右表為基準



合併資料集中包含兩個或以上相同列名時
引數 on 指定用於合併的主鍵
合併後的資料集中,之前相同的列名會被預設加上 _x 等字尾用於區分
引數 suffixes 可以自定義字尾
# df1,姓名和分組,df8,相同的姓名和分組
df8 = pd.DataFrame({
    'name': ['張三', '王五', '趙六', '李四'],
    'group': ['code', 'VP','VP', 'code']
})
pd.merge(df1, df8, on='name')
# 通過設定引數 suffixes 自定義字尾
pd.merge(df1, df8, on='name', suffixes=['_L', '_R'])

  

合併重疊資料(瞭解)

有一類資料組合問題不能用簡單的合併(merge)或連線(concatenation(concat))運算來處理。 如合併全部或部分重疊的兩個資料集

舉例,我們使用NumPy的where函式,它表示一種等價於面向陣列的if-else

以a為基準合併,a的缺失值使用b填充

先給a打補丁(用b填充a的缺失值,再合併)

a = pd.Series([np.nan, 2.5, np.nan, 3.5, 4.5, np.nan], index=['f', 'e', 'd', 'c', 'b', 'a'])
b = pd.Series(np.arange(len(a), dtype=np.float64), index=['f', 'e', 'd', 'c', 'b', 'a'])
b[-1] = np.nan

ax = a.copy()
ax.isnull()
ax[ax.isnull()]  # 查詢a的缺失值
ax[ax.isnull()] = b  # a的缺失值用b填充(操作預設是索引對齊)



Series有一個combine_first方法,實現的也是類似功能,
除了用b填充a的缺失值,還帶有pandas資料對齊的合併功能
# :以a2為基準合併,a2缺失資料使用b2填充
a2 = a[2:].copy()
b2 = b[:-2].copy()
方法1:使用原生方式打補丁(a2的缺失值使用b2填充)
# 1:使用b2填充a2的缺失值(打補丁)
a2[a2.isnull()] = b2
# 2:合併資料
a22 = a2.append(b2)
# 3:去重,排序
a22.index.duplicated()
a22[~(a22.index.duplicated())].sort_index()

方法2:使用combine_first方法,先打補丁,再合併,再去重排序
a3 = a[2:].copy()
a3.combine_first(b2)


對於DataFrame,combine_first會在列上應用同樣操作,可以將其看做:
用傳遞物件中的資料為呼叫物件的缺失資料“打補丁”
df11 = pd.DataFrame({'a': [1., np.nan, 5., np.nan], 'b': [np.nan, 2., np.nan, 6.], 'c': range(2, 18, 4)})
df21 = pd.DataFrame({'a': [5., 4., np.nan, 3., 7.], 'b': [np.nan, 3., 4., 6., 8.]})
df11.combine_first(df21)  # 以df11為基準,先填充缺失值(用df21的值填充df11),再合併(df21的多餘行列合併到df11上)
df21.combine_first(df11)  # 以df21為基準,先填充(用df11的值填充df21),再合併