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也不要丢弃其他列的信息

你可能感兴趣的:(学习)