在Python中计算多峰列表的众数

7
我正在尝试在Python中计算一组值的众数(最常出现的值)。我提出了一个解决方案,但结果是错误的。后来我意识到我的数据可能具有多个众数;
ie 1,1,2,3,4,4 mode = 1 & 4

这是我目前想到的:

def mode(valueList):
  frequencies = {}
  for value in valueList:
    if value in frequencies:
      frequencies[value] += 1
    else:
      frequencies[value] = 1
  mode = max(frequencies.itervalues())
  return mode

我认为问题在于我输出的是值而不是最大值的指针。无论如何,有人可以建议一种更好的方法来处理存在多个众数的情况吗?或者如果失败了,我该如何修复已有的代码并找出单个众数?
正如你可能已经注意到的,我很新手,感谢您的帮助。
编辑:应该提到我使用的是Python 2.4版本。

2
你真的应该升级到最新支持的Python版本。 - Burhan Khalid
4个回答

5
在Python >=2.7中,使用collections.Counter来生成频率表。
from collections import Counter
from itertools import takewhile

data = [1,1,2,3,4,4]
freq = Counter(data)
mostfreq = freq.most_common()
modes = list(takewhile(lambda x_f: x_f[1] == mostfreq[0][1], mostfreq))

请注意使用匿名函数 (lambda) 检查一个二元组 (_, f) 是否具有与最高频元素相同的频率。

2
@Captastic:那你应该升级了。Python 2.4是2004年的版本;即使Python 2.5也不再接收安全补丁。 - Fred Foo
我真的不知道该怎么做才能让最后那个“takewhile”行在我的3.2.3版本上正常运行,但我是新手。你有什么想法吗? - Matthew Cornell
1
@MatthewCornell:在函数定义(包括lambda表达式)中的元组解包在3.2版本中已被移除,甚至可能更早。我会更新答案。 - Fred Foo

5

注意,从 Python 3.8 开始,标准库中包含了 statistics.multimode 函数,它会按照首次出现的顺序返回一个最常出现的值列表:

from statistics import multimode

multimode([1, 1, 2, 3, 4, 4])
# [1, 4]

4

首先,您返回的是frequencies中的值而不是键。这意味着您得到的是众数的计数,而不是众数本身。通常情况下,要获取众数,您会使用max函数的key关键字参数,如下所示:

>>> max(frequencies, key=counts.get())

但在2.4中不存在这个!我相信以下方法可以在2.4中实现:

>>> import random
>>> l = [random.randrange(0, 5) for _ in range(50)]
>>> frequencies = {}
>>> for i in l:
...     frequencies[i] = frequencies.get(i, 0) + 1
... 
>>> frequencies
{0: 11, 1: 13, 2: 8, 3: 8, 4: 10}
>>> mode = max((v, k) for k, v in frequencies.iteritems())[1]
>>> mode
1
>>> max_freq = max(frequencies.itervalues())
>>> modes = [k for k, v in frequencies.iteritems() if v == max_freq]
>>> modes
[1]

我更喜欢使用装饰-排序-取消装饰的习惯用法来代替使用cmp关键字。我认为这样更易读。可能只是我的个人观点。


非常感谢,我还应该提到我在2.4版本中。我已经更新了帖子。 - Captastic
@Captastic,啊。没有defaultdict,没有Counter,也没有max函数的key参数。哎呀。只能用最麻烦的方式来做了……等一下。 - senderle
在Py2.4中,您将使用maxcmp参数而不是key(当时还不存在); 因此,mode = max(frequencies, cmp=lambda i,j: cmp(counts[i], counts[j])) - lvc
@senderle 那就是它!它工作得非常好。非常感谢你。现在我要学习它到底是如何工作的 :) - Captastic

1

在迭代时,您可以使用计数器作为顶部值,类似于以下内容:

def mode(valueList):
  frequencies = {}
  mx = None
  for value in valueList:
    if value in frequencies:
      frequencies[value] += 1
    else:
      frequencies[value] = 1
    if not mx or frequencies[value] > mx[1]:
      mx = (value, frequencies[value])

  mode = mx[0]
  return mode

多种模式的另一种方法是使用nlargest,它可以给出字典中N个最大的值:

from heapq import nlargest
import operator

def mode(valueList, nmodes):
  frequencies = {}

  for value in valueList:
    frequencies[value] = frequencies.get(value, 0) + 1

  return [x[0] for x in nlargest(nmodes,frequencies.iteritems(),operator.itemgetter(1))]

那就可以了,谢谢。虽然不确定如何将其转换为适用于多个模式。 - Captastic
谢谢你的建议,我选择了无服务器的方法,但为了学习经验,我会试试你的方法。 - Captastic

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