访问列表中高频元素的出现组

时间:2018-07-16 16:57:43

标签: python list

我有几个列表,其中某些元素不止一个出现。一个例子是

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 AGroup B。我正在考虑编写一个for循环来检查所有组中每个合格元素的出现,但这似乎效率很低。

2 个答案:

答案 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,然后将appendadd

如果要为组命名而不是索引,则必须将这些名称放在某处,例如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]]