我正在创建一个变量,用于测量3个不同字典中给定键的值的子样本的大小。例如,我想要对应于字典dict_a中的键A1,字典dict_b中的键b2和字典dict_c中的键c5的值集(即对应于3个字典中给定键的值集的交集)。
我已经编写了一个使用循环的代码,如下所示:
import numpy as np
dict_a = {'a1':[1,3,4], 'a2':[1,5,6,7,8,9,13]}
dict_b = {'b1':[85,7,25], 'b2':[1,8,10,70], 'b3':[1,5,69,13], 'b4':[1,75,15,30]}
dict_c = {'c1':[1,3,4], 'c2':[725,58,2,89], 'c3':[5,684,6,8,2], 'c4':[4,8,88,55,75,2,8], 'c5':[8,5,6,28,24,6], 'c6':[8,52,3,58,26,2]}
keys_a = list(dict_a.keys())
keys_b = list(dict_b.keys())
keys_c = list(dict_c.keys())
a= []
b= []
c= []
size = []
for y in keys_a:
for u in keys_b:
for w in keys_c:
a.append(u)
b.append(w)
c.append(y)
# Define subsample
subsample = np.intersect1d(dict_a[y],dict_b[u],dict_c[w])
size.append(len(subsample))
问题在于我的词典比示例中的词典大得多,并且运行时间很长。
有没有办法使它更有效?
答案 0 :(得分:0)
如何使用集合?
size = []
for y in keys_a:
for u in keys_b:
for w in keys_c:
common = set.intersection(set(dict_a[y]),
set(dict_b[u]),
set(dict_c[w]))
size.append(len(common))
计算集合的相交也比将数字列表首先转换为数组然后再使用np.intersection快得多。
您可以对列表中的任何可哈希类型使用此方法。
答案 1 :(得分:0)
我将把它分成几部分。首先生成a
,b
和c
列表,然后使用numpy生成主要的size
列表,最后对Python列表执行相同的操作。
获取密钥列表
因此,如果我们看一下,那么c
实际上是dict_a
中的键的列表,依此类推。我将假设这样做是有目的的,但是如果不是故意的,则将y
替换为key_a
,您会明白我的意思。
我们可以在不进入主循环的情况下轻松地预先计算出该值。每个项目仅由其他两个列表中的键数乘积来重复。我们可以用类似的方法做到这一点:
from itertools import repeat, chain
def key_summary(dict_1, dict_2, dict_3):
count = len(dict_2) * len(dict_3)
return chain(*(repeat(k, count) for k in dict_1.keys()))
a = list(key_summary(dict_b, dict_a, dict_c))
b = list(key_summary(dict_c, dict_a, dict_b))
c = list(key_summary(dict_a, dict_b, dict_c))
这应该更快,因为它没有深深地嵌套在循环中,但是鉴于计算起来很容易,我认为您可能想考虑为什么需要这样做。您可以在没有实际列出清单的情况下实现目标吗?
获取尺寸列表
我认为您没有正确使用intersect1d()
函数。 The docs指出第三个参数是assume_unique
,这不是我认为您要尝试的操作。我假设您希望出现在所有列表中的元素的一种实现方法是
np.intersect1d(np.intersect1d(val_a, val_b), val_c))
这建议一种优化循环的方法。代替在每个循环中计算val_a
和val_b
的交集,我们可以只执行一次并重新使用它。
for val_a in dict_a.values():
for val_b in dict_b.values():
# Get the intersection of a and b first
cache = np.intersect1d(val_a, val_b)
if not len(cache):
# Our first two sets have nothing in common, we know that we are
# just going to add a bunch of zeros for everything in dict_c
size.extend(repeat(0, len(dict_c)))
else:
size.extend(
len(np.intersect1d(cache, val_c)) for val_c in dict_c.values())
这也使我们可以应用另一种优化方法,即如果dict_c
和val_a
的交集不包含任何内容,则完全跳过对val_b
的循环。如果val_a
为空,我们也可以做类似的事情。
作为最后的优化,您应该始终使dict_a
最小,dict_c
最大,因为这给了我们最好的跳过步骤的机会。
进行上述操作后,我的速度提高了约200%(在给出的示例中为1.493ms-> 0.8ms)。
获取尺寸列表(使用Python集)
我假设您有充分的理由使用numpy函数,但是如果它们不是必需的,则可以将列表转换为集合,这些列表非常快速地在Python中执行交集。我们可以采用与上述非常相似的方法:
dset_a = {k: set(v) for k, v in dict_a.items()}
dset_b = {k: set(v) for k, v in dict_b.items()}
dset_c = {k: set(v) for k, v in dict_c.items()}
size = []
for val_a in dset_a.values():
for val_b in dset_b.values():
cache = val_a & val_b
if not cache:
size.extend(repeat(0, len(dict_c)))
else:
size.extend(len(cache & val_c) for val_c in dset_c.values())
在给定的示例上,这快得多。原来的时间为0.019毫秒,而原始时间为1.493毫秒(快约80倍!)。