在Python中查找列表的中位数

280

如何在Python中找到列表的中位数?该列表可以是任意大小,并且数字不保证按任何特定顺序排列。

如果列表包含偶数个元素,则函数应返回中间两个元素的平均值。

以下是一些示例(为了显示目的而排序):

median([1]) == 1
median([1, 1]) == 1
median([1, 1, 2, 4]) == 1.5
median([0, 2, 5, 6, 8, 9, 9]) == 6
median([0, 0, 0, 0, 4, 4, 6, 8]) == 2

2
选择算法 - amit
13
这里的答案很好,所以我认为我想让这个问题大致成为寻找中位数的规范答案,(主要是为了能够关闭这个问题)。请注意,该问题已经有了30,000个浏览量。如果这个问题没有被关闭或遗忘,我将不胜感激,因为它可以继续出现在搜索结果中并吸引那些浏览量。 - Veedrac
28个回答

326

Python 3.4有 statistics.median:

返回数字数据的中位数(中间值)。

当数据点数量为奇数时,返回中间数据点。 当数据点数量为偶数时,通过取两个中间值的平均值进行插值计算得到中位数:

>>> median([1, 3, 5])
3
>>> median([1, 3, 5, 7])
4.0
使用方法:
import statistics

items = [6, 1, 8, 2, 3]

statistics.median(items)
#>>> 3

它也对类型非常小心:

statistics.median(map(float, items))
#>>> 3.0

from decimal import Decimal
statistics.median(map(Decimal, items))
#>>> Decimal('3')

很棒,对我非常有用,将其加入到pip3 install itunizer中,以将中位数数据添加到查询结果中。干杯 - james-see
如果您想查找已排序数组的中位数,那该怎么办呢?因为再次排序会减慢速度,所以您不能使用内置函数statistics.median。 - GilbertS
3
那么请看中间的元素,或者求中间两个数的平均数。 - Veedrac

197

(适用于):

def median(lst):
    n = len(lst)
    s = sorted(lst)
    return (s[n//2-1]/2.0+s[n//2]/2.0, s[n//2])[n % 2] if n else None

>>> median([-5, -5, -3, -4, 0, -1])
-3.5

numpy.median():

>>> from numpy import median
>>> median([1, -4, -1, -1, 1, -3])
-1.0

对于 ,使用 statistics.median

>>> from statistics import median
>>> median([5, 2, 3, 8, 9, -2])
4.0

10
虽然它不是一个函数,但在我看来,这仍然是一个更“Pythonic”的解决方案。 - dartdog
6
并不完全正确;如果没有充分的理由,强制转换为Numpy数组是不可取的。您已经强制转换了类型,并且更糟糕的是失去了对任意类型的支持。 - Veedrac
3
这个函数比必要的工作量要大得多。 - Martijn Pieters
4
PEP 450提出了一个很好的观点,反对不使用库。你最终会犯错误。 - Alex Harvey
1
对于那些无法或不想安装numpy包的人来说,statistics.median非常有效。 - Filipe Manuel
显示剩余2条评论

75

sorted()函数对此非常有帮助。使用sorted函数对列表进行排序,然后只需返回中间值(如果列表包含偶数个元素,则取中间两个值的平均值)。

def median(lst):
    sortedLst = sorted(lst)
    lstLen = len(lst)
    index = (lstLen - 1) // 2
   
    if (lstLen % 2):
        return sortedLst[index]
    else:
        return (sortedLst[index] + sortedLst[index + 1])/2.0

然而,这种方法效率非常低:在最坏情况下(Theta(n lg n)),排序比选择中位数(Theta(n))要多得多的工作... - J..y B..y
我编写了一个函数,使用模运算来确定是否可以进行均匀分割。def median(values): """获取值列表的中位数 Args: values (可迭代的浮点数): 数字列表 Returns: float """

编写median()函数

values=values.sort() n = len(values) if n%2==0: median1 = values[n//2] median2 = values[n//2 - 1] median = (median1 + median2)/2 else: median = values[n//2] return medianprint(median([1,2,4,3,5]))
- Golden Lion

19
当然,在Python3中您可以使用内置函数,但如果您正在使用Python2或只想创建自己的函数,您可以像这样操作。这里的诀窍是使用 ~ 操作符将正数转换为负数。例如 ~2 -> -3,并且在Python中使用负数的列表将从末尾计算项。因此,如果mid == 2,则它将从开头取第三个元素和倒数第三个元素的第三个项。
def median(data):
    data.sort()
    mid = len(data) // 2
    return (data[mid] + data[~mid]) / 2.0

13

以下是更简洁的解决方案:

def median(lst):
    quotient, remainder = divmod(len(lst), 2)
    if remainder:
        return sorted(lst)[quotient]
    return sum(sorted(lst)[quotient - 1:quotient + 1]) / 2.

注意:答案已更改以纳入评论中的建议。


7
float(sum(…) / 2) 应该替换为 sum(…) / 2.0;否则,如果 sum(…) 是整数,则会得到整数商的浮点版本。例如:float(sum([3, 4]) / 2)3.0,但 sum([3, 4]) / 2.03.5 - musiphil
为了完整性,@musiphil:仅适用于Python 2,并且仅在您尚未执行“from future import division”时。 - Chris L. Barnes

11
你可以使用list.sort避免使用sorted创建新列表,并原地对列表进行排序。
此外,不应将list用作变量名,因为它会掩盖Python自己的列表
def median(l):
    half = len(l) // 2
    l.sort()
    if not len(l) % 2:
        return (l[half - 1] + l[half]) / 2.0
    return l[half]

7
简单的实用函数最好不要改变任何参数(尤其是如果函数名是一个名词)。另外,使用 sorted 而不是 .sort() 可以使参数不必是一个列表,可以是任何迭代器。 - Will S
1
我的观点是函数改变了列表。我提到支持任何可迭代对象是sorted的一个不错的副作用,但那并不是它的主要优点。我个人希望median(list)能像几乎所有其他内置函数或数学函数一样工作。next()会改变对象本身,但我想不出其他的例子。意外的变异对于调试来说非常麻烦。 - Will S
@WillS,如果已经有文档说明了这种情况,那怎么可能会是一个惊喜呢?如果你要处理大量数据,或者内存受限而无法复制列表,那该怎么办呢? - Padraic Cunningham
3
让这个函数接受一个已排序的列表并进行文档说明。 mylist.sort(); middle(mylist),但这显然是个人口味问题。我只是认为在可能的情况下应该将变异保留给方法。列表.sort()之所以返回None而不是列表本身,是为了使行为尽可能明显和清晰。在文档中隐藏所有内容就像在小字体中隐藏东西一样。 - Will S
让我们在聊天中继续这个讨论 - Will S

11

如果需要更快的平均运行时间,您可以尝试使用quickselect算法。在平均(和最佳)情况下,quickselect的性能为O(n),但在不利情况下可能会达到O(n²)

这是一个使用随机选择枢轴的实现:

import random

def select_nth(n, items):
    pivot = random.choice(items)

    lesser = [item for item in items if item < pivot]
    if len(lesser) > n:
        return select_nth(n, lesser)
    n -= len(lesser)

    numequal = items.count(pivot)
    if numequal > n:
        return pivot
    n -= numequal

    greater = [item for item in items if item > pivot]
    return select_nth(n, greater)

你可以轻松地将这转化为一个查找中位数的方法:
def median(items):
    if len(items) % 2:
        return select_nth(len(items)//2, items)

    else:
        left  = select_nth((len(items)-1) // 2, items)
        right = select_nth((len(items)+1) // 2, items)

        return (left + right) / 2

这段代码非常不优化,但即使进行了优化,也很难超过Tim Sort(CPython内置的sort),因为它非常快速。我以前试过了,结果失败了。


那么如果sort()更快,为什么还要考虑这个呢? - Max
@Max 如果你正在使用PyPy,或者某些类型无法轻易地进行“sort”操作,或者愿意为了速度编写C扩展等。 - Veedrac

9
def median(x):
    x = sorted(x)
    listlength = len(x) 
    num = listlength//2
    if listlength%2==0:
        middlenum = (x[num]+x[num-1])/2
    else:
        middlenum = x[num]
    return middlenum

7
def median(array):
    """Calculate median of the given list.
    """
    # TODO: use statistics.median in Python 3
    array = sorted(array)
    half, odd = divmod(len(array), 2)
    if odd:
        return array[half]
    return (array[half - 1] + array[half]) / 2.0

6

一个简单的函数,用于返回给定列表的中位数:

def median(lst):
    lst = sorted(lst)  # Sort the list first
    if len(lst) % 2 == 0:  # Checking if the length is even
        # Applying formula which is sum of middle two divided by 2
        return (lst[len(lst) // 2] + lst[(len(lst) - 1) // 2]) / 2
    else:
        # If length is odd then get middle value
        return lst[len(lst) // 2]

median函数的一些示例:

>>> median([9, 12, 20, 21, 34, 80])  # Even
20.5
>>> median([9, 12, 80, 21, 34])  # Odd
21

如果您想使用库,只需简单地执行以下操作:

>>> import statistics
>>> statistics.median([9, 12, 20, 21, 34, 80])  # Even
20.5
>>> statistics.median([9, 12, 80, 21, 34])  # Odd
21

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