Encontrar la mediana de la lista en Python

181

¿Cómo encuentras la mediana de una lista en Python? La lista puede ser de cualquier tamaño y no se garantiza que los números estén en ningún orden en particular.

Si la lista contiene un número par de elementos, la función debería devolver el promedio de los dos medios.

Aquí hay algunos ejemplos (ordenados para mostrar):

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
ChucksPlace
fuente
9
Las respuestas aquí son buenas, así que creo que quiero que esto sea más o menos una respuesta canónica para encontrar medianas, en gran parte para poder cerrar esto . Tenga en cuenta que esa pregunta tiene 30 mil visitas. Agradecería si esta pregunta no se cerró o no se ocultó de alguna manera para que pueda permanecer en los resultados de búsqueda y absorber esas vistas.
Veedrac

Respuestas:

214

Python 3.4 tiene statistics.median:

Devuelve la mediana (valor medio) de los datos numéricos.

Cuando el número de puntos de datos es impar, devuelva el punto de datos medio. Cuando el número de puntos de datos es par, la mediana se interpola tomando el promedio de los dos valores medios:

>>> 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

También es bastante cuidadoso con los tipos:

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

from decimal import Decimal
statistics.median(map(Decimal, items))
#>>> Decimal('3')
Veedrac
fuente
Perfecto, funcionó para mí agregarlo pip3 install itunizerpara agregar datos medianos a los resultados de la consulta. Saludos
jamescampbell
¿Qué sucede si desea encontrar la mediana de una matriz ordenada? Por lo tanto, no puede usar estadísticas incorporadas en la
función.median
2
@ GilbertS Luego mira el elemento del medio, o promedia los dos del medio.
Veedrac
163

(Funciona 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

por , use statistics.median:

>>> from statistics import median
>>> median([5, 2, 3, 8, 9, -2])
4.0
AJ Uppal
fuente
9
Si bien no está escribiendo una función, sigue siendo una solución más "pitónica" en mi opinión
dartdog
66
@dartdog No realmente; No es aconsejable forzar una matriz Numpy sin una buena razón. Ha forzado tipos y, lo que es peor, ha perdido el soporte para tipos arbitrarios.
Veedrac
1
Puntos tomados, útiles.
dartdog
3
Sin embargo, la función es mucho más laboriosa de lo que debe ser.
Martijn Pieters
3
PEP 450 hace un buen argumento en contra de no usar una biblioteca. Eventualmente cometerás un error.
Alex Harvey
51

La función sorted () es muy útil para esto. Use la función ordenada para ordenar la lista, luego simplemente devuelva el valor medio (o promedie los dos valores medios si la lista contiene una cantidad par de elementos).

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
swolfe
fuente
Sin embargo, es muy ineficiente: ordenar es mucho más trabajo en el peor de los casos (Theta (n lg n)) que seleccionar la mediana (Theta (n)) ...
Jeremy
12

Aquí hay una solución más limpia:

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 respuesta cambió para incorporar sugerencias en los comentarios.

Batuhan Ulug
fuente
77
float(sum(…) / 2)debe ser reemplazado con sum(…) / 2.0; de lo contrario, si sum(…)es un entero, obtendrá una versión flotante del cociente entero. Por ejemplo: float(sum([3, 4]) / 2)es 3.0, pero sum([3, 4]) / 2.0es 3.5.
musiphil
Para completar, @musiphil: solo en Python 2, y solo si no lo has hecho from __future__ import division.
Chris L. Barnes
11

Puede probar el algoritmo de selección rápida si se necesitan tiempos de ejecución de caso promedio más rápidos. Quickselect tiene un rendimiento promedio (y el mejor) de los casos O(n), aunque puede terminar O(n²)en un mal día.

Aquí hay una implementación con un pivote elegido al azar:

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)

Puede convertir esto trivialmente en un método para encontrar medianas:

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

Esto es muy poco optimizado, pero no es probable que incluso una versión optimizada supere a Tim Sort (incorporado en CPython sort) porque es realmente rápido . Lo intenté antes y perdí.

Veedrac
fuente
Entonces, ¿por qué pensar en esto si sort () es más rápido?
Max
@Max Si está utilizando PyPy, o algún tipo que no puede sortfácilmente, o está dispuesto a escribir una extensión C para velocidad, etc.
Veedrac
10

Por supuesto, puede usar funciones integradas, pero si desea crear las suyas propias, puede hacer algo como esto. El truco aquí es usar el operador ~ que cambia el número positivo a negativo. Por ejemplo, ~ 2 -> -3 y el uso de negativo en la lista en Python contará los elementos desde el final. Entonces, si tiene mid == 2, entonces tomará un tercer elemento desde el principio y un tercer elemento desde el final.

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

Puede usar el list.sortpara evitar crear nuevas listas sortedy ordenar las listas en su lugar.

Además, no debe usarse listcomo un nombre de variable ya que sombrea la propia lista de 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]
Padraic Cunningham
fuente
55
Las funciones de utilidad simples probablemente no deberían mutar ningún argumento (especialmente si el nombre de la función es un nombre IMO). También usar sorted over .sort () significa que el argumento no tiene que ser una lista. Podría ser cualquier iterador.
Se S
1
Mi punto era sobre la función que muta la lista. Mencioné apoyar cualquier iterable como un buen efecto secundario de ordenado, pero ese no es su principal beneficio. Por mi parte, esperaría que la mediana (lista) funcione como casi todas las demás funciones integradas o matemáticas. next () muta, pero no puedo pensar en ningún otro. La mutación sorpresa es un dolor en el culo para la depuración.
Se S
@WillS, ¿cómo es una sorpresa cuando se documenta? ¿Qué sucede si se trata de datos de gran tamaño o si tiene cantidades restringidas de memoria y no puede hacer una copia de la lista, entonces qué?
Padraic Cunningham
2
Haga que la función espere una lista ordenada y documente eso. mylist.sort(); middle(mylist), pero sin duda es una cuestión de gustos. Solo creo que la mutación en general debería reservarse para métodos en la medida de lo posible. La razón por la que list.sort () devuelve None en lugar de la lista en sí es hacer que el comportamiento sea lo más obvio y claro posible. Ocultar todo en la documentación es como ocultar cosas en letra pequeña.
Se S
Continuemos esta discusión en el chat .
Se 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
warvariuc
fuente
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
Bulent
fuente
1
Parece que su primera línea de código quedó fuera, puede resolver esto editando su publicación e sangrando el encabezado de la función con 4 espacios.
Johan
4

Publiqué mi solución en la implementación de Python del algoritmo "mediana de medianas" , que es un poco más rápido que usar sort (). Mi solución usa 15 números por columna, para una velocidad ~ 5N que es más rápida que la velocidad ~ 10N de usar 5 números por columna. La velocidad óptima es ~ 4N, pero podría estar equivocado al respecto.

Por solicitud de Tom en su comentario, agregué mi código aquí, como referencia. Creo que la parte crítica para la velocidad es usar 15 números por columna, en lugar de 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)
usuario5818263
fuente
3

Aquí lo que se me ocurrió durante este ejercicio en 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])
BynderRox
fuente
2

función 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
Юрий Мойдом Киев
fuente
2

Tuve algunos problemas con las listas de valores flotantes. Terminé usando un fragmento de código de python3 statistics.median y funciona perfectamente con valores flotantes sin importaciones. fuente

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
Daniel
fuente
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])
vk123
fuente
1

Definí una función mediana para una lista de números como

def median(numbers):
    return (sorted(numbers)[int(round((len(numbers) - 1) / 2.0))] + sorted(numbers)[int(round((len(numbers) - 1) // 2.0))]) / 2.0
Fred Beck
fuente
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])
Luke Willey
fuente
3
Si bien este código puede responder a la pregunta, proporcionar un contexto adicional con respecto a por qué y / o cómo este código responde a la pregunta mejora su valor a largo plazo.
rollstuhlfahrer
1
¡Lo siento mucho! Acabo de empezar, Stack Overflow, y no sé cómo agregar un resumen ...
Luke Willey
Haga clic en el enlace "Editar" debajo de su publicación y agregue un resumen, luego guarde.
Robert Columbia
1

mediana de la función:

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
fati
fuente
1

En caso de que necesite información adicional sobre la distribución de su lista, el método de percentil probablemente será útil. Y un valor medio corresponde al percentil 50 de una lista:

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 
Gabriel123
fuente
1

Una función simple para devolver la mediana de la lista dada:

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

si quieres usar la biblioteca, simplemente puedes hacerlo;

import statistics

statistics.median([9, 12, 20, 21, 34, 80])
El AG
fuente
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]))
sim
fuente
0

Un enfoque más generalizado para la mediana (y los percentiles) sería:

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
conmak
fuente
-2

Aquí está la forma tediosa de encontrar la mediana sin usar la medianfunción:

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
Me gusta
fuente
¿Es este tipo de burbuja? ¿Por qué?
Ry-
¿Por qué estás intercambiando valores?
ravi tanwar
-3

Es muy simple;

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

Y puede usar el valor de retorno de esta manera median = median(anyList)

Farhan
fuente
1
La mediana requiere que ordene su matriz antes de encontrar el punto medio.
Saurabh Jain
sListDevuelve la matriz ordenada. No devuelve la mediana
Farhan