目录
1. 关系型连接
1.1 连接基本概念
将两张相关的表按照某一个或某一组键连接起来,关键要素是键和连接形式,其中连接形式包含:
- 左连接
left:以左边的键为准,若右表中的键 出现于左表,则将改键添加到左表。否则处理为缺失值。 - 右连接
right:类似处理 - 内连接
inner:合并两边同时出现的键 - 外连接
out:又叫全连接,在内连接基础上也包含仅在单侧表中出现的键。
1.2 值连接
根据某一列连接

连接不具备相同列名的表

连接两个出现相同列名的表

指定多个列作为on参数

validate 参数

练一练
上面以多列为键的例子中,错误写法显然是一种多对多连接,而正确写法是一对一连接,请修改原表,使得以多列为键的正确写法能够通过 validate=‘1:m’ 的检验,但不能通过 validate=‘m:1’ 的检验。
df2 = pd.DataFrame({'Name':['San Zhang', 'San Zhang'],'Gender':['F', 'M'],'Class':['two', 'two']})
df1.merge(df2, on=['Name', 'Class'], how='left',validate="1:m")
>>>
Name Age Class Gender
0 San Zhang 20 one NaN
1 San Zhang 21 two F
2 San Zhang 21 two M
df1.merge(df2, on=['Name', 'Class'], how='left',validate="m:1")
>>> MergeError: Merge keys are not unique in right dataset; not a many-to-one merge
1.3 索引连接
要实现类似merge的 多列为键 的操作,join需要使用 多级索引 。
df1 = pd.DataFrame({'Age':[20,21]},index=pd.MultiIndex.from_arrays([['San Zhang', 'San Zhang'],['one', 'two']],names=('Name','Class')))
df1
df2 = pd.DataFrame({'Gender':['F', 'M']},index=pd.MultiIndex.from_arrays([['San Zhang', 'San Zhang'],['two', 'one']],names=('Name','Class')))
df2
df1.join(df2)

2. 方向连接
2.1 concat
axis拼接方向,0-纵向拼接多表(常用于多样本拼接),1-横向拼接多表(常用于多字段/特征拼接)join连接形式,默认状态下join=outer表示保留所有的列,将不存在的值设为缺失,join=inner表示 两个表都出现过的列key在新表中指示来自于哪一张旧表的名字
# 纵向拼接:根据列索引对齐
df1 = pd.DataFrame({'Name':['San Zhang','Si Li'],'Age':[20,30]})
df1
Name Age
0 San Zhang 20
1 Si Li 30
======
df2 = pd.DataFrame({'Name':['Wu Wang'], 'Age':[40],'Gender':['F']})
df2
Name Age Gender
0 Wu Wang 40 F
======
pd.concat([df1, df2])
Name Age Gender
0 San Zhang 20 NaN
1 Si Li 30 NaN
0 Wu Wang 40 F
# 横向拼接:根据行索引对齐
df2 = pd.DataFrame({'Grade':[80, 90]}, index=[1, 2])
pd.concat([df1, df2], 1)
>>>
Name Age Grade
0 San Zhang 20.0 NaN
1 Si Li 30.0 80.0
2 NaN NaN 90.0
当使用多表直接方向合并时,尤其是横向合并,先用reset_index方法恢复默认整数索引再进行合并,防止出现由索引误对齐和重复索引的笛卡尔积带来的错误结果。
# keys参数 能够表示新表中的数据来自于哪个原表
df1 = pd.DataFrame({'Name':['San Zhang','Si Li'],'Age':[20,21]})
df1
Name Age
0 San Zhang 20
1 Si Li 21
======
df2 = pd.DataFrame({'Name':['Wu Wang'],'Age':[21]})
df2
Name Age
0 Wu Wang 21
======
pd.concat([df1, df2], keys=['one', 'two'])

2.2 序列与表的合并
append将序列追加到行末assign将序列追加到列末
s = pd.Series(['Wu Wang', 21], index = df1.columns)
df1.append(s, ignore_index=True)

s = pd.Series([80, 90])
df1.assign(Grade=s)

3. 类连接操作
3.1 比较

- 两表相同的值会被填充为
NaN other指代传入的参数表self指代被调用的表自身
3.2 组合
# 选出对应索引位置较小的元素
def choose_min(s1, s2):
s2 = s2.reindex_like(s1)
res = s1.where(s1<s2, s2) # 注意where逻辑,若df1<df_2为真,则保持原来的值;否则替换为新值
res = res.mask(s1.isna())
return res
df1 = pd.DataFrame({'A':[1,2], 'B':[3,4], 'C':[5,6]})
df2 = pd.DataFrame({'B':[5,6], 'C':[7,8], 'D':[9,10]}, index=[1,2])
df1.combine(df2, choose_min)
>>>
A B C D
0 NaN NaN NaN NaN
1 NaN 4.0 6.0 NaN
2 NaN NaN NaN NaN
练一练
请在上述代码的基础上修改,保留 df2 中4个未被 df1 替换的相应位置原始值。
def choose_min(s1, s2):
s2 = s2.reindex_like(s1)
res = s1.where(s1<s2, s2) # 注意where逻辑,若df1<df_2为真,则保持原来的值;否则替换为新值
# res = res.mask(s1.isna())
return res
# overwrite=False 可以保留 被调用表 中未出现在传入的参数表中的列
df1.combine(df2, choose_min, overwrite=False)
>>>
A B C D
0 1.0 NaN NaN NaN
1 2.0 4.0 6.0 NaN
2 NaN NaN NaN NaN
练一练
除了 combine 之外, pandas 中还有一个 combine_first 方法,其功能是在对两张表组合时,若第二张表中的值在第一张表中对应索引位置的值不是缺失状态,那么就使用第一张表的值填充。下面给出一个例子,请用 combine 函数完成相同的功能。
df1.combine(df2, lambda x,y : x.mask(x.isna(), y))
Ex1:美国疫情数据集

我的思路
# zfill() 方法返回指定长度的字符串,原字符串右对齐,前面填充0。
date = date.dt.month.astype('string').str.zfill(2) + '-' + date.dt.day.astype('string').str.zfill(2) + '-' + '2020'
date = date.tolist()
result = pd.DataFrame(columns=['Confirmed', 'Deaths', 'Recovered', 'Active'])
for date_item in date:
data = pd.read_csv('./data/us_report/%s.csv'%(date_item))
data = data.set_index('Province_State')
res = data.loc['New York',['Confirmed', 'Deaths', 'Recovered', 'Active']]
result = result.append(res,ignore_index=True)
result.index = date

哈哈哈哈哈哈哈哈居然和答案写得基本一样
# 答案中的for循环部分
L.append(data.to_frame().T)
# 拼接使用的是 concat,两个表之间的方向连接
res = pd.concat(L)
Ex2:实现join函数

# 想了一下下,然后直接看了答案
def join(df1, df2, how='left'):
res_col = df1.columns.tolist() + df2.columns.tolist()
dup = df1.index.unique().intersection(df2.index.unique()) # 取交集
res_df = pd.DataFrame(columns = res_col) # 定义结果的df
for label in dup:
cartesian = [list(i)+list(j) for i in df1.loc[label].values for j in df2.loc[label].values] # 两表查询结果拼接
dup_df = pd.DataFrame(cartesian, index = [label]*len(cartesian), columns = res_col) # 放入df中
res_df = pd.concat([res_df,dup_df]) # 两个df拼接,公共部分的拼接结果
# 左连接
if how in ['left', 'outer']:
for label in df1.index.unique().difference(dup): # 取差集,据df1与公共部分的差集作为查询依据
if isinstance(df1.loc[label], pd.DataFrame): # 如果是df
cat = [list(i)+[np.nan]*df2.shape[1] for i in df1.loc[label].values]
else: cat = [list(i)+[np.nan]*df2.shape[1] for i in df1.loc[label].to_frame().values]
dup_df = pd.DataFrame(cat, index = [label]*len(cat), columns = res_col)
res_df = pd.concat([res_df,dup_df])
# 右连接
if how in ['right', 'outer']:
for label in df2.index.unique().difference(dup):
if isinstance(df2.loc[label], pd.DataFrame):
cat = [[np.nan]+list(i)*df1.shape[1] for i in df2.loc[label].values]
else: cat = [[np.nan]+list(i)*df1.shape[1] for i in df2.loc[label].to_frame().values]
dup_df = pd.DataFrame(cat, index = [label]*len(cat), columns = res_col)
res_df = pd.concat([res_df,dup_df])
return res_df

本文围绕数据连接展开,介绍了关系型连接,包括基本概念、值连接和索引连接;方向连接,如concat拼接及序列与表的合并;类连接操作,有比较和组合。还给出美国疫情数据集和实现join函数两个实例,帮助理解数据连接操作。
235

被折叠的 条评论
为什么被折叠?



