Trovare la mediana dell'elenco in Python


181

Come si trova la mediana di un elenco in Python? L'elenco può essere di qualsiasi dimensione e non è garantito che i numeri siano in alcun ordine particolare.

Se l'elenco contiene un numero pari di elementi, la funzione dovrebbe restituire la media dei due centrali.

Ecco alcuni esempi (ordinati per scopi di visualizzazione):

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
Le risposte qui sono buone, quindi penso che voglio che questa sia all'incirca una risposta canonica per trovare i mediani, in gran parte per poterlo chiudere . Si noti che quella domanda ha 30 mila visualizzazioni. Gradirei se questa domanda non fosse chiusa o dimenticata in alcun modo in modo che potesse rimanere sui risultati di ricerca e aspirare invece quelle visualizzazioni.
Veedrac,

Risposte:


214

Python 3.4 ha statistics.median:

Restituisce la mediana (valore medio) dei dati numerici.

Quando il numero di punti dati è dispari, restituire il punto dati centrale. Quando il numero di punti dati è pari, la mediana viene interpolata prendendo la media dei due valori medi:

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

Uso:

import statistics

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

statistics.median(items)
#>>> 3

È abbastanza attento anche con i tipi:

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

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

Perfetto, ha funzionato per me aggiungerlo pip3 install itunizerper aggiungere dati mediani ai risultati della query. Saluti
jamescampbell,

Cosa succede se si desidera trovare la mediana di un array ordinato. Quindi non è possibile utilizzare la funzione built-in statistics.median perché rallenterà durante l'ordinamento di nuovo
GilbertS

2
@GilbertS Quindi guarda l'elemento centrale, o media i due centrali.
Veedrac,

163

(Lavora con ):

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

Per , utilizzare statistics.median:

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

9
Anche se non sta scrivendo una funzione, è ancora una soluzione più "pitonica" imho
dartdog

6
@dartdog Non proprio; non è consigliabile forzare un array Numpy senza una buona ragione. Hai costretto i tipi e, peggio ancora, hai perso il supporto per i tipi arbitrari.
Veedrac,

1
Punti presi, utili.
dartdog

3
Tuttavia, la funzione è molto più laboriosa di quanto debba essere.
Martijn Pieters

3
PEP 450 fa una buona argomentazione contro il non utilizzo di una libreria. Alla fine commetterai un errore.
Alex Harvey,

51

La funzione sort () è molto utile per questo. Utilizzare la funzione ordinata per ordinare l'elenco, quindi restituire semplicemente il valore medio (o fare la media dei due valori medi se l'elenco contiene una quantità pari di elementi).

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

Tuttavia è molto inefficiente: l'ordinamento è molto più lavoro nel caso peggiore (Theta (n lg n)) rispetto alla selezione della mediana (Theta (n)) ...
Jeremy,

12

Ecco una soluzione più pulita:

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

Nota: la risposta è stata modificata per includere suggerimenti nei commenti.


7
float(sum(…) / 2)dovrebbe essere sostituito con sum(…) / 2.0; altrimenti, se sum(…)è un numero intero, otterrai una versione float del quoziente intero. Ad esempio: float(sum([3, 4]) / 2)è 3.0, ma sum([3, 4]) / 2.0è 3.5.
musiphil,

Per completezza, @musiphil: solo in Python 2 e solo se non lo hai fatto from __future__ import division.
Chris L. Barnes,

11

È possibile provare la quickselect algoritmo se sono necessari tempi caso medio più veloce in esecuzione. Quickselect ha prestazioni medie (e migliori) del caso O(n), sebbene possa finire O(n²)in una brutta giornata.

Ecco un'implementazione con un perno scelto casualmente:

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)

Puoi banalmente trasformarlo in un metodo per trovare mediane:

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

Questo è molto non ottimizzato, ma è improbabile che anche una versione ottimizzata supererà Tim Sort (integrato in CPython sort) perché è molto veloce . Ci ho provato prima e ho perso.


Quindi perché pensare anche a questo se sort () è più veloce?
Max

@Max Se stai usando PyPy, o qualche tipo che non puoi sortfacilmente, o sei disposto a scrivere un'estensione C per la velocità, ecc.
Veedrac,

10

Ovviamente puoi usare le funzioni integrate, ma se desideri crearne di tue puoi fare qualcosa del genere. Il trucco qui è usare l'operatore ~ ​​che cambia il numero positivo in negativo. Ad esempio ~ 2 -> -3 e l'utilizzo di negativo in per l'elenco in Python conterà gli elementi dalla fine. Quindi, se hai mid == 2, prenderà il terzo elemento dall'inizio e il terzo elemento dalla fine.

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

8

È possibile utilizzare il list.sortper evitare di creare nuovi elenchi con sortede ordinare gli elenchi in atto.

Inoltre, non dovresti usare listcome nome di variabile poiché ombreggia il proprio elenco di 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
Le semplici funzioni di utilità probabilmente non dovrebbero mutare alcun argomento (specialmente se il nome della funzione è un sostantivo IMO). Anche l'utilizzo di .sort () in ordine significa che l'argomento non deve essere un elenco. Potrebbe essere qualsiasi iteratore.
Will

1
Il mio punto era sulla funzione che muta l'elenco. Ho menzionato il supporto di qualsiasi iterabile come un piacevole effetto collaterale di smistamento, ma non è questo il vantaggio principale. Per quanto mi riguarda, mi aspetto che la mediana (elenco) funzioni come quasi tutti gli altri builtin o funzioni matematiche. next () muta, ma non riesco a pensare ad altri. La mutazione a sorpresa è un dolore nel culo per il debug.
Sarà l'

@WillS, come è una sorpresa quando è documentato? Che cosa succede se si ha a che fare con dati di grandi dimensioni o se si dispone di quantità limitate di memoria e non è possibile effettuare una copia dell'elenco, e poi?
Padraic Cunningham,

2
Fai in modo che la funzione si aspetti un elenco ordinato e documentalo. mylist.sort(); middle(mylist), ma poi è innegabilmente una questione di gusti. Penso solo che la mutazione in generale debba essere riservata ai metodi per quanto possibile. Il motivo list.sort () restituisce None anziché l'elenco stesso per rendere il comportamento il più ovvio e chiaro possibile. Nascondere tutto nella documentazione è come nascondere cose in caratteri piccoli.
Will 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
Sembra che la tua prima riga di codice sia stata lasciata fuori, puoi risolverlo modificando il tuo post e indentando l'intestazione della funzione con 4 spazi.
Johan,

4

Ho pubblicato la mia soluzione all'implementazione Python dell'algoritmo "median of medians" , che è un po 'più veloce rispetto all'utilizzo di sort (). La mia soluzione utilizza 15 numeri per colonna, per una velocità ~ 5 N che è più veloce della velocità ~ 10 N dell'utilizzo di 5 numeri per colonna. La velocità ottimale è di ~ 4 N, ma potrei sbagliarmi.

Su richiesta di Tom nel suo commento, ho aggiunto il mio codice qui, come riferimento. Credo che la parte critica per la velocità stia usando 15 numeri per colonna, anziché 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

Ecco cosa mi è venuto in mente durante questo esercizio in 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

Funzione mediana

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

Ho avuto dei problemi con gli elenchi dei valori float. Ho finito per usare uno snippet di codice da python3 statistics.median e funziona perfettamente con valori float senza importazioni. fonte

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

Ho definito una funzione mediana per un elenco di numeri come

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
Mentre questo codice può rispondere alla domanda, fornendo un contesto aggiuntivo riguardo al perché e / o al modo in cui questo codice risponde alla domanda migliora il suo valore a lungo termine.
rollstuhlfahrer,

1
Mi dispiace molto! Ho appena iniziato, Stack Overflow, e non so come aggiungere un riepilogo ....
Luke Willey,

Fai clic sul link "Modifica" sotto il tuo post e aggiungi un riepilogo, quindi salva.
Robert Columbia,

1

mediana della funzione:

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

Nel caso abbiate bisogno di ulteriori informazioni sulla distribuzione del vostro elenco, il metodo percentile sarà probabilmente utile. E un valore mediano corrisponde al 50 ° percentile di un elenco:

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

Una semplice funzione per restituire la mediana dell'elenco indicato:

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

se vuoi usare la libreria puoi semplicemente farlo;

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

Un approccio più generalizzato per la mediana (e i percentili) sarebbe:

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

Ecco il modo noioso per trovare la mediana senza usare la medianfunzione:

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

Questa bolla è ordinata? Perché?
Ry-

perché stai scambiando valori?
Ravi Tanwar,

-3

È molto semplice;

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

E puoi usare il valore di ritorno in questo modo median = median(anyList)


1
La mediana richiede di ordinare l'array prima di trovare il punto medio.
Saurabh Jain,

sListrestituisce l'array ordinato. Non restituisce la mediana
Farhan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.