概述
也是一个不知道怎么描述的需求。
简而言之,就是一个多值的列,匹配另一个多值的列表,求是否存在公共元素。
问题定义
有dataframe,名为df,含id,status,type,3个字段。
其中status为多值字段,多值以','隔开。
id | status | type |
1 | 5,9 | 1 |
2 | 2,3 | 1 |
3 | 4,6 | 2 |
现有查询指令values = '2,3,6',类型str。
要求,只要status里至少有1个元素,存在于values中, 则返回对应id。
本质是逐行求交集。
由于status列是多值的,所以 df['status'].isin(values)这个方法就被砍掉了。
下面是我的思路。
方法一 自定义my_isin()函数
一开始在my_isin里面尝试了双重循环,low炸了。
改用集合运算之后基本达到了网上能找到的最优解。
def my_isin(df_value,values):
p = set(str(df_value).split(','))
q = set(values.split(','))
if p & q:
return True
return False
value = '2,3,8,10'
idx = df['status'].map( lambda x: my_isin(x,values))
tmp_df = df[idx]
target_id = tmp_df['id'].tolist()
数据源10万行记录,大概0.13秒±跑完一次查询,返回一组id。
数据源100万行记录,大约在1~2秒内跑完一次查询,返回一组id。
如果小数据样本,这样就足够了。
而现在我有13万条查询请求,要在100+万的用户表里查数据,每次1~2秒。
查询的同时,还需要对结果做一些其他操作。
平均下来,一次查询需要跑3秒,13万条总共需要108小时=4.5天,完全不能接受!!!
于是开始寻找其他解法。
方法二 空间换时间
我们先研究一下,在某列里查1个值的时间
ts =time.time()
status = df['status']
idx = status.map(lambda x : True if '5' in x.split(',') else False)
res = df[idx]['id']
print(time.time()-ts)
#0.43140435218811035
可以看到非常小。
而我们容易知道,status总共只有0~18,十九种取值。
ts = time.time()
status = df['status']
s_status = set()
for x in status:
for y in x.split(','):
s_status.add(y)
print(time.time()-ts)
print(len(s_status))
#0.4s
#19
那么就有了思路,我们可以构造user_id的字典
status = df['status']
status_id_dict={}
for st_i in s_status:
idx = status.map(lambda x : True if st_i in x.split(',') else False)
status_id_dict[st_i] = set(df[idx]['id'])
这样构造的dict,就可以输入第i种status取值,获得对应的id集合。
构造字典的时间消耗,每次查询0.43秒,19种取值就是19次查询,
总共只需要8秒多。
从此以后查询的时候
#原来我们是这样写的
values = '2,3,8,10'
idx = df['status'].map( lambda x: my_isin(x,values))
tmp_df = df[idx]
res = set(tmp_df['id'])
#现在利用字典,不再需要做匹配,只需要做交集算法
ts=time.time()
values = '2,3,8,10'
res = set()
for x in values.split(','):
#为空时赋值
if not res:
res = status_id_dict[x]
else:
#不为空时取并集
res = res | status_id_dict[x]
print(len(res))
print(time.time()-ts)
#53182
#0.00019979476928710938
任务流从【对n行匹配k个value值,对o(n)的index序列做k次交集,用index取表,用表取序列】
变成了 【取出k个id集合,做k次id并集】,注意这里是并集。
计时:
values ='2,3' , time = 0.000835418701171875
values = '2,7,11' , time = 0.0017690658569335938
values ='2,5,8,10' , time = 0.0006830692291259766
跑一次运算的耗时取决于,要查的value个数,和要做交集的set大小。
同样在100万行的用户表里查询,数量级已经从原来的1~2秒,降低到了1毫秒左右。
构造字典+跑完13万条记录,总耗时3分钟。
只是这种做法比较消耗空间。
而且不适用于取值很多的字段,比如'area',有全国几万个县市的标签,有几万种取值。
跑起来太耗内存了。
最后
以上就是舒服纸飞机为你收集整理的pandas 两个多值序列匹配的全部内容,希望文章能够帮你解决pandas 两个多值序列匹配所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复