pandas 两个多值序列匹配

也是一个不知道怎么描述的需求。

简而言之,就是一个多值的列,匹配另一个多值的列表,求是否存在公共元素。

 

问题定义

有dataframe,名为df,含id,status,type,3个字段。

其中status为多值字段,多值以','隔开。

idstatustype
15,91
22,31
34,62

 

现有查询指令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',有全国几万个县市的标签,有几万种取值。

跑起来太耗内存了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值