Pandas 支持 equi-join,其中 join 中涉及的键被认为是相等的。这是通过 merge 和 join 函数实现的。但是,在某些情况下,所涉及的Key可能不相等;联接中还涉及一些其他逻辑条件、这称为非等式连接或不等式连接或者条件连接。
这种情况下使用pandas来的话得可以从以下2个方面着手。
1、先建立笛卡尔连接然后筛选。缺点是性能很差。
2、对于时间序列可以使用asof来连接。缺点是功能受限,不是所有的条件筛选都能用asof,特别是非时间序列的情况下。
解决方案
1、使用DuckDB 的 SQL 查询 DataFrame,性能极高。
2、使用pandas的功能增强库pyjanitor 库的 conditional_join 函数,既节省内存又不损性能;‘
解决方案一:
由于DuckDB是一个比较火热的项目已经成为的Python数据库。考虑到性能和代码的维护主要推荐这种方式
映入DuckDB的python库,拼接字符串为SQL语句,然后查询。
解决方案二:
该方案的有优点是代码和pandas高度兼容,如果只做简单的不等连接可以使用,复杂的条件还需要DuckDB。
可以参考下面的文章。这篇文章的主要价值在于描述了条件筛选的主要用法,相比与使用pyjanitor 我更倾向于使用DuckDB库的SQL方法。
https://samukweku.github.io/data-wrangling-blog/notebooks/Fast-and-Efficient-Inequality-Joins-in-Pandas.html
为了避免外网站打不开我接了图。
对于使用到的pyjanitor 库,它是一个从R迁移来的python库,主要有以下3方面的功能
https://pyjanitor-devs.github.io/pyjanitor/api/io/
1、条件连接、不等式连接conditional_join。主要功能
2、对pandas读取csv和excel在功能上的增强,如一次读取多个sheet的excelAPI :xlsx_table(path, sheetname, table=None)
,读取多个csv等 API:read_csvs(files_path, separate_df=False, **kwargs)
。次要功能,这些功能在pandas也较为容易实现,没必要多引入库和API。
3、数据清洗。主要是对空值、列的增删的处理,和各个行业(如金融)等封装的清洗方法。个人认为用处不大,提供的功能多数pandas都能实现,还得多记API。
4、其他可能有用的功能
https://www.cnblogs.com/feffery/p/15998079.html
利用also()方法穿插执行任意函数#
pyjanitor中的also()方法允许在pandas链式过程中随意插入执行任意函数,接受上一步状态的数据框运算结果,且不影响对下一步处理逻辑的数据输入,我非常喜欢这个功能,下面是一个简单的例子:
df = (
# 构造示例数据框
pd.DataFrame({"a": [1, 2, 3], "b": list("abc")})
.query("a > 1")
# 利用also()插入lambda函数接受上一步的输入对象
.also(lambda df: print(f"a字段<=1的记录有{df.query('a <= 1').shape[0]}行"))
.rename(columns={'a': 'new_a'})
# 利用also()实现中间计算结果的导出
.also(lambda df: df.to_csv("temp.csv", index=False))
# 利用also()打印到这一步时数据框计算结果的字段名
.also(
lambda df: print(f"字段名:{df.columns.tolist()}")
)
.drop(columns='b')
)
2、利用case_when()方法实现多条件分支#
pyjanitor中的case_when()方法可以帮助我们针对数据框实现类似SQL中的的多条件分支运算,注意,因为是多条件分支,所以包含最后的“其他”条件在内,需要至少定义3条分支规则,参考下面的例子:
df = pd.DataFrame(
{
“a”: [0, 0, 1, 2],
“b”: [0, 3, 4, 5],
“c”: [6, 7, 8, 9],
}
)
df.case_when(
((df.a == 0) & (df.b == 0)), ‘类别1’,
((df.a == 0) & (df.b != 0)), ‘类别2’,
# 其他情况
‘类别3’,
column_name=“类别”,
)
3、利用move()方法快捷完成字段位置调整
pyjanitor中的move()方法用于快捷调整某行或某列数据的位置,通过source参数指定需要移动的数据行index或列的字段名,target参数用于指定移动的目标位置数据行index或列的字段名,position用于设置移动方式('before’表示移动到目标之前一个位置,after表示后一个位置),axis用于设定移动方式(0表示行移动,1表示列移动)。
以最常用的列移动为例:
df = pd.DataFrame({"a": [2, 4, 6],"b": [1, 3, 5],"c": [7, 8, 9]})