select 行中的性能,其中条件是与集合的匹配百分比
Performance in select rows where condition is a percentage of match with a set
鉴于此示例:
df = pd.DataFrame({'col1':['id1','id2','id3'],
'col2':['name1','foobar','name3'],
'col3':[{'am', 'e1', 'me', 'na'},{'ar', 'ba', 'fo', 'ob', 'oo'},{'am', 'e3', 'me', 'na'}]})
col1 col2 col3
0 id1 name1 {na, e1, me, am}
1 id2 foobar {ar, fo, ba, oo, ob}
2 id3 name3 {na, e3, me, am}
目标是用满足两个集合交集的匹配阈值的所有行对 df
进行子集化。
我的解决方案:
def subset_by_intersection_threshold(set_1, set_2, threshold):
intersection = len(list(set_1.intersection(set_2)))
union = (len(set_1) + len(set_2)) - intersection
return float(intersection / union)>threshold
使用 jaccard 函数和 pandas apply
按阈值过滤所有匹配条件的行(本例中为匹配的 0.4)。
set_words=set(['na','me'])
df[df.col3.apply(lambda x: subset_by_intersection_threshold(set(x), set_words,0.4))]
因为我觉得这个解决方案有点蛮力模式,所以我打开这个问题是为了学习考虑执行时间的更有效的替代方案。
添加在我的个人笔记本电脑上执行的基准测试分数:
从慢到快:
%timeit df.col3.apply(lambda x: original(set(x), set_words, 0.4)) # 74 ms per loop
%timeit df.col3.apply(lambda x: jpp(x, set_words, 0.4)) # 32.3 ms per loop
%timeit list(map(lambda x: jpp(x, set_words, 0.4), df['col3'])) # 13.9 ms
%timeit [jpp(x, set_words, 0.4) for x in df['col3']] # 12.2 ms
通过避免不必要的 list
创建和 float
/ set
转换,您可以将性能提高约 2 倍。为了获得额外的提升,通过使用列表理解构建的布尔值列表进行索引。通常情况下,pd.Series.apply
可能不如列表理解中的常规循环。
def original(set_1, set_2, threshold):
intersection = len(list(set_1.intersection(set_2)))
union = (len(set_1) + len(set_2)) - intersection
return float(intersection / union)>threshold
def jpp(set_1, set_2, threshold):
intersection = len(set_1 & set_2)
union = (len(set_1) + len(set_2)) - intersection
return (intersection / union) > threshold
set_words = {'na', 'me'}
df = pd.concat([df]*10000)
%timeit df.col3.apply(lambda x: original(set(x), set_words, 0.4)) # 74 ms per loop
%timeit df.col3.apply(lambda x: jpp(x, set_words, 0.4)) # 32.3 ms per loop
%timeit [jpp(x, set_words, 0.4) for x in df['col3']] # 23.4 ms per loop
根据问题的结构以及是否要 运行 多次测试,您可以将数据重塑为布尔值,然后进行稍微向量化的 jaccard 计算:
# Create bool table
na me am e1 ar fo ob oo ba e3
0 True True True True False False False False False False
1 False False False False True True True True True False
2 True True True False False False False False False True
可能不可行(如果有太多不同的值)或太慢(设置需要很长时间),但这是它的代码:
df_bool = df.col3.apply(lambda x: pd.Series({s: True for s in x})).fillna(False)
# set_words as bool
sw = df_bool.columns.to_series().apply(lambda x: x in set_words).values
# intersection / union > 0.5
res = (df_bool & sw).sum(axis=1) / (df_bool | sw).sum(axis=1) > 0.4
# setup code (run once)
%timeit df.col3.apply(lambda x: pd.Series({s: True for s in x})).fillna(False) #
%timeit [jpp(x, set_words, 0.4) for x in df['col3']] # 14.4 ms per loop
%timeit (df_bool & sw).sum(axis=1) / (df_bool | sw).sum(axis=1) > 0.4 # 10.6 ms per loop
鉴于此示例:
df = pd.DataFrame({'col1':['id1','id2','id3'],
'col2':['name1','foobar','name3'],
'col3':[{'am', 'e1', 'me', 'na'},{'ar', 'ba', 'fo', 'ob', 'oo'},{'am', 'e3', 'me', 'na'}]})
col1 col2 col3
0 id1 name1 {na, e1, me, am}
1 id2 foobar {ar, fo, ba, oo, ob}
2 id3 name3 {na, e3, me, am}
目标是用满足两个集合交集的匹配阈值的所有行对 df
进行子集化。
我的解决方案:
def subset_by_intersection_threshold(set_1, set_2, threshold):
intersection = len(list(set_1.intersection(set_2)))
union = (len(set_1) + len(set_2)) - intersection
return float(intersection / union)>threshold
使用 jaccard 函数和 pandas apply
按阈值过滤所有匹配条件的行(本例中为匹配的 0.4)。
set_words=set(['na','me'])
df[df.col3.apply(lambda x: subset_by_intersection_threshold(set(x), set_words,0.4))]
因为我觉得这个解决方案有点蛮力模式,所以我打开这个问题是为了学习考虑执行时间的更有效的替代方案。
添加在我的个人笔记本电脑上执行的基准测试分数:
从慢到快:
%timeit df.col3.apply(lambda x: original(set(x), set_words, 0.4)) # 74 ms per loop
%timeit df.col3.apply(lambda x: jpp(x, set_words, 0.4)) # 32.3 ms per loop
%timeit list(map(lambda x: jpp(x, set_words, 0.4), df['col3'])) # 13.9 ms
%timeit [jpp(x, set_words, 0.4) for x in df['col3']] # 12.2 ms
通过避免不必要的 list
创建和 float
/ set
转换,您可以将性能提高约 2 倍。为了获得额外的提升,通过使用列表理解构建的布尔值列表进行索引。通常情况下,pd.Series.apply
可能不如列表理解中的常规循环。
def original(set_1, set_2, threshold):
intersection = len(list(set_1.intersection(set_2)))
union = (len(set_1) + len(set_2)) - intersection
return float(intersection / union)>threshold
def jpp(set_1, set_2, threshold):
intersection = len(set_1 & set_2)
union = (len(set_1) + len(set_2)) - intersection
return (intersection / union) > threshold
set_words = {'na', 'me'}
df = pd.concat([df]*10000)
%timeit df.col3.apply(lambda x: original(set(x), set_words, 0.4)) # 74 ms per loop
%timeit df.col3.apply(lambda x: jpp(x, set_words, 0.4)) # 32.3 ms per loop
%timeit [jpp(x, set_words, 0.4) for x in df['col3']] # 23.4 ms per loop
根据问题的结构以及是否要 运行 多次测试,您可以将数据重塑为布尔值,然后进行稍微向量化的 jaccard 计算:
# Create bool table
na me am e1 ar fo ob oo ba e3
0 True True True True False False False False False False
1 False False False False True True True True True False
2 True True True False False False False False False True
可能不可行(如果有太多不同的值)或太慢(设置需要很长时间),但这是它的代码:
df_bool = df.col3.apply(lambda x: pd.Series({s: True for s in x})).fillna(False)
# set_words as bool
sw = df_bool.columns.to_series().apply(lambda x: x in set_words).values
# intersection / union > 0.5
res = (df_bool & sw).sum(axis=1) / (df_bool | sw).sum(axis=1) > 0.4
# setup code (run once)
%timeit df.col3.apply(lambda x: pd.Series({s: True for s in x})).fillna(False) #
%timeit [jpp(x, set_words, 0.4) for x in df['col3']] # 14.4 ms per loop
%timeit (df_bool & sw).sum(axis=1) / (df_bool | sw).sum(axis=1) > 0.4 # 10.6 ms per loop