1. 需求
将多个数据帧连接,这几个数据帧可能有不一样的长度,并且数据的排列顺序不固定。
2. 遇到的问题
- 因为数据的顺序和长度是不固定的,只能通过表连接方式进行合并,即
np.merge()
方法。 np.merge()
方法一次只能处理两个数据帧。- 如何处理同名的列。
3 实现
1. 生成模拟数据
import pandas as pd
import numpy as np
from typing import *
df1 = pd.DataFrame([["Alice", 23,2500],
["Bob", 26,3000],
["Carol",27,3000]]
, columns=["name","age","salary"])
df2=pd.DataFrame([["Alice",2000],
["Bob",3000],
["Dave",3500]],
columns=["name","salary"])
df3=pd.DataFrame([["Alice",2000],
["Carol",3000],
["Dave",3500]],
columns=["name","salary"])
df1,df2,df3
2.代码实现
from collections import Counter
from functools import reduce,partial
def merge_dataframes(*dataframes:List[pd.DataFrame],how:str="outer" , on: str|List[str] = ...,suffixes:List[str]=...):
#如果传入suffixes参数,判断是否和表格数量相等
if isinstance(suffixes,Iterable) and not isinstance(suffixes,str):
assert(len(dataframes)==len(suffixes))
#没有传入suffixes参数就直接合并
else:
#使用reduce方法将表格依次合并
return reduce( lambda A,B: pd.merge(A,B,how=how,on=on)
,dataframes)
# 下面的代码是根据用户定义的suffix修改重复的列名
# 将单个对象或者可迭代对象转化为列表
tolist=lambda x:list(x) if isinstance(x,(Tuple,List,pd.Index)) else [x]
#列出所有列名,并计算出现的频次
all_labels=[col for df in dataframes for col in df.columns]
label_counter= Counter(all_labels)
# 定义列名转换规则,如果一个列名出现多次,那么就在后面加上后缀
def label_map(label,suffix):
if label in tolist(on) or label_counter[label]==1:
return label
else:
return str(label)+str(suffix)
# 生成一系列临时的数据帧,改名方式利用了偏函数
temp_dataframes=[ df.rename(columns=partial(label_map,suffix=suffix)) for df,suffix in zip(dataframes,suffixes)]
#使用reduce方法将表格依次合并
return reduce( lambda A,B: pd.merge(A,B,how=how,on=on)
,temp_dataframes)
3. 测试:
merge_dataframes(df1,df2,df3,on="name",suffixes=["_1","_2","_3"])
输出:
name age salary_1 salary_2 salary_3
0 Alice 23.0 2500.0 2000.0 2000.0
1 Bob 26.0 3000.0 3000.0 NaN
2 Carol 27.0 3000.0 NaN 3000.0
3 Dave NaN NaN 3500.0 3500.0