1. 程式人生 > >04-2pandas的拼接操作

04-2pandas的拼接操作

pandas的拼接操作

pandas的拼接分為兩種:

  • 級聯:pd.concat, pd.append (沒有重複資料)
  • 合併:pd.merge, pd.join (有重複資料)

0. 回顧numpy的級聯

============================================

練習12:

  1. 生成2個3*3的矩陣,對其分別進行兩個維度上的級聯

============================================

import numpy as np
import pandas as pd
from pandas import Series,DataFrame
nd1 = np.random.randint(0,10,size=(3,3))
nd2 = np.random.randint(10,100,size=(3,3))
display(nd1,nd2)
結果為:
array([[7, 2, 9],
       [4, 6, 5],
       [6, 0, 8]])
array([[18, 19, 44],
       [33, 71, 91],
       [69, 42, 33]])
np.concatenate((nd1,nd2))
結果為:
array([[ 5,  3,  9],
       [ 9,  4,  7],
       [ 3,  1,  5],
       [11, 46, 23],
       [40, 61, 70],
       [28, 47, 12]])
np.concatenate((nd1,nd2),axis=1)
結果為:
array([[ 7,  0,  8, 54, 81, 24],
       [ 3,  0,  3, 24, 66, 91],
       [ 3,  9,  5, 28, 40, 68]])

1. 使用pd.concat()級聯

為方便講解,我們首先定義一個生成DataFrame的函式:

df1 = DataFrame(nd1)
df2 = DataFrame(nd2)
display(df1,df2)
0 1 2
0 7 0 8
1 3 0 3
2 3 9 5
0 1 2
0 54 81 24
1 24 66 91
2 28 40 68

1) 簡單級聯

pandas使用pd.concat函式,與np.concatenate函式類似

df3 = pd.concat((df1,df2)) #預設 axis是0 是縱向拼接
df3
0 1 2
0 7 0 8
1 3 0 3
2 3 9 5
0 54 81 24
1 24 66 91
2 28 40 68

索引有重複 會產生一些問題

df3.loc[0]
0 1 2
0 7 0 8
0 54 81 24

可以通過 重置索引的方式 去重新讓索引不重複

# ignore_index=False 忽略原索引 建立新索引 預設是False
df3 = pd.concat((df1,df2),ignore_index=True,axis=0) # 預設是 0 豎直方向這樣寫:df3 = pd.concat((df1,df2),ignore_index=True)
df3
0 1 2
0 7 0 8
1 3 0 3
2 3 9 5
3 54 81 24
4 24 66 91
5 28 40 68
df3 = pd.concat((df1,df2),axis=1) # 1 水平方向拼接
df3
0 1 2 0 1 2
0 7 0 8 54 81 24
1 3 0 3 24 66 91
2 3 9 5 28 40 68
df3 = pd.concat((df1,df2),ignore_index=True,axis=1)
df3
0 1 2 3 4 5
0 7 0 8 54 81 24
1 3 0 3 24 66 91
2 3 9 5 28 40 68
df3 = pd.concat((df1,df2),keys=["第一個","第二個"])
df3
0 1 2
第一個 0 7 0 8
1 3 0 3
2 3 9 5
第二個 0 54 81 24
1 24 66 91
2 28 40 68

2) 不匹配級聯

不匹配指的是級聯的維度的索引不一致。例如縱向級聯時列索引不一致,橫向級聯時行索引不一致

有3種連線方式:

  • 外連線:補NaN(預設模式)

  • 內連線:只連線匹配的項

  • 連線指定軸 join_axes

nd1,nd2
結果為:
(array([[7, 0, 8],
        [3, 0, 3],
        [3, 9, 5]]),
 array([[54, 81, 24],
        [24, 66, 91],
        [28, 40, 68]]))

把相同索引的行或列進行級聯,如果存在不匹配的行列標籤,補nan

df3 = DataFrame(data=nd1,columns=list("ABC"))
df4 = DataFrame(data=nd2,columns=list("BCD"))
display(df3,df4)
A B C
0 7 0 8
1 3 0 3
2 3 9 5
B C D
0 54 81 24
1 24 66 91
2 28 40 68
pd.concat((df3,df4),sort=False) # dataframe 拼接 預設是 外聯 
A B C D
0 7.0 0 8 NaN
1 3.0 0 3 NaN
2 3.0 9 5 NaN
0 NaN 54 81 24.0
1 NaN 24 66 91.0
2 NaN 28 40 68.0
pd.concat((df3,df4),sort=False,join="inner") 
B C
0 0 8
1 0 3
2 9 5
0 54 81
1 24 66
2 28 40
pd.concat((df3,df4),sort="False",axis=1,ignore_index="True")
0 1 2 3 4 5
0 7 0 8 54 81 24
1 3 0 3 24 66 91
2 3 9 5 28 40 68

使用keys引數,可以自動設定為多層級索引,避免索引重複

pd.concat((df3,df4),keys=['期中','期末'],sort="False")
A B C D
期中 0 7.0 0 8 NaN
1 3.0 0 3 NaN
2 3.0 9 5 NaN
期末 0 NaN 54 81 24.0
1 NaN 24 66 91.0
2 NaN 28 40 68.0
index = pd.Index(["B","C"])#改變列標題
pd.concat((df3,df4),sort="False",join_axes=[index])
B C
0 0 8
1 0 3
2 9 5
0 54 81
1 24 66
2 28 40

總結:pd.concat() 引數

  1. objs 傳入列表或者元素 裡面是要拼接的DataFrame
  2. axis 拼接的時候是沿著什麼方向 預設值是0 縱向 如果是1就是橫向
  3. join 指定了拼接的方式 預設是outer
    outer 外聯 所有的列都會拼進來
    inner 內聯 只有那些兩個DataFrame都有的列才會拼進來
  4. join_axes 直接指定那些列要放進來
  5. ignore_index=False 忽略原有索引建立新的索引 (如果索引有重複可以通過忽略原索引來重置)
  6. keys 可以把不同的DataFrame分成多組 也可以用來解決index重複的問

============================================

練習13:

  1. 想一想級聯的應用場景?
  2. 使用昨天的知識,建立一個期中考試張三、李四的成績表df
  3. 假設新增考試學科"計算機",如何實現?
  4. 新增王老五同學的成績,如何實現?

============================================

import numpy as np
data = np.random.randint(0,150,size=(2,3))
index = ["張三","李四"]
columns = ["語文","數學","外語"]
df1 = DataFrame(data,index,columns)
df1
語文 數學 外語
張三 112 12 72
李四 123 79 2
data = np.random.randint(0,150,size=(2,1))
index = ["張三","李四"]
columns = ["計算機"]
df2 = DataFrame(data,index,columns)
df2
計算機
張三 110
李四 37
df5 = pd.concat((df1,df2),axis=1,sort=False)
df5
語文 數學 外語 計算機
張三 112 12 72 110
李四 123 79 2 37
data = np.random.randint(0,150,size=(1,4))
index = ["王老五"]
columns = ["語文","數學","外語","計算機"]
df3 = DataFrame(data,index,columns)
df3
語文 數學 外語 計算機
王老五 120 115 140 11
pd.concat((df1,df3),sort=False)
語文 數學 外語 計算機
張三 112 12 72 NaN
李四 123 79 2 NaN
王老五 120 115 140 11.0

3) 使用append()函式新增

由於在後面級聯的使用非常普遍,因此有一個函式append專門用於在後面新增

df5.append(df3)
語文 數學 外語 計算機
張三 112 12 72 110
李四 123 79 2 37
王老五 120 115 140 11

============================================

練習15:

新建一個只有張三李四王老五的期末考試成績單ddd3,使用append()與期中考試成績表ddd級聯

============================================

2. 使用pd.merge()合併

merge與concat的區別在於,merge需要依據某一共同的行或列來進行合併

使用pd.merge()合併時,會自動根據兩者相同column名稱的那一列,作為key來進行合併。

注意每一列元素的順序不要求一致

1) 一對一合併

2) 多對一合併

3) 多對多合併

table1 = pd.read_excel("./關係表.xls",sheet_name=0)
table2 = pd.read_excel("./關係表.xls",sheet_name=1)
table3 = pd.read_excel("./關係表.xls",sheet_name=2)
table4 = pd.read_excel("./關係表.xls",sheet_name=3)
table5 = pd.read_excel("./關係表.xls",sheet_name=4)
display(table1,table2)
手機型號 參考價格
0 windowsPhone 2500
1 iPhone 7500
2 Android 4000
手機型號 重量
0 windowsPhone 0.50
1 iPhone 0.40
2 Android 0.45
3 other 0.60

一一對應的表格 通過merge融合 把資料對應上就可以了

pd.merge(table1,table2)
手機型號 參考價格 重量
0 windowsPhone 2500 0.50
1 iPhone 7500 0.40
2 Android 4000 0.45

how指的是如何拼接 預設是inner 內聯 (兩個表格都有的專案才留下){‘left’, ‘right’, ‘outer’, ‘inner’}, default ‘inner’

pd.merge(table1,table2,how="inner") # inner  是取交集 兩個都有的專案才出現
手機型號 參考價格 重量
0 windowsPhone 2500 0.50
1 iPhone 7500 0.40
2 Android 4000 0.45
pd.merge(table1,table2,how="outer") # outer 是取並集 任何一個表格裡出現的專案都會出現
手機型號 參考價格 重量
0 windowsPhone 2500.0 0.50
1 iPhone 7500.0 0.40
2 Android 4000.0 0.45
3 other NaN 0.60
pd.merge(table1,table2,how="left") # 左邊的表格有多少專案 這裡就有多少專案
手機型號 參考價格 重量
0 windowsPhone 2500 0.50
1 iPhone 7500 0.40
2 Android 4000 0.45
pd.merge(table1,table2,how="right") # 右邊的表格有多少專案 這裡就有多少專案
手機型號 參考價格 重量
0 windowsPhone 2500.0 0.50
1 iPhone 7500.0 0.40
2 Android 4000.0 0.45
3 other NaN 0.60

一對多

display(table2,table3)
手機型號 重量
0 windowsPhone 0.50
1 iPhone 0.40
2 Android 0.45
3 other 0.60
經銷商 發貨地區 手機型號
0 pegge beijing iPhone
1 lucy beijing Android
2 tom guangzhou iPhone
3 petter shenzhen windowsPhone
4 mery guangzhou Android

一對多 的 表格拼接 兩個表格需要有一個相同的column 然後在把1*多個 計算出新的行

pd.merge(table2,table3,how="inner")
手機型號 重量 經銷商 發貨地區
0 windowsPhone 0.50 petter shenzhen
1 iPhone 0.40 pegge beijing
2 iPhone 0.40 tom guangzhou
3 Android 0.45 lucy beijing
4 Android 0.45 mery guangzhou
pd.merge(table2,table3,how="outer")
手機型號 重量 經銷商 發貨地區
0 windowsPhone 0.50 petter shenzhen
1 iPhone 0.40 pegge beijing
2 iPhone 0.40 tom guangzhou
3 Android 0.45 lucy beijing
4 Android 0.45 mery guangzhou
5 other 0.60 NaN NaN
pd.merge(table2,table3,how="left")
手機型號 重量 經銷商 發貨地區
0 windowsPhone 0.50 petter shenzhen
1 iPhone 0.40 pegge beijing
2 iPhone 0.40 tom guangzhou
3 Android 0.45 lucy beijing
4 Android 0.45 mery guangzhou
5 other 0.60 NaN NaN
pd.merge(table2,table3,how="right")
手機型號 重量 經銷商 發貨地區
0 windowsPhone 0.50 petter shenzhen
1 iPhone 0.40 pegge beijing
2 iPhone 0.40 tom guangzhou
3 Android 0.45 lucy beijing
4 Android 0.45 mery guangzhou

多對多

display(table3,table4)
經銷商 發貨地區 手機型號
0 pegge beijing iPhone
1 lucy beijing Android
2 tom guangzhou iPhone
3 petter shenzhen windowsPhone
4 mery guangzhou Android
發貨地區 手機型號 價格
0 beijing iPhone 7000
1 beijing windowsPhone 2300
2 beijing Android 3600
3 guangzhou iPhone 7600
4 guangzhou windowsPhone 2800
5 guangzhou Android 4200
6 shenzhen iPhone 7400
7 shenzhen windowsPhone 2750
8 shenzhen Android 3900

方式1 我們通過on 指定 按照哪一列進行拼接 然後可以通過suffixes指定重複的列的字尾

pd.merge(table3,table4,on="手機型號",suffixes=["_1","_2"])
經銷商 發貨地區_1 手機型號 發貨地區_2 價格
0 pegge beijing iPhone beijing 7000
1 pegge beijing iPhone guangzhou 7600
2 pegge beijing iPhone shenzhen 7400
3 tom guangzhou iPhone beijing 7000
4 tom guangzhou iPhone guangzhou 7600
5 tom guangzhou iPhone shenzhen 7400
6 lucy beijing Android beijing 3600
7 lucy beijing Android guangzhou 4200
8 lucy beijing Android shenzhen 3900
9 mery guangzhou Android beijing 3600
10 mery guangzhou Android guangzhou 4200
11 mery guangzhou Android shenzhen 3900
12 petter shenzhen windowsPhone beijing 2300
13 petter shenzhen windowsPhone guangzhou 2800
14 petter shenzhen windowsPhone shenzhen 2750

第二種方式 指定兩個相同的列 這兩列中的專案必須都對應上才會顯示到新的表格中

pd.merge(table3,table4,on=["手機型號","發貨地區"])
經銷商 發貨地區 手機型號 價格
0 pegge beijing iPhone 7000
1 lucy beijing Android 3600
2 tom guangzhou iPhone 7600
3 petter shenzhen windowsPhone 2750
4 mery guangzhou Android 4200
display(table4,table5)
發貨地區 手機型號 價格
0 beijing iPhone 7000
1 beijing windowsPhone 2300
2 beijing Android 3600
3 guangzhou iPhone 7600
4 guangzhou windowsPhone 2800
5 guangzhou Android 4200
6 shenzhen iPhone 7400
7 shenzhen windowsPhone 2750
8 shenzhen Android 3900
型號 價格
0 iPhone 7000
1 windowsPhone 2300
2 Android 3600
3 iPhone 7600
4 windowsPhone 2800
5 Android 4200
6 iPhone 7400
7 windowsPhone 2750
8 Android 3900

4) key的規範化

  • 使用on=顯式指定哪一列為key,當有多個key相同時使用

  • 使用left_on和right_on指定左右兩邊的列作為key,當左右兩邊的key都不想等時使用

============================================

練習16:

  1. 假設有兩份成績單,除了ddd是張三李四王老五之外,還有ddd4是張三和趙小六的成績單,如何合併?
  2. 如果ddd4中張三的名字被打錯了,成為了張十三,怎麼辦?
  3. 自行練習多對一,多對多的情況
  4. 自學left_index,right_index

============================================

5) 內合併與外合併

  • 內合併:只保留兩者都有的key(預設模式)

  • 外合併 how=‘outer’:補NaN

  • 左合併、右合併:how=‘left’,how=‘right’,

============================================

練習17:

  1. 考慮應用情景,使用多種方式合併ddd與ddd4

============================================

6) 列衝突的解決

當列衝突時,即有多個列名稱相同時,需要使用on=來指定哪一個列作為key,配合suffixes指定衝突列名

可以使用suffixes=自己指定字尾

============================================

練習18:

假設有兩個同學都叫李四,ddd5、ddd6都是張三和李四的成績表,如何合併?

============================================

知識補充

s1 = Series(["A","B","C","B"])
s1
結果為:
0    A
1    B
2    C
3    B
dtype: object
s1.unique()   #去重
結果為:
array(['A', 'B', 'C'], dtype=object)

作業

3. 案例分析:美國各州人口資料分析

首先匯入檔案,並檢視資料樣本

df_abbr = pd.read_csv("./data/state-abbrevs.csv") # csv檔案的資料匯入後 會變成DataFrame供我們使用
df_areas = pd.read_csv("./data/state-areas.csv") 
df_pop = pd.read_csv("./data/state-population.csv")

合併popu與abbrevs兩個DataFrame,分別依據state/region列和abbreviation列來合併。為了保留所有資訊,使用外合併。

df_pop2 = pd.merge(df_abbr,df_pop,left_on="abbreviation",right_on="state/region",how="outer")

去除abbreviation的那一列(axis=1)

df_pop3 = df_pop2.drop(labels="abbreviation",axis=1)
df_pop3

檢視存在缺失資料的列。使用.isnull().any(),只有某一列存在一個缺失資料,就會顯示True。

df_pop3.isnull().any() # isnull()有空值是True 沒有空值是False any()只要有True就是True 合在一起使用 就是 這一列中 只要有空值就是true
state            True
state/region    False
ages            False
year            False
population       True
dtype: bool

找到有哪些state/region使得state的值為NaN,使用unique()檢視非重複值

df_pop3["state"].isnull() #這是一個序列 有值是False 沒有值是True
df_pop3[df_pop3["state"].isnull()] #DataFrame 後面 的中括號 中可以傳入 序列 如果序列中是布林值 False這這一項不取 True就取出這一項
df_pop3[df_pop3["state"].isnull()]["state/region"].unique()
結果為:
array(['PR', 'USA'], dtype=object)

為找到的這些state/region的state項補上正確的值,從而去除掉state這一列的所有NaN!記住這樣清除缺失資料NaN的方法!

df_pop3[df_pop3["state"].isnull()]["state"] = "Puerto Rico" # 為了安全不能直接設定值 
temp = df_pop3[df_pop3["state"].isnull()].copy()
temp["state"] = "Puerto Rico"
df_pop3[df_pop3["state"].isnull()] = temp # 不能直接操作值 但是可以把DataFrame賦值給DataFrame
temp2 = df_pop3[df_pop3["state/region"]=="USA"].copy()
temp2["state"]="United States"
df_pop3[df_pop3["state/region"]=="USA"] = temp2
df_pop3.isnull().any()
結果為:
state           False
state/region    False
ages            False
year            False
population       True
dtype: bool
df_pop4 = df_pop3.dropna()  #清除人口中為空的資料  
df_pop4.isnull().any()
結果為:
state           False
state/region    False
ages            False
year            False
population      False
dtype: bool

合併各州人口資料和麵積資料areas,使用外合併。

思考一下為什麼使用外合併?

df_pop_area = pd.merge(df_pop4,df_areas,how="outer")
df_pop_area.dropna() #清除面積為空的資料

找出2010年的全民人口資料,df.query(查詢語句)

df_2010 = df_pop_area.query("year==2010 & ages=='total'")
df_2010.dropna()

對查詢結果進行處理,以state列作為新的行索引:set_index

df_2010.set_index("state") 

計算人口密度。注意是Series/Series,其結果還是一個Series。

df_2010 = df_2010.dropna()
dens = df_2010["population"]/df_2010["area (sq. mi)"]

排序,並找出人口密度最高的五個州sort_values()的密度

dens.sort_values().tail(5)

找出人口密度最低的五個州的密度

dens.sort_values().head()

要點總結:

  • 統一用loc()索引
  • 善於使用.isnull().any()找到存在NaN的列
  • 善於使用.unique()確定該列中哪些key是我們需要的
  • 一般使用外合併、左合併,目的只有一個:寧願該列是NaN也不要丟棄其他列的資訊