第3章 Pandas数据处理(3.7-3.8)_Python数据科学手册学习笔记

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))
ABC
0A0B0C0
1A1B1C1
2A2B2C2

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)
AB
0A0B0
1A1B1
2A2B2
3A3B3

增加多级索引: 通过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'])
namerank_lrank_r
0bob13
1jake21
2lisa34
3sue42

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/regionagesyearpopulationstate
2448PRunder181990NaNNaN
2449PRtotal1990NaNNaN
2450PRtotal1991NaNNaN
2451PRunder181991NaNNaN
2452PRtotal1993NaNNaN
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/regionagesyearpopulationstatearea (sq. mi)
0ALunder1820121117489.0Alabama52423.0
1ALtotal20124817528.0Alabama52423.0
2ALunder1820101130966.0Alabama52423.0
3ALtotal20104785570.0Alabama52423.0
4ALunder1820111125763.0Alabama52423.0
data2010 = final.query("year == 2010 & ages == 'total'")
data2010.head()
state/regionagesyearpopulationstatearea (sq. mi)
3ALtotal20104785570.0Alabama52423.0
91AKtotal2010713868.0Alaska656425.0
101AZtotal20106408790.0Arizona114006.0
189ARtotal20102922280.0Arkansas53182.0
197CAtotal201037333601.0California163707.0
# data2010.set_index('state',inplace=True)    # 为什么报错
# density = data2010['population'] / data2010['area(sq.mi)']
# density.sort_values(ascending=False,inplace=True)
# density.head()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

python技巧(数据分析及可视化)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值