Pandas案例精进 | 结构化数据非等值范围查找 ③

大家好,我是小五🐶

欢迎来到👉「Pandas案例精进」专栏!前文回顾:

Pandas案例精进 | 结构化数据非等值范围查找 ①

Pandas案例精进 | 结构化数据非等值范围查找 ②

本文是承接前两篇的实战案例,没看过的小伙伴建议先点击👆上方链接查看前文

前两篇文章就已经解决了问题,考虑到上述区间查找其实是一个顺序查找的问题,所以我们可以使用二分查找进一步优化减少查找次数。

当然二分查找对于这种2位数级别的区间个数查找优化不明显,但是当区间增加到万级别,几十万的级别时,那个查找效率一下子就体现出来了,大概就是几万次查找和几次查找的区别。

字典查找+二分查找高效匹配

本次优化,主要通过字典查询大幅度加快了查询的效率,几乎实现了将非等值连接转换为等值连接。

首先读取数据:

1import pandas as pd 2 3product = pd.read_excel('sample.xlsx', sheet_name='A') 4cost = pd.read_excel('sample.xlsx', sheet_name='B') 5cost.head() 6

下面计划将价格表直接转换为能根据地区代码和索引快速查找价格的字典。

先取出区间范围列表,用于索引位置查找:

1price_range = cost.columns[2:].str.split("~").str[1].astype("float").tolist() 2price_range 3

结果:

1[0.5, 1.0, 2.0, 3.0, 4.0, 5.0, 7.0, 10.0, 15.0, 100000.0] 2

下面将测试二分查找的效果:

1import bisect 2import numpy as np 3 4for a in np.linspace(0.5, 5, 10): 5    idx = bisect.bisect_left(price_range, a) 6    print(a, idx) 7

结果:

10.5 0 21.0 1 31.5 2 42.0 2 52.5 3 63.0 3 73.5 4 84.0 4 94.5 5 105.0 5 11

可以打印索引列表方便对比:

1print(*enumerate(price_range)) 2

结果:

1(0, 0.5) (1, 1.0) (2, 2.0) (3, 3.0) (4, 4.0) (5, 5.0) (6, 7.0) (7, 10.0) (8, 15.0) (9, 100000.0) 2

经过对比可以看到,二分查找可以正确的找到一个指定的重量在重量区间的索引位置。

于是我们可以构建地区代码和索引位置作联合主键快速查找价格的字典:

1cost_dict = {} 2for area_id, area, *prices in cost.values: 3    for idx, price in enumerate(prices): 4        cost_dict[(area_id, idx)] = area, price 5

然后就可以批量查找对应的运费了:

1result = [] 2for product_id, area_id, weight in product.values: 3    idx = bisect.bisect_left(price_range, weight) 4    area, price = cost_dict[(area_id, idx)] 5    result.append((product_id, area_id, area, weight, price)) 6result = pd.DataFrame(result, columns=["产品ID", "地区代码", "地区缩写", "重量(kg)", "价格"]) 7result 8

字典查找+二分查找高效匹配的完整代码:

1import pandas as pd 2import bisect 3 4product = pd.read_excel('sample.xlsx', sheet_name='A') 5cost = pd.read_excel('sample.xlsx', sheet_name='B') 6price_range = cost.columns[2:].str.split("~").str[1].astype("float").tolist() 7cost_dict = {} 8for area_id, area, *prices in cost.values: 9    for idx, price in enumerate(prices): 10        cost_dict[(area_id, idx)] = area, price 11result = [] 12for product_id, area_id, weight in product.values: 13    idx = bisect.bisect_left(price_range, weight) 14    area, price = cost_dict[(area_id, idx)] 15    result.append((product_id, area_id, area, weight, price)) 16result = pd.DataFrame(result, columns=["产品ID", "地区代码", "地区缩写", "重量(kg)", "价格"]) 17result 18

两种算法的性能对比

可以看到即使如此小的数据量下依然存在几十倍的性能差异,将来更大的数量量时,性能差异会更大。

将非等值连接转换为等值连接

基于以上测试,我们可以将非等值连接转换为等值连接直接连接出结果,完整代码如下:

1import pandas as pd 2import bisect 3 4product = pd.read_excel('sample.xlsx', sheet_name='A') 5cost = pd.read_excel('sample.xlsx', sheet_name='B') 6price_range = cost.columns[2:].str.split("~").str[1].astype("float").tolist() 7cost.columns = ["地区代码", "地区缩写"]+list(range(cost.shape[1]-2)) 8cost = cost.melt(id_vars=["地区代码", "地区缩写"], 9                       var_name='idx', value_name='运费') 10product["idx"] = product["重量(kg)"].apply( 11    lambda weight: bisect.bisect_left(price_range, weight)) 12result = pd.merge(product, cost, on=['地区代码', 'idx'], how='left') 13result.drop(columns=["idx"], inplace=True) 14result 15

该方法的平均耗时为6ms:

欢迎你在下方评论区留言,发表你的看法,给大家分享和互动!

如果大家喜欢我的文章,请动动你的小手,点个赞吧~

本文转转自微信公众号凹凸数据原创[https://mp.weixin.qq.com/s/gMAKutucqkS04Eofp-Qjsg(https://mp.weixin.qq.com/s/gMAKutucqkS04Eofp-Qjsg),可扫描二维码进行关注: 如有侵权,请联系删除。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Python3:sqlalchemy对mysql数据库操作,非sql语句

Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''