我是一名缺乏经验的程序员,正在使用Python进行大量的生物信息学练习。
一个问题区域计算名称组之间的集合交集中的元素,以及计算在字典中的存储。每个列表有两个2000个名称组;名称组中的名称是物种的拉丁名称。例如:
list__of_name_groups_1 = [
['Canis Lupus', 'Canis Latrans'],
['Euarctos Americanus', 'Lynx Rufus'],
...
]
list__of_name_groups_2 = [
['Nasua Narica', 'Odocoileus Hemionus'],
['Felis Concolor', 'Peromyscus Eremicus'],
['Canis Latrans', 'Cervus Canadensis']
...
]
我需要一个字典,其中包含名称组之间的所有交叉点大小,例如
>>> intersections
{ (0, 0): 0, (0, 1): 0, (0, 2): 1, (1, 0): 0, (1, 1): 0, (2, 1): 0,
(2, 0): 1, (2, 1): 0, (2, 2): 0 }
('Canis Latrans'
出现在第一个列表中的元素0
中,第二个列表中的元素2
。)
我有一个可行的算法实现,但运行速度太慢。
overlap = {}
for i in list_of_lists_of_names_1:
for j in list_of_lists_of_names_2:
overlap[(i,j)] = len(set(i) & set(j))
是否有更快的方法来计算集合交叉点中的元素数量?
(你好主持人......尼克,这个修改过的帖子实际上问的问题与我正在处理的问题略有不同。虽然你的答案对于解决这个问题非常好,但我担心这个方法你建议实际上对我正在尝试做的事情没有用。我非常感谢你在答案中投入的时间和精力,以及编辑这篇文章,但我会要求将这篇文章还原为原文。) / p>
答案 0 :(得分:1)
首先, Python set
擅长查找交叉点(它们使用散列),但您的代码反复构造相同的set
。例如。如果两个list
每个包含2000个元素[你的意思是外部或内部list
是那么长吗?],只有4000个不同的set
要计算,但你的代码计算2000 x 2000 x 2 = 800万set
s。
所以一次计算那4000套:
list_of_name_tuples_1 = [("a", "aa"), ("b", "bbb"), ("c", "cc", "ccc")]
list_of_name_tuples_2 = [("a", "AA"), ("b", "BBB"), ("c", "cc", "CCC")]
name_sets_1 = [set(i) for i in list_of_name_tuples_1]
name_sets_2 = [set(i) for i in list_of_name_tuples_2]
overlap = {}
for l1, s1 in zip(list_of_name_tuples_1, name_sets_1):
for l2, s2 in zip(list_of_name_tuples_2, name_sets_2):
overlap[(l1, l2)] = len(s1 & s2)
Python list
是不可用的,因此它们不能用于dict
键,因此我将名单列表更改为元组列表 - 名。
(此代码假设您正在使用Python 3,其中zip()
返回迭代器。如果您使用的是Python 2,则调用itertools.izip()
以获取配对元素的迭代器。)< / p>
其次,考虑将overlap
重组为dict
dict
,而不是由元组编制索引的dict
。
list_of_name_tuples_1 = [("a", "aa"), ("b", "bbb"), ("c", "cc", "ccc")]
list_of_name_tuples_2 = [("a", "AA"), ("b", "BBB"), ("c", "cc", "CCC")]
name_sets_1 = [set(i) for i in list_of_name_tuples_1]
name_sets_2 = [set(i) for i in list_of_name_tuples_2]
overlap = {}
for l1, s1 in zip(list_of_name_tuples_1, name_sets_1):
d = overlap.setdefault(l1, {})
for l2, s2 in zip(list_of_name_tuples_2, name_sets_2):
d[l2] = len(s1 & s2)
这可以在后续代码中节省大量工作,后者代码将通过overlap[l1][l2]
而不是overlap[(l1, l2)]
访问它(没有元组构造或哈希生成),并且嵌套循环可以获取{{1然后在外部循环中访问内部循环中的d = overlap[l1]
。
答案 1 :(得分:0)
根据您的数据的具体情况,替代选项是,对于每个可能的数据项,您记录它所包含的列表。
使用这样的数据结构,您可以为每个数据项快速确定哪些列表包含它,并增加overlap
的相应条目。
答案 2 :(得分:0)
实际上你可以用一个长整数表示每个列表。例如,用第一个元素,第二个元素设置,但是没有第三个元素可以表示为(0 <&lt;&lt; 3)+(1 << 2 )+(1 << 1)= 6。
那么你可以通过计算整数&amp;来计算集合交集。操作
答案 3 :(得分:0)
这取决于您的数据的性质,但如果两个超级列表共有相对较少的拉丁名称,则可能会给您带来很大的节省。方法是:
0
。由于您执行的设置操作数,您自己的解决方案很慢:2,000 x 2,000 == 4,000,000。无论Python如何快速执行每一项,都需要时间。我的方法减少了因子1000计算的集合交叉点的数量,但代价是其他一些较小的计算。
我的封套计算是,如果普通名称相对较少,您可以将性能提高4倍或更高。这种改进将越少,通用名称就越少。
我在这里使用了一些可能对您不熟悉的内容:列表推导和enumerate()
,defaultdict
,使用in
列出成员资格和itertools方法。那让你陷入了困境。快乐的研究,如果你想要一些解释,请告诉我。
from collections import defaultdict
import itertools
list_of_name_groups_1 = [
['Canis Lupus', 'Canis Latrans'],
['Euarctos Americanus', 'Lynx Rufus'],
]
list_of_name_groups_2 = [
['Nasua Narica', 'Odocoileus Hemionus'],
['Felis Concolor', 'Peromyscus Eremicus'],
['Canis Latrans', 'Cervus Canadensis']
]
def flatten(list_of_lists):
return itertools.chain.from_iterable(list_of_lists)
def unique_names(list_of_name_groups):
return set(flatten(list_of_name_groups))
def get_matching_name_groups(name, list_of_name_groups):
return (list_index for list_index, name_group
in enumerate(list_of_name_groups)
if name in name_group)
list1_candidates = set()
list2_candidates = set()
common_names = unique_names(list_of_name_groups_1) & unique_names(list_of_name_groups_2)
for name in common_names:
list1_candidates.update(tuple(get_matching_name_groups(name, list_of_name_groups_1)))
list2_candidates.update(tuple(get_matching_name_groups(name, list_of_name_groups_2)))
intersections = defaultdict(lambda: 0)
for i, j in itertools.product(list1_candidates, list2_candidates):
intersections[(i, j)] = len(set(list_of_name_groups_1[i]) & set(list_of_name_groups_2[j]))
print(intersections)
>>> python intersections.py
defaultdict(<function <lambda> at 0x0000000000DC7620>, {(0, 2): 1})