目录
1. 学习内容
1. 学会如何对已有的表格新增行和列以及如何将不同的表格合并
2. 学会如何用其他表格的数据来对当前表格进行填充
2. 准备工作
import numpy as np
import pandas as pd
df = pd.read_csv('data/table.csv')
print(df.head())
School Class ID Gender Address Height Weight Math Physics
0 S_1 C_1 1101 M street_1 173 63 34.0 A+
1 S_1 C_1 1102 F street_2 192 73 32.5 B+
2 S_1 C_1 1103 M street_2 186 82 87.2 B+
3 S_1 C_1 1104 F street_2 167 81 80.4 B-
4 S_1 C_1 1105 F street_4 159 64 84.8 B+
3. append()方法和assign()方法
append()用来添加行,assign()用来添加列。
3.1 append()方法
3.1.1 利用序列添加单行
df_append = df.loc[:3, ['Gender','Height']].copy()
print(df_append)
Gender Height
0 M 173
1 F 192
2 M 186
3 F 167
s = pd.Series({'Gender':'F', 'Height':188}, name = 'new_row')
print(df_append.append(s))
Gender Height
0 M 173
1 F 192
2 M 186
3 F 167
new_row F 188
3.1.2 利用表格添加多行
df_temp = pd.DataFrame({'Gender':['F','M'], 'Height':[188,176]}, \
index = ['new_1', 'new_2'])
print(df_append.append(df_temp))
Gender Height
0 M 173
1 F 192
2 M 186
3 F 167
new_1 F 188
new_2 M 176
3.2 assign()方法
3.2.1 利用序列添加单列
s = pd.Series(list('abcd'), index = range(4))
print(df_append.assign(Letter = s)) # 列名由参数指定
Gender Height Letter
0 M 173 a
1 F 192 b
2 M 186 c
3 F 167 d
3.2.2 添加多列
print(df_append.assign(col1 = lambda x: x['Gender'] * 2, \
col2 = s))
Gender Height col1 col2
0 M 173 MM a
1 F 192 FF b
2 M 186 MM c
3 F 167 FF d
4. combine()方法和update()方法
4.1 combine()方法
combine()方法是按照表的顺序轮流进行逐列循环的,而且自动索引对齐,缺失值为NaN(默认状态下,后面的表没有的行列都会设置为NaN)。
df_combine_1 = df.loc[:1, ['Gender', 'Height']].copy()
df_combine_2 = df.loc[10:11, ['Gender', 'Height']].copy()
print(df_combine_1)
print(df_combine_2)
df_combine_1 = df_combine_1.combine(df_combine_2, lambda x, y: x)
print(df_combine_1)
Gender Height
0 M 173
1 F 192
Gender Height
10 M 161
11 F 175
Gender Height
0 M 173.0
1 F 192.0
10 NaN NaN
11 NaN NaN
df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})
df2 = pd.DataFrame({'A': [8, 7], 'B': [6, 5]})
print(df1.combine(df2, lambda x, y: x if x.mean() > y.mean() else y))
A B
0 8 6
1 7 5
df2 = pd.DataFrame({'B': [8, 7], 'C': [6, 5]}, index = [1, 2])
# NaN既不大于具体的数也不小于具体的数,因此NaN与数字比较大小时一律返回False
print(df1.combine(df2, lambda x, y: x if x.mean() > y.mean() else y))
A B C
0 NaN NaN NaN
1 NaN 8.0 6.0
2 NaN 7.0 5.0
print(df1.combine(df2,lambda x, y: x if x.mean() > y.mean() else y, \
overwrite = False))
A B C
0 1.0 NaN NaN
1 2.0 8.0 6.0
2 NaN 7.0 5.0
print(df1.combine(df2, lambda x, y: x if x.mean() > y.mean() else y, \
fill_value = -100))
A B C
0 1.0 -100.0 -100.0
1 2.0 8.0 6.0
2 -100.0 7.0 5.0
4.2 combine_first()方法
这个方法的作用是用其他表格的数据填补当前表格的缺失值。虽然功能简单,但是很实用。
df1 = pd.DataFrame({'A': [None, 0], 'B': [None, 4]})
df2 = pd.DataFrame({'A': [1, 1], 'B': [3, 3]})
print(df1.combine_first(df2))
A B
0 1.0 3.0
1 0.0 4.0
df1 = pd.DataFrame({'A': [None, 0], 'B': [4, None]})
df2 = pd.DataFrame({'B': [3, 3], 'C': [1, 1]}, index = [1, 2])
print(df1.combine_first(df2))
A B C
0 NaN 4.0 NaN
1 0.0 3.0 1.0
2 NaN 3.0 1.0
4.3 update()方法
返回的框索引只会与被调用框的一致(默认使用左连接,下一节会介绍)。同时,第二个框中的NaN元素不会起作用。update()没有返回值,是直接在当前表格上操作的。
df1 = pd.DataFrame({'A': [1, 2, 3],
'B': [400, 500, 600]})
print(df1)
df2 = pd.DataFrame({'B': [4, 5, 6],
'C': [7, 8, 9]})
print(df2)
df1.update(df2)
print(df1)
A B
0 1 400
1 2 500
2 3 600
B C
0 4 7
1 5 8
2 6 9
A B
0 1 4
1 2 5
2 3 6
df1 = pd.DataFrame({'A': ['a', 'b', 'c'],
'B': ['x', 'y', 'z']})
print(df1)
df2 = pd.DataFrame({'B': ['d', 'e']}, index = [1,2])
print(df2)
df1.update(df2)
print(df1)
A B
0 a x
1 b y
2 c z
B
1 d
2 e
A B
0 a x
1 b d
2 c e
df1 = pd.DataFrame({'A': [1, 2, 3],
'B': [400, 500, 600]})
print(df1)
df2 = pd.DataFrame({'B': [4, np.nan, 6]})
print(df2)
df1.update(df2)
print(df1)
A B
0 1 400
1 2 500
2 3 600
B
0 4.0
1 NaN
2 6.0
A B
0 1 4.0
1 2 500.0
2 3 6.0
5. concat()函数
concat()函数可以在两个维度上拼接,默认纵向凭借(axis=0),拼接方式默认外连接。所谓外连接,就是取拼接方向的并集,而内连接时取拼接方向(若使用默认的纵向拼接,则为列的交集)的交集。
df1 = pd.DataFrame({'A': ['A0', 'A1'],
'B': ['B0', 'B1']},
index = [0,1])
df2 = pd.DataFrame({'A': ['A2', 'A3'],
'B': ['B2', 'B3']},
index = [2,3])
df3 = pd.DataFrame({'A': ['A1', 'A3'],
'D': ['D1', 'D3'],
'E': ['E1', 'E3']},
index = [1,3])
print(pd.concat([df1, df2]))
A B
0 A0 B0
1 A1 B1
2 A2 B2
3 A3 B3
print(pd.concat([df1, df2], axis = 1))
A B A B
0 A0 B0 NaN NaN
1 A1 B1 NaN NaN
2 NaN NaN A2 B2
3 NaN NaN A3 B3
print(pd.concat([df3,df1], join = 'inner'))
A
1 A1
3 A3
0 A0
1 A1
# sort设置按列名排序,默认为False
print(pd.concat([df3, df1], join = 'outer', sort = True))
A B D E
1 A1 NaN D1 E1
3 A3 NaN D3 E3
0 A0 B0 NaN NaN
1 A1 B1 NaN NaN
其中,verify_integrity参数可以用来检查是否存在索引重叠。如果设置为True表示检查重叠,此时当索引出现重叠时,程序会报错。
# pd.concat([df3, df1], verify_integrity = True, sort = True)
s = pd.Series(['X0', 'X1'], name = 'X')
print(pd.concat([df1, s], axis = 1))
A B X
0 A0 B0 X0
1 A1 B1 X1
# key参数用于对不同的数据框增加一个标号,便于索引
pd.concat([df1,df2], keys=['x', 'y'])
print(pd.concat([df1,df2], keys=['x', 'y']).index)
MultiIndex([('x', 0),
('x', 1),
('y', 2),
('y', 3)],
)
6. merge()函数与join()方法
6.1 merge()函数
merge()函数的作用是将两个pandas对象横向合并(无法纵向拼接),遇到重复的索引项时会使用笛卡尔积,默认inner连接,可选left、outer、right连接。它的how参数就是concat()的join参数。所谓左连接,就是指以第一个表索引为基准,右边的表中如果不再左边的则不加入,如果在左边的就以笛卡尔积的方式加入。merge/join与concat的不同之处在于on参数,可以指定某一个对象为key来进行连接。
left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'],
'key2': ['K0', 'K1', 'K0', 'K1'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
'key2': ['K0', 'K0', 'K0', 'K0'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']})
right2 = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
'key2': ['K0', 'K0', 'K0', 'K0'],
'C': ['C0', 'C1', 'C2', 'C3']})
print(pd.merge(left, right, on = 'key1'))
key1 key2_x A B key2_y C D
0 K0 K0 A0 B0 K0 C0 D0
1 K0 K1 A1 B1 K0 C0 D0
2 K1 K0 A2 B2 K0 C1 D1
3 K1 K0 A2 B2 K0 C2 D2
4 K2 K1 A3 B3 K0 C3 D3
print(pd.merge(left, right, on = ['key1', 'key2']))
key1 key2 A B C D
0 K0 K0 A0 B0 C0 D0
1 K1 K0 A2 B2 C1 D1
2 K1 K0 A2 B2 C2 D2
print(pd.merge(left, right, how = 'outer', on = ['key1','key2']))
key1 key2 A B C D
0 K0 K0 A0 B0 C0 D0
1 K0 K1 A1 B1 NaN NaN
2 K1 K0 A2 B2 C1 D1
3 K1 K0 A2 B2 C2 D2
4 K2 K1 A3 B3 NaN NaN
5 K2 K0 NaN NaN C3 D3
print(pd.merge(left, right, how = 'left', on = ['key1', 'key2']))
key1 key2 A B C D
0 K0 K0 A0 B0 C0 D0
1 K0 K1 A1 B1 NaN NaN
2 K1 K0 A2 B2 C1 D1
3 K1 K0 A2 B2 C2 D2
4 K2 K1 A3 B3 NaN NaN
print(pd.merge(left, right, how = 'right', on = ['key1', 'key2']))
key1 key2 A B C D
0 K0 K0 A0 B0 C0 D0
1 K1 K0 A2 B2 C1 D1
2 K1 K0 A2 B2 C2 D2
3 K2 K0 NaN NaN C3 D3
left = pd.DataFrame({'A': [1, 2], 'B': [2, 2]})
right = pd.DataFrame({'A': [4, 5, 6], 'B': [2, 2, 2]})
print(pd.merge(left, right, on = 'B', how = 'outer'))
A_x B A_y
0 1 2 4
1 1 2 5
2 1 2 6
3 2 2 4
4 2 2 5
5 2 2 6
# validate检验的是到底哪一边出现了重复索引
# 如果是“one_to_one”则两侧索引都是唯一,如果"one_to_many"则左侧唯一
left = pd.DataFrame({'A': [1, 2], 'B': [2, 2]})
right = pd.DataFrame({'A': [4, 5, 6], 'B': [2, 3, 4]})
# pd.merge(left, right, on='B', how='outer',validate='one_to_one') #报错
left = pd.DataFrame({'A': [1, 2], 'B': [2, 1]})
print(pd.merge(left, right, on = 'B', how = 'outer', validate = 'one_to_one'))
A_x B A_y
0 1.0 2 4.0
1 2.0 1 NaN
2 NaN 3 5.0
3 NaN 4 6.0
# indicator参数指示了,合并后该行索引的来源
df1 = pd.DataFrame({'col1': [0, 1], 'col_left': ['a', 'b']})
df2 = pd.DataFrame({'col1': [1, 2, 2], 'col_right': [2, 2, 2]})
# indicator='indicator_column'也是可以的
print(pd.merge(df1, df2, on = 'col1', how = 'outer', indicator = True))
col1 col_left col_right _merge
0 0 a NaN left_only
1 1 b 2.0 both
2 2 NaN 2.0 right_only
3 2 NaN 2.0 right_only
6.2 join()方法
join()方法作用是将多个pandas对象横向拼接,遇到重复的索引项时会使用笛卡尔积,默认左连接,可选inner、outer和right连接。
left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
'B': ['B0', 'B1', 'B2']},
index = ['K0', 'K1', 'K2'])
right = pd.DataFrame({'C': ['C0', 'C2', 'C3'],
'D': ['D0', 'D2', 'D3']},
index = ['K0', 'K2', 'K3'])
print(left.join(right))
A B C D
K0 A0 B0 C0 D0
K1 A1 B1 NaN NaN
K2 A2 B2 C2 D2
# 对于many_to_one模式下的合并,往往join更为方便
# 同样可以指定key
left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3'],
'key': ['K0', 'K1', 'K0', 'K1']})
right = pd.DataFrame({'C': ['C0', 'C1'],
'D': ['D0', 'D1']},
index = ['K0', 'K1'])
print(left.join(right, on = 'key'))
A B key C D
0 A0 B0 K0 C0 D0
1 A1 B1 K1 C1 D1
2 A2 B2 K0 C0 D0
3 A3 B3 K1 C1 D1
# 多层key
left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3'],
'key1': ['K0', 'K0', 'K1', 'K2'],
'key2': ['K0', 'K1', 'K0', 'K1']})
index = pd.MultiIndex.from_tuples([('K0', 'K0'), ('K1', 'K0'),
('K2', 'K0'), ('K2', 'K1')],
names = ['key1','key2'])
right = pd.DataFrame({'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']},
index = index)
print(left.join(right, on = ['key1','key2']))
A B key1 key2 C D
0 A0 B0 K0 K0 C0 D0
1 A1 B1 K0 K1 NaN NaN
2 A2 B2 K1 K0 C1 D1
3 A3 B3 K2 K1 C3 D3
7. 问题与练习
7.1 问题
【问题一】 请思考什么是append/assign/combine/update/concat/merge/join各自最适合使用的场景,并举出相应的例子
append():可以利用Series或DataFrame添加行
assign():添加列
combine():利用规则在表中填充另一个表的数据
update():按索引来覆盖更新数据
concat():行/列两个方向拼接两个表
merge()和join():横向拼接(笛卡尔积方式),适用于多级索引的情况
【问题二】 merge_ordered和merge_asof的作用是什么?和merge是什么关系
merge_ordered():拼接有序数据,例如时间序列数据
merge_asof():类似于左连接拼接,但匹配的是最近的键而不是相等的键
【问题三】 请构造一个多级索引与多级索引合并的例子,尝试使用不同的合并函数。
略。
【问题四】 上文提到了连接的笛卡尔积,那么当连接方式变化时(inner/outer/left/right),这种笛卡尔积规则会相应变化吗?请构造相应例子。
不会的。
7.2 练习
略。