我有几个列表,其中某些元素不止一个出现。一个例子是
lst[0]
Output: [1,2,3]
lst[1]
Output: [1,2]
lst[2]
Output: [3]
到目前为止,我已经计算出下面每个元素的出现情况
flat = [i for sub in lst for i in sub] #Group A will be lst[0] and so on
tmp = Counter(flat)
counts = Counter(tmp.values())
counts
Output: Counter({1: 3, 2: 2, 3: 1})
现在,我想为出现多次的元素拔出组,即2
将对应于Group A
和Group B
。我正在考虑编写一个for循环来检查所有组中每个合格元素的出现,但这似乎效率很低。
答案 0 :(得分:1)
听起来您想要每个元素的组列表,而不只是外观的数量。
因此,您显然不能只使用Counter
,因为它显式地将值映射到计数而不是列表。但是您只能使用dict
。
而且您显然也无法在构建字典之前将列表弄平,因为这会丢弃您要存储的组信息。
同时,不要担心尝试将整个事情写成单行。先写一些您可以理解的东西,然后看一看它是否可以工作。
所以:
groupmapping = {}
for i, group in enumerate(lst):
for elem in group:
groupmapping.setdefault(element, list()).append(i)
…或:
groupmapping = defaultdict(list)
for i, group in enumerate(lst):
for elem in group:
groupmapping[element].append(i)
如果您打算将groupmapping
用作正常的dict
,应该提高KeyError
而不是在失败的查找后返回[]
,则可以执行第一个操作,但是这里,看来您只是将其用于此目的,因此defaultdict
更有意义。 (如果需要的话,它也会更快一些。)
如果您的值不能在组内重复(或者可以重复),但是您想忽略该值并折叠组内的所有外观,只需将list
替换为set
,然后将append
与add
。
如果要为组命名而不是索引,则必须将这些名称放在某处,例如names = ['Group A', 'Group B', 'Group C']
。如果是这样,只需将enumerate(list)
替换为zip(names, list)
。
现在,获取每个重复元素的组:
for value, groups in groupmapping.items():
if len(groups) > 1:
print(f'{value} appears multiple times, in groups {groups}')
这样可以提高效率。当然,第二个循环在不同元素的数量上花费了线性时间O(M)
,但是原始循环(无论是隐藏在Counter
调用中还是被明确地写出)已经在其中花费了线性时间O(N)
总元素数,以及O(N+M)
,其中M < N
只是O(N)
。
更重要的是,您显然无法击败O(M)
的任何过程,这些过程会生成一系列M
值。
答案 1 :(得分:1)
您仍然需要遍历您的组至少一次,以找出它们中是否包含元素。对于非常大的组和多个查找,将它们变成集合然后检查一个元素而不是遍历它们可能是有益的(因为在Python方面,迭代要慢得多),但是这是可以做到的。它。如果您仅对一个元素的存在感兴趣,也无需计算其余元素。
因此,一个简单的功能:
def get_groups(src, contains, minimum=2):
res = [x for x in src if contains in x]
if len(res) >= minimum:
return res
应超出您的需求。然后,您可以将其用作:
c = [[1, 2, 3], [1, 2], [1]] # etc.
print(get_groups(c, 2)) # [[1, 2, 3], [1, 2]]
print(get_groups(c, 3)) # None
print(get_groups(c, 2, 3)) # None
print(get_groups(c, 1, 3)) # [[1, 2, 3], [1, 2], [1]]