如何在列表中找到出现最频繁的元素,如果有并列,则返回最后一次出现的元素?

7
基本上,如果给定一个列表
events = [123,123,456,456,456,123]

我希望它返回456,因为456比123更早出现。

我制作了由数字列表的计数和索引组成的列表。我还创建了一个字典,其中键是来自事件(原始部分)的元素,而值是键的.count()

我不知道该怎么做,需要一些帮助。


3
让我们从基础开始。如果用纸和笔做这个,你会怎么做?你需要了解什么,需要跟踪哪些内容以满足这个测试条件? - Makoto
如果你知道如何在列表中找到最常见的元素,并且知道如何找到相等的元素,那么你可以从列表的末尾开始反向搜索每个元素的第一个反向出现。深入列表的元素获胜。 - Patashu
@Makoto,至少让我们总结一下,当前的解决方案可以确定最常见的元素,如果没有相等的话。 - Stephane Rolland
OP,不错的方法。唯一的问题是常规字典没有顺序。如果您想从基础开始,给定一个输入列表,您将为每个不同的元素创建一个元组(元素、计数)。获取一组不同元素的最快方法是什么?使用 set(input_list) 然后计算该集合中每个元素出现的次数。 - CppLearner
我需要知道列表中每个元素的位置和出现次数。然后,我需要查看是否存在最常见项的并列,并且如果有的话,我会选择最后出现的那个。 - user2180683
3个回答

5

方法

查找出现频率最高的项目(Counter.most_common)。然后在这些候选项中找到具有最小索引的项目(枚举成索引字典,min of {index: key}.iteritems())。

代码

从 @gnibbler 和 @Jeff 大量借鉴:

from collections import Counter

def most_frequent_first(events):
    frequencies = Counter(events)
    indexes = {event: i for i, event in enumerate(events)}
    most_frequent_with_indexes = {indexes[key]: key for key, _ in frequencies.most_common()}
    return min(most_frequent_with_indexes.iteritems())[1]

events = [123,123,456,456,456,123, 1, 2, 3, 2, 3]
print(most_frequent_first(events))

结果

>>> print(most_frequent_first(events))
456

代码

更好的代码将为您提供频率和索引,显示代码是否正确工作。这里是一个使用named_tuple实现的示例:

from collections import Counter, namedtuple

frequent_first = namedtuple("frequent_first", ["frequent", "first"])

def most_frequent_first(events):
    frequencies = Counter(events)
    indexes = {event: i for i, event in enumerate(events)}
    combined = {key: frequent_first(value, indexes[key]) for key, value in frequencies.iteritems()}
    return min(combined.iteritems(), key=lambda t: (-t[1].frequent, t[1].first))

events = [123,123,456,456,456,123, 1, 2, 3, 2, 3]
print(most_frequent_first(events))

结果

>>> print(most_frequent_first(events))
(456, frequent_first(frequent=3, first=4))

2

使用collections.counter

>>> import collections

>>> events = [123,123,456,456,456,123]
>>> counts = collections.Counter(events)
>>> print counts
Counter({456: 3, 123: 3})
>>> mostCommon = counts.most_common()
>>> print mostCommon
[(456, 3), (123, 3)]

那就是难点所在。


如果您更改顺序并将最后的123放在任何456之前,仍然会显示456作为第一个,而不是应该是123。 - gpoo
我认为 most_common() 无法完美解决这个问题,因为它会任意返回相同的计数。你无法决定哪一个在列表中排名最后。 - zhangyangyu
这里故意没有给出完整答案。正如答案所说,“那是难点”。理想情况下,Jeff将添加获取最后一部分完成的逻辑。一个可靠的方法可能是创建一个同时继承CounterOrderedDict的对象,从而获得两者的优点。(列表必须以相反的顺序迭代。) - Platinum Azure

2
>>> from collections import Counter
>>> events = [123,123,456,456,456,123]
>>> c = Counter(events)
>>> idxs = {k: v for v,k in enumerate(events)}
>>> sorted(c.items(), key=lambda (k,v): (-v, idxs[k]))
[(456, 3), (123, 3)]

我认为这个代码没有做正确的事情。>>> events = [123,123,456,456,456,123, 1, 2, 3, 2, 3] ... >>> sorted(c.items(), key=lambda (k,v): (v, idxs[k])) [(1, 1), (2, 2), (3, 2), (456, 3), (123, 3)] - hughdbrown
我认为你需要按照出现次数的降序和最后一次出现的索引的升序进行排序。这是正确的做法:>>> sorted(c.items(), key=lambda (k,v): (-v, idxs[k])) [(456, 3), (123, 3), (2, 2), (3, 2), (1, 1)] - hughdbrown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接