Tìm trung vị của danh sách trong Python


181

Làm thế nào để bạn tìm thấy trung vị của một danh sách trong Python? Danh sách có thể có bất kỳ kích thước nào và các con số không được đảm bảo theo bất kỳ thứ tự cụ thể nào.

Nếu danh sách chứa số phần tử chẵn, hàm sẽ trả về mức trung bình của hai phần tử ở giữa.

Dưới đây là một số ví dụ (được sắp xếp cho mục đích hiển thị):

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


9
Các câu trả lời ở đây là tốt, vì vậy tôi nghĩ rằng tôi muốn đây gần như là một câu trả lời kinh điển cho việc tìm kiếm trung bình, phần lớn để tôi có thể đóng nó . Lưu ý rằng câu hỏi đó có 30 nghìn lượt xem. Tôi đánh giá cao nếu câu hỏi này không bị đóng hoặc bị xóa theo bất kỳ cách nào để nó có thể nằm trên kết quả tìm kiếm và thay vào đó là những quan điểm đó.
Veedrac

Câu trả lời:


214

Python 3,4 có statistics.median:

Trả về giá trị trung bình (giá trị trung bình) của dữ liệu số.

Khi số lượng điểm dữ liệu là số lẻ, trả về điểm dữ liệu giữa. Khi số lượng điểm dữ liệu là chẵn, trung vị được nội suy bằng cách lấy trung bình của hai giá trị trung bình:

>>> median([1, 3, 5])
3
>>> median([1, 3, 5, 7])
4.0

Sử dụng:

import statistics

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

statistics.median(items)
#>>> 3

Nó cũng khá cẩn thận với các loại:

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

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

Hoàn hảo, làm việc cho tôi để thêm nó vào pip3 install itunizerđể thêm dữ liệu trung bình vào kết quả truy vấn. Chúc mừng
jamescampbell

Điều gì nếu bạn muốn tìm trung vị của một mảng được sắp xếp. Vì vậy, bạn không thể sử dụng hàm thống kê
hàm.median tích hợp

2
@GilbertS Sau đó nhìn vào phần tử ở giữa, hoặc trung bình hai phần giữa.
Veedrac

163

(Hoạt động với ):

def median(lst):
    n = len(lst)
    s = sorted(lst)
    return (sum(s[n//2-1:n//2+1])/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

Dành cho , sử dụng statistics.median:

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

9
Mặc dù nó không viết một chức năng, nhưng nó vẫn là một giải pháp "pythonic" hơn
imho

6
@dartdog Không thực sự; không thể buộc phải ép buộc vào một mảng Numpy mà không có lý do chính đáng. Bạn đã ép buộc các loại và tệ hơn là mất hỗ trợ cho các loại tùy ý.
Veedrac

1
Điểm lấy, hữu ích.
phi tiêu

3
Mặc dù vậy, chức năng này tốn nhiều công sức hơn mức cần thiết.
Martijn Pieters

3
PEP 450 đưa ra một lập luận tốt chống lại việc không sử dụng thư viện. Cuối cùng bạn sẽ phạm sai lầm.
Alex Harvey

51

Hàm sort () rất hữu ích cho việc này. Sử dụng hàm được sắp xếp để sắp xếp danh sách, sau đó chỉ cần trả về giá trị trung bình (hoặc trung bình hai giá trị giữa nếu danh sách chứa một số lượng phần tử chẵn).

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

Mặc dù nó rất kém hiệu quả: sắp xếp công việc nhiều hơn trong trường hợp xấu nhất (Theta (n lg n)) so với việc chọn trung vị (Theta (n)) ...
Jeremy

12

Đây là một giải pháp sạch hơn:

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

Lưu ý: Trả lời thay đổi để kết hợp đề xuất trong ý kiến.


7
float(sum(…) / 2)nên được thay thế bằng sum(…) / 2.0; mặt khác, nếu sum(…)là số nguyên, bạn sẽ nhận được phiên bản float của thương số nguyên. Ví dụ: float(sum([3, 4]) / 2)3.0, nhưng sum([3, 4]) / 2.03.5.
musiphil

Để hoàn thiện, @musiphil: chỉ trong python 2 và chỉ khi bạn chưa hoàn thành from __future__ import division.
Chris L. Barnes

11

Bạn có thể thử thuật toán quickselect nếu cần thời gian chạy trường hợp trung bình nhanh hơn. Quickselect có hiệu suất trường hợp trung bình (và tốt nhất) O(n), mặc dù nó có thể kết thúc O(n²)vào một ngày tồi tệ.

Đây là một triển khai với một trục được chọn ngẫu nhiê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)

Bạn có thể biến điều này thành một phương pháp để tìm trung bình:

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

Điều này rất không được đánh giá cao, nhưng không có khả năng ngay cả một phiên bản được tối ưu hóa sẽ vượt trội hơn so với Tim Sort (tích hợp sẵn của CPython sort) vì điều đó thực sự nhanh . Tôi đã thử trước đây và tôi đã thua.


Vậy tại sao thậm chí nghĩ về điều này nếu sort () nhanh hơn?
Tối đa

@Max Nếu bạn đang sử dụng PyPy hoặc một số loại bạn không thể sortdễ dàng hoặc sẵn sàng viết tiện ích mở rộng C cho tốc độ, v.v.
Veedrac

10

Tất nhiên bạn có thể sử dụng hàm xây dựng, nhưng nếu bạn muốn tạo riêng, bạn có thể làm một cái gì đó như thế này. Mẹo ở đây là sử dụng toán tử ~ lật số dương thành âm. Ví dụ ~ 2 -> -3 và sử dụng phủ định trong danh sách trong Python sẽ đếm các mục từ cuối. Vì vậy, nếu bạn có mid == 2 thì nó sẽ lấy phần tử thứ ba từ đầu và mục thứ ba từ cuối.

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

8

Bạn có thể sử dụng list.sortđể tránh tạo danh sách mới sortedvà sắp xếp danh sách tại chỗ.

Ngoài ra, bạn không nên sử dụng listlàm tên biến vì nó làm mờ danh sách riêng của 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]

5
Các hàm tiện ích đơn giản có lẽ không nên thay đổi bất kỳ đối số nào (Đặc biệt nếu tên hàm là danh từ IMO). Ngoài ra, sử dụng được sắp xếp trên .sort () có nghĩa là đối số không phải là một danh sách. Nó có thể là bất kỳ vòng lặp.
Sẽ

1
Quan điểm của tôi là về chức năng đột biến danh sách. Tôi đã đề cập đến việc hỗ trợ bất kỳ lần lặp nào như một tác động phụ tốt đẹp của việc sắp xếp, nhưng đó không phải là lợi ích chính. Tôi cho người ta mong đợi trung bình (danh sách) hoạt động giống như hầu hết các hàm dựng sẵn hoặc hàm toán học khác. next () đột biến, nhưng tôi không thể nghĩ ra bất kỳ ai khác. Đột biến bất ngờ là một nỗi đau ở mông để gỡ lỗi.
Sẽ

@WillS, làm thế nào là một bất ngờ khi nó được ghi lại? Điều gì sẽ xảy ra nếu bạn đang xử lý dữ liệu lớn hoặc bạn bị hạn chế số lượng bộ nhớ và bạn không thể tạo một bản sao của danh sách, sau đó thì sao?
Padraic Cickyham

2
Làm cho hàm mong đợi một danh sách được sắp xếp và tài liệu đó. mylist.sort(); middle(mylist)Nhưng không thể phủ nhận đó là vấn đề của hương vị. Tôi chỉ nghĩ rằng đột biến nói chung nên được dành riêng cho các phương pháp càng xa càng tốt. Lý do list.sort () trả về Không thay vì chính danh sách là để làm cho hành vi rõ ràng và rõ ràng nhất có thể. Giấu mọi thứ trong tài liệu cũng giống như giấu đồ trong bản in nhỏ.
Sẽ


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

7
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

1
Có vẻ như dòng mã đầu tiên của bạn bị bỏ sót, bạn có thể giải quyết điều này bằng cách chỉnh sửa bài đăng của mình và thụt vào tiêu đề chức năng với 4 khoảng trắng.
Johan

4

Tôi đã đăng giải pháp của mình khi triển khai Python về thuật toán "median of median" , nhanh hơn một chút so với sử dụng sort (). Giải pháp của tôi sử dụng 15 số trên mỗi cột, cho tốc độ ~ 5N nhanh hơn tốc độ ~ 10N khi sử dụng 5 số trên mỗi cột. Tốc độ tối ưu là ~ 4N, nhưng tôi có thể sai về nó.

Theo yêu cầu của Tom trong bình luận của anh ấy, tôi đã thêm mã của mình vào đây, để tham khảo. Tôi tin rằng phần quan trọng cho tốc độ là sử dụng 15 số trên mỗi cột, thay vì 5.

#!/bin/pypy
#
# TH @stackoverflow, 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)

3

Đây là những gì tôi đã đưa ra trong bài tập này trong 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])

2

Chức năng trung bình

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

2

Tôi đã có một số vấn đề với danh sách các giá trị float. Tôi đã kết thúc bằng cách sử dụng một đoạn mã từ thống kê python3.median và đang hoạt động hoàn hảo với các giá trị float mà không cần nhập. nguồn

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

2
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])

1

Tôi đã định nghĩa một hàm trung vị cho một danh sách các số là

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

1
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])

3
Mặc dù mã này có thể trả lời câu hỏi, cung cấp ngữ cảnh bổ sung về lý do và / hoặc cách mã này trả lời câu hỏi cải thiện giá trị lâu dài của nó.
rollstuhlfahrer

1
Tôi rất xin lỗi! Tôi mới bắt đầu, Stack Overflow và tôi không biết cách thêm tóm tắt ....
Luke Willey

Nhấp vào liên kết "Chỉnh sửa" bên dưới bài đăng của bạn và thêm tóm tắt, sau đó lưu lại.
Robert Columbia

1

trung gian đấu giá:

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] + data[m+1]) / 2
    return med

1

Trong trường hợp bạn cần thêm thông tin về việc phân phối danh sách của mình, phương pháp phân vị có thể sẽ hữu ích. Và giá trị trung bình tương ứng với phân vị thứ 50 của danh sách:

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 

1

Một hàm đơn giản để trả về trung vị của danh sách đã cho:

def median(lsts):
        if len(lsts)%2 == 0:  #Checking if the length is even
            return (lsts[len(lsts)//2] + lsts[(len(lsts) - 1) //2]) //2 # Applying formula which is sum of middle two divided by 2
            
        else:
            return lsts[len(lsts)//2] # If length is odd then get middle value
            
        
median([2,3,5,6,10]) #Calling function

nếu bạn muốn sử dụng thư viện, bạn chỉ cần làm;

import statistics

statistics.median([9, 12, 20, 21, 34, 80])

0
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]))

0

Một cách tiếp cận tổng quát hơn cho trung vị (và phần trăm) sẽ là:

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

-2

Đây là cách tẻ nhạt để tìm trung vị mà không sử dụng medianhàm:

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

Là loại bong bóng này? Tại sao?
Ry-

Tại sao bạn trao đổi giá trị?
ravi tanwar

-3

Nó rất đơn giản;

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

Và bạn có thể sử dụng giá trị trả về như thế này median = median(anyList)


1
Median yêu cầu bạn sắp xếp mảng của bạn trước khi bạn tìm thấy điểm giữa.
Saurabh Jain

sListtrả về mảng đã sắp xếp Không trả lại trung vị
Farhan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.