在 Python 中查找列表的中位数

Posted

技术标签:

【中文标题】在 Python 中查找列表的中位数【英文标题】:Finding median of list in Python 【发布时间】:2014-07-28 21:16:20 【问题描述】:

如何在 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

【问题讨论】:

Selection Algorithm 这里的答案很好,所以我想我希望这是寻找中位数的大致规范答案,largely so I could close this。请注意,该问题有 30,000 次查看。如果这个问题没有以任何方式关闭或消除,我将不胜感激,以便它可以留在搜索结果中并吸收这些观点。 【参考方案1】:

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)) 的工作要多得多... (我写了一个函数,使用 mod 来确定是否会发生偶数分裂) def median(values): """获取值列表的中值 Args: values (iterable of float) : 一个数字列表 Returns: float """ # 写出 median() 函数 values=values.sort() n = len(values) if n%2==0: median1 = values[n//2] median2 = values[n//2 - 1] 中位数 = (median1 + median2)/2 else: median = values[n//2] 返回中位数 print(median([1,2,4,3,5]))跨度> 【参考方案2】:

您可以使用list.sort 来避免使用sorted 创建新列表并就地对列表进行排序。

另外你不应该使用list作为变量名,因为它会影响python自己的list。

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]

【讨论】:

简单的实用程序函数可能不应该改变任何参数(特别是如果函数名称是名词 IMO)。同样使用 sorted over .sort() 意味着参数不必是列表。它可以是任何迭代器。 我的意思是关于改变列表的函数。我提到支持任何可迭代作为排序的一个很好的副作用,但这不是它的主要好处。我希望中位数(列表)能够像几乎所有其他内置函数或数学函数一样工作。 next() 发生变异,但我想不出其他任何人。意外突变是调试的痛苦。 @WillS,当它被记录在案时有什么惊喜?如果您正在处理大数据或者您的内存量有限并且您无法制作列表的副本,那该怎么办? 使函数期望一个排序列表并记录它。 mylist.sort(); middle(mylist),但不可否认,这是一个品味问题。我只是认为,一般来说,变异应该尽可能地保留给方法。 list.sort() 返回 None 而不是列表本身的原因是为了使行为尽可能明显和清晰。在文档中隐藏所有内容就像在小字体中隐藏内容。 让我们continue this discussion in chat。【参考方案3】:

(适用于 python-2.x):

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

对于python-3.x,使用statistics.median

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

【讨论】:

虽然它不是写函数,但它仍然是一个更“pythonic”的解决方案恕我直言 @dartdog 不是真的;没有充分理由强制使用 Numpy 数组是不可取的。你强制了类型,更糟糕的是,失去了对任意类型的支持。 点数,有用。 这个函数比它需要的要费力得多。 PEP 450 提出了一个很好的论据来反对不使用库。你最终会犯错。【参考方案4】:

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 以将中值数据添加到查询结果中。干杯 如果你想找到一个排序数组的中位数怎么办。所以你不能使用内置函数 statistics.median 因为它会在再次排序时变慢 @GilbertS 然后看中间元素,或者平均中间两个。【参考方案5】:

如果需要更快的平均案例运行时间,您可以尝试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 如果你使用 PyPy,或者某些类型你不能轻易sort,或者愿意为速度写一个 C 扩展,等等。【参考方案6】:

我为一个数字列表定义了一个中值函数

def median(numbers):
    return (sorted(numbers)[int(round((len(numbers) - 1) / 2.0))] + sorted(numbers)[int(round((len(numbers) - 1) // 2.0))]) / 2.0

【讨论】:

【参考方案7】:

这是一个更清洁的解决方案:

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

注意:答案已更改为在 cmets 中包含建议。

【讨论】:

float(sum(…) / 2) 应替换为sum(…) / 2.0;否则,如果sum(…) 是整数,您将获得整数商的浮点版本。例如:float(sum([3, 4]) / 2)3.0,但 sum([3, 4]) / 2.03.5 为了完整起见,@musiphil:仅在 python 2 中,并且仅当您尚未完成 from __future__ import division【参考方案8】:

中值函数

def median(midlist):
    midlist.sort()
    lens = len(midlist)
    if lens % 2 != 0: 
        midl = (lens / 2)
        res = midlist[midl]
    else:
        odd = (lens / 2) -1
        ev = (lens / 2) 
        res = float(midlist[odd] + midlist[ev]) / float(2)
    return res

【讨论】:

【参考方案9】:

我在 Python implementation of "median of medians" algorithm 上发布了我的解决方案,这比使用 sort() 快一点。我的解决方案每列使用 15 个数字,速度约为 5N,比每列使用 5 个数字的速度 ~10N 快。最佳速度约为 4N,但我可能错了。

根据 Tom 在评论中的要求,我在此处添加了我的代码,以供参考。我认为速度的关键部分是每列使用 15 个数字,而不是 5 个。

#!/bin/pypy
#
# TH @***, 2016-01-20, linear time "median of medians" algorithm
#
import sys, random


items_per_column = 15


def find_i_th_smallest( A, i ):
    t = len(A)
    if(t <= items_per_column):
        # if A is a small list with less than items_per_column items, then:
        #
        # 1. do sort on A
        # 2. find i-th smallest item of A
        #
        return sorted(A)[i]
    else:
        # 1. partition A into columns of k items each. k is odd, say 5.
        # 2. find the median of every column
        # 3. put all medians in a new list, say, B
        #
        B = [ find_i_th_smallest(k, (len(k) - 1)/2) for k in [A[j:(j + items_per_column)] for j in range(0,len(A),items_per_column)]]

        # 4. find M, the median of B
        #
        M = find_i_th_smallest(B, (len(B) - 1)/2)


        # 5. split A into 3 parts by M,  < M ,  == M , and  > M 
        # 6. find which above set has A's i-th smallest, recursively.
        #
        P1 = [ j for j in A if j < M ]
        if(i < len(P1)):
            return find_i_th_smallest( P1, i)
        P3 = [ j for j in A if j > M ]
        L3 = len(P3)
        if(i < (t - L3)):
            return M
        return find_i_th_smallest( P3, i - (t - L3))


# How many numbers should be randomly generated for testing?
#
number_of_numbers = int(sys.argv[1])


# create a list of random positive integers
#
L = [ random.randint(0, number_of_numbers) for i in range(0, number_of_numbers) ]


# Show the original list
#
# print L


# This is for validation
#
# print sorted(L)[int((len(L) - 1)/2)]


# This is the result of the "median of medians" function.
# Its result should be the same as the above.
#
print find_i_th_smallest( L, (len(L) - 1) / 2)

【讨论】:

【参考方案10】:
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

【讨论】:

【参考方案11】:

这是我在 Codecademy 的这个练习中想到的:

def median(data):
    new_list = sorted(data)
    if len(new_list)%2 > 0:
        return new_list[len(new_list)/2]
    elif len(new_list)%2 == 0:
        return (new_list[(len(new_list)/2)] + new_list[(len(new_list)/2)-1]) /2.0

print median([1,2,3,4,5,9])

【讨论】:

【参考方案12】:

以下是不使用median 函数查找中位数的繁琐方法:

def median(*arg):
    order(arg)
    numArg = len(arg)
    half = int(numArg/2)
    if numArg/2 ==half:
        print((arg[half-1]+arg[half])/2)
    else:
        print(int(arg[half]))

def order(tup):
    ordered = [tup[i] for i in range(len(tup))]
    test(ordered)
    while(test(ordered)):
        test(ordered)
    print(ordered)


def test(ordered):
    whileloop = 0 
    for i in range(len(ordered)-1):
        print(i)
        if (ordered[i]>ordered[i+1]):
            print(str(ordered[i]) + ' is greater than ' + str(ordered[i+1]))
            original = ordered[i+1]
            ordered[i+1]=ordered[i]
            ordered[i]=original
            whileloop = 1 #run the loop again if you had to switch values
    return whileloop

【讨论】:

这是冒泡排序吗?为什么? 你为什么要交换值?【参考方案13】:

我在浮点值列表方面遇到了一些问题。我最终使用了来自 python3 statistics.median 的代码 sn-p 并且在没有导入的情况下可以完美地处理浮点值。 source

def calculateMedian(list):
    data = sorted(list)
    n = len(data)
    if n == 0:
        return None
    if n % 2 == 1:
        return data[n // 2]
    else:
        i = n // 2
        return (data[i - 1] + data[i]) / 2

【讨论】:

【参考方案14】:

当然你可以使用内置函数,但是如果你想创建自己的函数,你可以这样做。这里的技巧是使用 ~ 运算符将正数翻转为负数。例如 ~2 -> -3 并在 Python 中使用负数 for list 将从末尾开始计算项目。因此,如果您有 mid == 2 那么它将从开头取第三个元素,从结尾取第三个元素。

def median(data):
    data.sort()
    mid = len(data) // 2
    return (data[mid] + data[~mid]) / 2

【讨论】:

【参考方案15】:
def midme(list1):

    list1.sort()
    if len(list1)%2>0:
            x = list1[int((len(list1)/2))]
    else:
            x = ((list1[int((len(list1)/2))-1])+(list1[int(((len(list1)/2)))]))/2
    return x


midme([4,5,1,7,2])

【讨论】:

【参考方案16】:
def median(array):
    if len(array) < 1:
        return(None)
    if len(array) % 2 == 0:
        median = (array[len(array)//2-1: len(array)//2+1])
        return sum(median) / len(median)
    else:
        return(array[len(array)//2])

【讨论】:

虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。 非常抱歉!刚开始,Stack Overflow,不知道怎么加个总结...... 点击帖子下方的“编辑”链接并添加摘要,然后保存。【参考方案17】:
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

【讨论】:

看来你的第一行代码被遗漏了,你可以通过编辑你的帖子并将函数标题缩进4个空格来解决这个问题。【参考方案18】:

很简单;

def median(alist):
    #to find median you will have to sort the list first
    sList = sorted(alist)
    first = 0
    last = len(sList)-1
    midpoint = (first + last)//2
    return midpoint

你可以像这样使用返回值median = median(anyList)

【讨论】:

Median 要求您在找到中点之前对数组进行排序。 sList 返回排序后的数组。不返回中位数【参考方案19】:
import numpy as np
def get_median(xs):
        mid = len(xs) // 2  # Take the mid of the list
        if len(xs) % 2 == 1: # check if the len of list is odd
            return sorted(xs)[mid] #if true then mid will be median after sorting
        else:
            #return 0.5 * sum(sorted(xs)[mid - 1:mid + 1])
            return 0.5 * np.sum(sorted(xs)[mid - 1:mid + 1]) #if false take the avg of mid
print(get_median([7, 7, 3, 1, 4, 5]))
print(get_median([1,2,3, 4,5]))

【讨论】:

【参考方案20】:

函数中位数

def median(d):
    d=np.sort(d)
    n2=int(len(d)/2)
    r=n2%2
    if (r==0):
        med=d[n2] 
    else:
        med=(d[n2] + d[n2+1]) / 2
    return med

【讨论】:

逻辑需要更正,以下一些答案 [upvoted] 具有正确的逻辑,需要对长度进行“偶数”检查,否则 ex 失败。对于 [1,2,3] 它返回 2.5 预期答案是 2。【参考方案21】:

如果您需要有关列表分布的更多信息,百分位数方法可能会很有用。中值对应于列表的第 50 个百分位:

import numpy as np
a = np.array([1,2,3,4,5,6,7,8,9])
median_value = np.percentile(a, 50) # return 50th percentile
print median_value 

【讨论】:

【参考方案22】:

中位数(和百分位数)的更通用方法是:

def get_percentile(data, percentile):
    # Get the number of observations
    cnt=len(data)
    # Sort the list
    data=sorted(data)
    # Determine the split point
    i=(cnt-1)*percentile
    # Find the `floor` of the split point
    diff=i-int(i)
    # Return the weighted average of the value above and below the split point
    return data[int(i)]*(1-diff)+data[int(i)+1]*(diff)

# Data
data=[1,2,3,4,5]
# For the median
print(get_percentile(data=data, percentile=.50))
# > 3
print(get_percentile(data=data, percentile=.75))
# > 4

# Note the weighted average difference when an int is not returned by the percentile
print(get_percentile(data=data, percentile=.51))
# > 3.04

【讨论】:

【参考方案23】:

返回给定列表中位数的简单函数:

def median(lst):
    lst.sort()  # 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

【讨论】:

统计数据太棒了!【参考方案24】:

两行就够了。

def get_median(arr):
    '''
    Calculate the median of a sequence.
    :param arr: list
    :return: int or float
    '''
    arr.sort()
    return arr[len(arr)//2] if len(arr) % 2 else (arr[len(arr)//2] + arr[len(arr)//2-1])/2

【讨论】:

【参考方案25】:

做自己。

def median(numbers):
    """
    Calculate median of a list numbers.
    :param numbers: the numbers to be calculated.
    :return: median value of numbers.

    >>> median([1, 3, 3, 6, 7, 8, 9])
    6
    >>> median([1, 2, 3, 4, 5, 6, 8, 9])
    4.5
    >>> import statistics
    >>> import random
    >>> numbers = random.sample(range(-50, 50), k=100)
    >>> statistics.median(numbers) == median(numbers)
    True
    """
    numbers = sorted(numbers)
    mid_index = len(numbers) // 2
    return (
        (numbers[mid_index] + numbers[mid_index - 1]) / 2 if mid_index % 2 == 0
        else numbers[mid_index]
    )


if __name__ == "__main__":
    from doctest import testmod

    testmod()

source from

【讨论】:

【参考方案26】:

我做的是这样的:

def median(a):
    a.sort()
    if len(a) / 2 != int:
        return a[len(a) / 2]
    else:
        return (a[len(a) / 2] + a[(len(a) / 2) - 1]) / 2

解释: 基本上如果列表中的项目数是奇数,则返回中间数,否则,如果您是偶数列表的一半,python会自动将较大的数字四舍五入,因此我们知道之前的数字那会少一个(因为我们对其进行了排序),我们可以将默认的较高数字和低于它的数字相加,然后将它们除以 2 以找到中位数。

【讨论】:

欢迎来到 Stack Overflow!请检查您的解决方案是否已被提议为像 one 这样的另一个答案。此外,if len(a) / 2 != int 始终为 True,因为整数或浮点 value 不能等于整数 class【参考方案27】:

简单地说,创建一个带有参数作为数字列表的中值函数并调用该函数。

def median(l):
        l.sort()
        lent = len(l)
        if (lent%2)==0:
            m = int(lent/2)
            result = l[m]
        else:
            m = int(float(lent/2) -0.5)
            result = l[m]
        return ('median is: '.format(result))

希望对你有帮助,谢谢!

【讨论】:

【参考方案28】:

试试这个

import math
def find_median(arr):
    if len(arr)%2==1:
        med=math.ceil(len(arr)/2)-1
        return arr[med]
    else:
        return -1
print(find_median([1,2,3,4,5,6,7,8]))

【讨论】:

以上是关于在 Python 中查找列表的中位数的主要内容,如果未能解决你的问题,请参考以下文章

python常用的简单算法,二分查找冒泡排序数组翻转等

Leetcode刷题Python295. 数据流的中位数

Leetcode刷题Python295. 数据流的中位数

python-leetcode295-双堆数据流的中位数

C ++有效地计算运行中位数[重复]

理解python 中位数