Пошук медіани списку в Python


181

Як ви знайдете медіану списку в 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


9
Відповіді тут хороші, тому я думаю, що я хочу, щоб це було приблизно канонічною відповіддю для пошуку медіанів, в основному, щоб я міг це закрити . Зауважимо, що це питання має 30 тисяч переглядів. Буду вдячний, якщо це питання не було закрито чи не було уникнуто жодним чином, щоб воно могло залишатися в результатах пошуку і замість цього висмоктувати ці погляди.
Ведрак

Відповіді:


213

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щоб додати медіанні дані до результатів запиту. Ура
jamescampbell

Що робити, якщо ви хочете знайти медіану відсортованого масиву. Таким чином, ви не можете використовувати вбудовану функцію statistics.median, оскільки вона сповільниться під час сортування
GilbertS

2
@GilbertS Потім подивіться на середній елемент, або середній середній два.
Ведрак

163

(Працює с ):

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

Для , використовуйте statistics.median:

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

9
Поки він не пише функції, це все-таки "пітонічне" рішення imho
dartdog

6
@dartdog Не дуже; без поважних причин примушувати до масиву Numpy недоцільно. Ви змусили типів і, що ще гірше, втратили підтримку довільних типів.
Ведрак

1
Очки взяті, корисні.
дартдог

3
Однак функція набагато більш трудомістка, ніж повинна бути.
Martijn Pieters

3
PEP 450 робить хороший аргумент проти використання бібліотеки. Ви зрештою помилитесь.
Алекс Харві

51

Для цього дуже допомагає функція сортування (). Використовуйте відсортовану функцію для замовлення списку, а потім просто поверніть середнє значення (або середнє значення двох середніх значень, якщо список містить парну кількість елементів).

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)) ...
Jeremy

12

Ось більш чисте рішення:

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.0є 3.5.
musiphil

Для повноти @musiphil: лише в python 2, і лише якщо ви цього не зробили from __future__ import division.
Кріс Л. Барнс

11

Ви можете спробувати алгоритм швидкого вибору , якщо потрібні швидші середні регістри часу роботи. 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), оскільки це дуже швидко . Я раніше пробував і програв.


То чому б навіть думати про це, якщо сортування () швидше?
Макс

@Max Якщо ви використовуєте PyPy або якийсь тип, ви не можете sortлегко, або бажаєте написати розширення C для швидкості тощо
Veedrac,

10

Звичайно, ви можете використовувати функції побудови у функціях, але якщо ви хочете створити свою, ви можете зробити щось подібне. Хитрість тут полягає у використанні оператора ~, який переверне позитивне число на негативне. Наприклад, ~ 2 -> -3 і за допомогою мінус-in для списку в Python буде підраховано елементи з кінця. Отже, якщо у вас є середина == 2, то вона займе третій елемент з початку і третій елемент з кінця.

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

8

Ви можете використовувати цю функцію, 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]

5
Прості функції утиліти, ймовірно, не повинні мутувати жодних аргументів (особливо, якщо ім'я функції є іменником IMO). Також використання сортованого по .sort () означає, що аргумент не повинен бути списком. Це може бути будь-який ітератор.
Буде S

1
Моя думка стосувалася функції, що мутує список. Я згадав підтримати будь-який ітерабельний приємний побічний вплив на сортування, але це не головна користь. Я б, напевно, очікував, що медіана (список) працюватиме як майже всі інші вбудовані або математичні функції. next () мутує, але я не можу придумати жодних інших. Мутація сюрпризу - це біль у попі за налагодження.
Буде S

@WillS, як це несподіванка, коли це задокументовано? Що робити, якщо ви маєте справу з великими даними або у вас обмежена кількість пам'яті, і ви не можете зробити копію списку, що тоді?
Padraic Cunningham

2
Зробіть функцію очікувати відсортованим списком і документуйте це. mylist.sort(); middle(mylist), але тоді це, безперечно, справа смаку. Я просто думаю, що мутація взагалі повинна бути зарезервована для методів, наскільки це можливо. Причина list.sort () повертає None замість самого списку полягає в тому, щоб зробити поведінку максимально очевидною і зрозумілою. Ховати все в документації - це як приховувати речі дрібним шрифтом.
Буде 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
Схоже, ваш перший рядок коду залишився поза межами, ви можете вирішити це, відредагувавши свій пост і відступивши заголовок функції на 4 пробіли.
Йоган

4

Я розмістив своє рішення на реалізації Python алгоритму "медіана медіанів" , який трохи швидше, ніж використання sort (). Моє рішення використовує 15 чисел на стовпчик, для швидкості ~ 5N, що швидше, ніж швидкість ~ 10N використання 5 чисел на стовпець. Оптимальна швидкість - ~ 4N, але я можу помилитися з цим.

За запитом Тома у своєму коментарі я додав свій код сюди, для довідки. Я вважаю, що критичною частиною швидкості є використання 15 чисел на стовпчик, а не 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

Ось що я придумав під час цієї вправи в 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

серединна функція

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

У мене виникли проблеми зі списками значень float. Я в кінцевому підсумку використовував фрагмент коду з python3 statistics.median і ідеально працює з знаками float без імпорту. джерело

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

Медіанну функцію для списку чисел я визначив як

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
Хоча цей код може відповісти на питання, надаючи додатковий контекст стосовно того, чому та / або як цей код відповідає на питання, покращує його довгострокове значення.
rollstuhlfahrer

1
Мені дуже шкода! Я щойно почав, Переповнення стека, і я не знаю, як додати резюме ....
Люк Віллі

Натисніть посилання "Редагувати" під публікацією та додайте резюме, а потім збережіть.
Роберт Колумбія

1

медіана fuction:

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

Якщо вам потрібна додаткова інформація про розподіл вашого списку, метод процентилів, ймовірно, буде корисним. І середнє значення відповідає 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 

1

Проста функція повернення медіани даного списку:

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

якщо ви хочете використовувати бібліотеку, ви можете просто зробити це;

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

Більш узагальненим підходом для медіани (та відсотків) був би:

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

Ось нудний спосіб знайти медіану без використання 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

Це сорт міхура? Чому?
Ри-

чому ти міняєш значення?
ravi tanwar

-3

Це дуже просто;

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)


1
Median вимагає, щоб ви сортували масив, перш ніж знайти середину.
Саурах Джайн

sListповернути відсортований масив. Медіана не повертається
Фархан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.