pandas的拼接操作
pandas的拼接分为两种:
============================================
练习12:
============================================
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]])
为方便讲解,我们首先定义一个生成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 |
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 |
不匹配指的是级联的维度的索引不一致。例如纵向级联时列索引不一致,横向级联时行索引不一致
有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() 参数
============================================
练习13:
============================================
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 |
由于在后面级联的使用非常普遍,因此有一个函数append专门用于在后面添加
df5.append(df3)
语文 | 数学 | 外语 | 计算机 | |
---|---|---|---|---|
张三 | 112 | 12 | 72 | 110 |
李四 | 123 | 79 | 2 | 37 |
王老五 | 120 | 115 | 140 | 11 |
============================================
练习15:
新建一个只有张三李四王老五的期末考试成绩单ddd3,使用append()与期中考试成绩表ddd级联
============================================
merge与concat的区别在于,merge需要依据某一共同的行或列来进行合并
使用pd.merge()合并时,会自动根据两者相同column名称的那一列,作为key来进行合并。
注意每一列元素的顺序不要求一致
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 |
使用on=显式指定哪一列为key,当有多个key相同时使用
使用left_on和right_on指定左右两边的列作为key,当左右两边的key都不想等时使用
============================================
练习16:
============================================
内合并:只保留两者都有的key(默认模式)
外合并 how=‘outer’:补NaN
左合并、右合并:how=‘left’,how=‘right’,
============================================
练习17:
============================================
当列冲突时,即有多个列名称相同时,需要使用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)
首先导入文件,并查看数据样本
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()
要点总结: