3.7 合并数据集: Concat与Append操作
- pd.concat
- pd.merge
- pd.join
import pandas as pd
def make_df(cols,ind):
data = {c: [str(c) + str(i) for i in ind]
for c in cols}
return pd.DataFrame(data,ind) # 用字典创建DataFrame
make_df('ABC',range(3))
A | B | C | |
---|---|---|---|
0 | A0 | B0 | C0 |
1 | A1 | B1 | C1 |
2 | A2 | B2 | C2 |
3.7.1 知识回顾: NumPy数组的合并
用np.concatenate完成
- 第一个参数表示要合并的数组或元组, 第二个参数表示合并的坐标轴方向
import pandas as pd
import numpy as np
x = [1,2,3]
y = [4,5,6]
z = [7,8,9]
np.concatenate([x,y,z])
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
x = [[1,2],
[3,4]]
np.concatenate([x,x], axis=1)
array([[1, 2, 1, 2],
[3, 4, 3, 4]])
3.7.2 通过pd.concat实现简易合并
- Pandas中的pd.concat()函数与np.concatenate()类似, 配置参数更多,功能更强大.
pd.concat()可以简单的合并一维的Series和DataFrame对象
ser1 = pd.Series(['a','b','c'],index=[1,2,3])
ser2 = pd.Series(['d','e','f'],index=[4,5,6])
pd.concat([ser1,ser2])
1 a
2 b
3 c
4 d
5 e
6 f
dtype: object
ser1 = pd.Series(['a','b','c'],index=[1,2,3])
ser2 = pd.Series(['d','e','f'],index=[4,5,6])
ser1 + ser2
1 NaN
2 NaN
3 NaN
4 NaN
5 NaN
6 NaN
dtype: object
ser1 = pd.Series(['a','b','c'],index=[1,2,3])
ser2 = pd.Series(['d','e','f'],index=[1,2,3])
ser1 + ser2
1 ad
2 be
3 cf
dtype: object
df1 = make_df('AB',[1,2])
df2 = make_df('AB',[3,4])
print(df1);print(df2);print(pd.concat([df1,df2]))
A B
1 A1 B1
2 A2 B2
A B
3 A3 B3
4 A4 B4
A B
1 A1 B1
2 A2 B2
3 A3 B3
4 A4 B4
df1 = make_df('AB',[1,2])
df2 = make_df('BD',[3,4])
print(df1);print(df2);print(pd.concat([df1,df2]))
A B
1 A1 B1
2 A2 B2
B D
3 B3 D3
4 B4 D4
A B D
1 A1 B1 NaN
2 A2 B2 NaN
3 NaN B3 D3
4 NaN B4 D4
D:\Anaconda3\lib\site-packages\ipykernel_launcher.py:3: FutureWarning: Sorting because non-concatenation axis is not aligned. A future version
of pandas will change to not sort by default.
To accept the future behavior, pass 'sort=True'.
To retain the current behavior and silence the warning, pass sort=False
This is separate from the ipykernel package so we can avoid doing imports until
索引重叠
np.concatenate与pd.concat最主要的差异是,Pandas在合并时会保留索引, 即使索引是重复的.
x = make_df('AB',[0,1])
y = make_df('AB',[2,3])
y.index = x.index
print(x);print(y);print(pd.concat([x,y]))
A B
0 A0 B0
1 A1 B1
A B
0 A2 B2
1 A3 B3
A B
0 A0 B0
1 A1 B1
0 A2 B2
1 A3 B3
捕捉索引重复的错误: 可以设置verify_integrity参数. 将参数设置为true. 合并时若有重复就会触发异常.
try:
pd.concat([x,y],verify_integrity=True)
except ValueError as e:
print('ValueError:',e)
ValueError: Indexes have overlapping values: Int64Index([0, 1], dtype='int64')
忽略索引: 设置ignore_index参数, 设置为True是会创建一个新的整数索引
pd.concat([x,y],ignore_index=True)
A | B | |
---|---|---|
0 | A0 | B0 |
1 | A1 | B1 |
2 | A2 | B2 |
3 | A3 | B3 |
增加多级索引: 通过keys参数为数据源设置多级索引
print(x);print(y);print(pd.concat([x,y],keys=['x','y']))
A B
0 A0 B0
1 A1 B1
A B
0 A2 B2
1 A3 B3
A B
x 0 A0 B0
1 A1 B1
y 0 A2 B2
1 A3 B3
类似join的合并
df5 = make_df('ABC',[1,2])
df6 = make_df('BCD',[3,4])
print(df5);print(df6);print(pd.concat([df5,df6]))
A B C
1 A1 B1 C1
2 A2 B2 C2
B C D
3 B3 C3 D3
4 B4 C4 D4
A B C D
1 A1 B1 C1 NaN
2 A2 B2 C2 NaN
3 NaN B3 C3 D3
4 NaN B4 C4 D4
D:\Anaconda3\lib\site-packages\ipykernel_launcher.py:3: FutureWarning: Sorting because non-concatenation axis is not aligned. A future version
of pandas will change to not sort by default.
To accept the future behavior, pass 'sort=True'.
To retain the current behavior and silence the warning, pass sort=False
This is separate from the ipykernel package so we can avoid doing imports until
设置join参数为inner或者outer
print(df5);print(df6);print(pd.concat([df5,df6],join='inner'))
A B C
1 A1 B1 C1
2 A2 B2 C2
B C D
3 B3 C3 D3
4 B4 C4 D4
B C
1 B1 C1
2 B2 C2
3 B3 C3
4 B4 C4
直接确定结果使用的列
print(df5);print(df6);print(pd.concat([df5,df6],join_axes=[df5.columns]))
A B C
1 A1 B1 C1
2 A2 B2 C2
B C D
3 B3 C3 D3
4 B4 C4 D4
A B C
1 A1 B1 C1
2 A2 B2 C2
3 NaN B3 C3
4 NaN B4 C4
print(df1);print(df2);print(df1.append(df2))
A B
1 A1 B1
2 A2 B2
A B
3 A3 B3
4 A4 B4
A B
1 A1 B1
2 A2 B2
3 A3 B3
4 A4 B4
3.8 合并数据集: 合并与连接
- Pandas的主要接口是pd.merge()函数
3.8.1 关系代数
pd.merge()实现的功能基于关系代数的一部分. 关系代数式处理关系型数据的通用理论,绝大部分数据库的可用操作都以此为理论基础. 关系代数方法论的强大之处在于, 它提出的若干简单的操作规则经过组合就可以为任意数据构建十分复杂的操作.
Pandas在pd.merge()函数与Series和DataFrame的join()方法里实现了这些基本操作规则.
3.8.2 数据连接的类型
pd.merge()函数实现三种数据连接的类型: 一对一,多对一和多对多.
** 一对一连接 **
import pandas as pd
df1 = pd.DataFrame({'雇员':['张三','李四','王五','赵六'],'部门':['生产','技术','技术','人力']}) # 需要加大括号
df2 = pd.DataFrame({'雇员':['赵六','李四','张三','王五'],'入职年份':[2010,2011,2012,2011]})
print(df1);print(df2)
雇员 部门
0 张三 生产
1 李四 技术
2 王五 技术
3 赵六 人力
雇员 入职年份
0 赵六 2010
1 李四 2011
2 张三 2012
3 王五 2011
df3 = pd.merge(df1,df2)
print(df3)
雇员 部门 入职年份
0 张三 生产 2012
1 李四 技术 2011
2 王五 技术 2011
3 赵六 人力 2010
pd.merge()方法会发现两个DataFrame都有’雇员’列, 并自动以这列为键进行连接. 两个输入的合并结果是一个新的DataFrame. 共同列的位置可以不一致.
pd.merge()会默认丢弃原来的行索引, 不过也可以自定义.
多对一连接 能否也叫一对多连接
需要连接的两个列中, 有一列的值有重复. 通过多对一连接获得的结果DataFrame将会保留重复值.
df4 = pd.DataFrame({'部门':['生产','技术','人力'],'负责人':['生产经理','技术经理','人力经理']})
print(df3);print(df4);print(pd.merge(df3,df4))
雇员 部门 入职年份
0 张三 生产 2012
1 李四 技术 2011
2 王五 技术 2011
3 赵六 人力 2010
部门 负责人
0 生产 生产经理
1 技术 技术经理
2 人力 人力经理
雇员 部门 入职年份 负责人
0 张三 生产 2012 生产经理
1 李四 技术 2011 技术经理
2 王五 技术 2011 技术经理
3 赵六 人力 2010 人力经理
多对多连接
df5 = pd.DataFrame({'部门':['生产','生产','技术','技术','人力','人力'],'子部门':['生产1','生产2','技术1','技术2','人力1','人力2']})
print(df1);print(df5);print(pd.merge(df1,df5))
雇员 部门
0 张三 生产
1 李四 技术
2 王五 技术
3 赵六 人力
部门 子部门
0 生产 生产1
1 生产 生产2
2 技术 技术1
3 技术 技术2
4 人力 人力1
5 人力 人力2
雇员 部门 子部门
0 张三 生产 生产1
1 张三 生产 生产2
2 李四 技术 技术1
3 李四 技术 技术2
4 王五 技术 技术1
5 王五 技术 技术2
6 赵六 人力 人力1
7 赵六 人力 人力2
3.8.3 设置数据合并的键
pd.merge()的默认行为, 它会将两个输入的一个或多个共同列作为键进行合并. 但由于两个输入要合并的列通常都不是同名的, 因此pd.merge()提供了一些参数处理这个问题.
参数on的用法
- 这个参数只能在两个DataFrame有共同列名的时候才可以使用.
print(df1);print(df2);print(pd.merge(df1,df2,on='雇员'))
雇员 部门
0 张三 生产
1 李四 技术
2 王五 技术
3 赵六 人力
雇员 入职年份
0 赵六 2010
1 李四 2011
2 张三 2012
3 王五 2011
雇员 部门 入职年份
0 张三 生产 2012
1 李四 技术 2011
2 王五 技术 2011
3 赵六 人力 2010
left_on与right_on参数
- 有时候也需要合并两个列名不同的数据集.
df3 = pd.DataFrame({'姓名':['张三','李四','王五','赵六'],'工资':[5000,6000,7000,5500]})
print(df1);print(df2);print(pd.merge(df1,df3,left_on='雇员',right_on='姓名'))
雇员 部门
0 张三 生产
1 李四 技术
2 王五 技术
3 赵六 人力
雇员 入职年份
0 赵六 2010
1 李四 2011
2 张三 2012
3 王五 2011
雇员 部门 姓名 工资
0 张三 生产 张三 5000
1 李四 技术 李四 6000
2 王五 技术 王五 7000
3 赵六 人力 赵六 5500
汇总的结果会多一列, 可以用drop()方法去掉.
pd.merge(df1,df3,left_on='雇员',right_on='姓名').drop('姓名',axis=1)
雇员 | 部门 | 工资 | |
---|---|---|---|
0 | 张三 | 生产 | 5000 |
1 | 李四 | 技术 | 6000 |
2 | 王五 | 技术 | 7000 |
3 | 赵六 | 人力 | 5500 |
left_index与right_index参数
- 除了合并列之外, 还可以合并索引.
df1a = df1.set_index('雇员')
df2a = df2.set_index('雇员')
print(df1a);print(df2a)
部门
雇员
张三 生产
李四 技术
王五 技术
赵六 人力
入职年份
雇员
赵六 2010
李四 2011
张三 2012
王五 2011
可以通过设置pd.merge()中的left_index或right_index参数将索引设置为键来实现合并.
print(df1a);print(df2a);
print(pd.merge(df1a,df2a,left_index=True,right_index=True)) # 是True , 不是 = 索引的名称
部门
雇员
张三 生产
李四 技术
王五 技术
赵六 人力
入职年份
雇员
赵六 2010
李四 2011
张三 2012
王五 2011
部门 入职年份
雇员
张三 生产 2012
李四 技术 2011
王五 技术 2011
赵六 人力 2010
为了方便考虑, DataFrame实现了join()方法, 它可以按照索引进行数据合并.
print(df1a);print(df2a);print(df1a.join(df2a))
部门
雇员
张三 生产
李四 技术
王五 技术
赵六 人力
入职年份
雇员
赵六 2010
李四 2011
张三 2012
王五 2011
部门 入职年份
雇员
张三 生产 2012
李四 技术 2011
王五 技术 2011
赵六 人力 2010
如果想将索引与列混合使用, 可以使用left_index与right_on, 或者结合left_on与right_index
print(df1a);print(df3)
print(pd.merge(df1a,df3,left_index=True,right_on='姓名')) # 多个索引怎么办
部门
雇员
张三 生产
李四 技术
王五 技术
赵六 人力
姓名 工资
0 张三 5000
1 李四 6000
2 王五 7000
3 赵六 5500
部门 姓名 工资
0 生产 张三 5000
1 技术 李四 6000
2 技术 王五 7000
3 人力 赵六 5500
3.8.4 设置数据连接的集合操作规则
df6 = pd.DataFrame({'姓名':['张三','李四','王五','赵六'],
'食物':['鱼','西瓜','冬瓜','大头菜']})
df7 = pd.DataFrame({'姓名':['张三','老李'],
'喝酒':['白酒','红酒']})
print(df6);print(df7);print(pd.merge(df6,df7))
姓名 食物
0 张三 鱼
1 李四 西瓜
2 王五 冬瓜
3 赵六 大头菜
姓名 喝酒
0 张三 白酒
1 老李 红酒
姓名 食物 喝酒
0 张三 鱼 白酒
和别的两个数据集, 在’姓名’列中只有一个共同的值’张三’. 默认情况下, 结果中只出现会包含两个输入集合的交集(inner join). 这种连接方式被称为内连接. 我们可以通过how参数设置连接方式,默认为’inner’
pd.merge(df6,df7,how='inner')
姓名 | 食物 | 喝酒 | |
---|---|---|---|
0 | 张三 | 鱼 | 白酒 |
how参数支持的连接方式: outer, left,inner 和right.
- outer(外连接)返回两个输入列的并集, 所有缺失值都用NaN填充.
print(df6);print(df7);print(pd.merge(df6,df7,how='outer'))
姓名 食物
0 张三 鱼
1 李四 西瓜
2 王五 冬瓜
3 赵六 大头菜
姓名 喝酒
0 张三 白酒
1 老李 红酒
姓名 食物 喝酒
0 张三 鱼 白酒
1 李四 西瓜 NaN
2 王五 冬瓜 NaN
3 赵六 大头菜 NaN
4 老李 NaN 红酒
left join(左连接), right join(右连接)返回的结果分别只包括左列和右列
print(df6);print(df7);print(pd.merge(df6,df7,how='left'))
姓名 食物
0 张三 鱼
1 李四 西瓜
2 王五 冬瓜
3 赵六 大头菜
姓名 喝酒
0 张三 白酒
1 老李 红酒
姓名 食物 喝酒
0 张三 鱼 白酒
1 李四 西瓜 NaN
2 王五 冬瓜 NaN
3 赵六 大头菜 NaN
3.8.5 重复列名: suffixes参数
- 可能会遇到两个输入DataFrame有重名列的情况.
df8 = pd.DataFrame({'name':['bob','jake','lisa','sue'],
'rank':[1,2,3,4]})
df9 = pd.DataFrame({'name':['bob','jake','lisa','sue'],
'rank':[3,1,4,2]})
print(df8);print(df9);print(pd.merge(df8,df9,on='name'))
name rank
0 bob 1
1 jake 2
2 lisa 3
3 sue 4
name rank
0 bob 3
1 jake 1
2 lisa 4
3 sue 2
name rank_x rank_y
0 bob 1 3
1 jake 2 1
2 lisa 3 4
3 sue 4 2
当有两个重复的列名, pd.merge()函数会自动为它们增加后缀_x _y.
也可以通过suffixes参数自定义后缀
pd.merge(df8,df9,on='name',suffixes=['_l','_r'])
name | rank_l | rank_r | |
---|---|---|---|
0 | bob | 1 | 3 |
1 | jake | 2 | 1 |
2 | lisa | 3 | 4 |
3 | sue | 4 | 2 |
3.8.6 案例: 美国各州的统计数据
# 文件路径不能有中文
pop = pd.read_csv(r'E:\Python\state-population.csv')
areas = pd.read_csv(r'E:\Python\state-abbrevs.csv')
abbrevs = pd.read_csv(r'E:\Python\state-areas.csv')
print(pop.head());print(areas.head());print(abbrevs.head())
state/region ages year population
0 AL under18 2012 1117489.0
1 AL total 2012 4817528.0
2 AL under18 2010 1130966.0
3 AL total 2010 4785570.0
4 AL under18 2011 1125763.0
state abbreviation
0 Alabama AL
1 Alaska AK
2 Arizona AZ
3 Arkansas AR
4 California CA
state area (sq. mi)
0 Alabama 52423
1 Alaska 656425
2 Arizona 114006
3 Arkansas 53182
4 California 163707
merged = pd.merge(pop,areas,how='outer',left_on='state/region',right_on='abbreviation')
merged = merged.drop('abbreviation',1) # 参数1不能少
print(merged.head())
state/region ages year population state
0 AL under18 2012 1117489.0 Alabama
1 AL total 2012 4817528.0 Alabama
2 AL under18 2010 1130966.0 Alabama
3 AL total 2010 4785570.0 Alabama
4 AL under18 2011 1125763.0 Alabama
检查每个字段是否有缺失值
merged.isnull().any()
state/region False
ages False
year False
population True
state True
dtype: bool
部分population是缺失值, 查看是哪些数据缺失值
merged[merged['population'].isnull()].head()
state/region | ages | year | population | state | |
---|---|---|---|---|---|
2448 | PR | under18 | 1990 | NaN | NaN |
2449 | PR | total | 1990 | NaN | NaN |
2450 | PR | total | 1991 | NaN | NaN |
2451 | PR | under18 | 1991 | NaN | NaN |
2452 | PR | total | 1993 | NaN | NaN |
merged.loc[merged['state'].isnull(),'state/region'].unique()
array(['PR', 'USA'], dtype=object)
merged.loc[merged['state/region'] == 'PR','state'] = 'Puerto Rico'
merged.loc[merged['state/region'] == 'USA','state'] = 'Uinted States'
merged.isnull().any()
state/region False
ages False
year False
population True
state False
dtype: bool
final = pd.merge(merged,abbrevs,on='state',how='left')
final.head()
state/region | ages | year | population | state | area (sq. mi) | |
---|---|---|---|---|---|---|
0 | AL | under18 | 2012 | 1117489.0 | Alabama | 52423.0 |
1 | AL | total | 2012 | 4817528.0 | Alabama | 52423.0 |
2 | AL | under18 | 2010 | 1130966.0 | Alabama | 52423.0 |
3 | AL | total | 2010 | 4785570.0 | Alabama | 52423.0 |
4 | AL | under18 | 2011 | 1125763.0 | Alabama | 52423.0 |
data2010 = final.query("year == 2010 & ages == 'total'")
data2010.head()
state/region | ages | year | population | state | area (sq. mi) | |
---|---|---|---|---|---|---|
3 | AL | total | 2010 | 4785570.0 | Alabama | 52423.0 |
91 | AK | total | 2010 | 713868.0 | Alaska | 656425.0 |
101 | AZ | total | 2010 | 6408790.0 | Arizona | 114006.0 |
189 | AR | total | 2010 | 2922280.0 | Arkansas | 53182.0 |
197 | CA | total | 2010 | 37333601.0 | California | 163707.0 |
# data2010.set_index('state',inplace=True) # 为什么报错
# density = data2010['population'] / data2010['area(sq.mi)']
# density.sort_values(ascending=False,inplace=True)
# density.head()