Recherche de la médiane de la liste en Python

181

Comment trouvez-vous la médiane d'une liste en Python? La liste peut être de n'importe quelle taille et les nombres ne sont pas garantis d'être dans un ordre particulier.

Si la liste contient un nombre pair d'éléments, la fonction doit renvoyer la moyenne des deux éléments du milieu.

Voici quelques exemples (triés à des fins d'affichage):

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
MandrinsPlace
la source
9
Les réponses ici sont bonnes, donc je pense que je veux que ce soit à peu près une réponse canonique pour trouver des médianes, en grande partie pour que je puisse fermer cela . Notez que cette question a 30 mille vues. J'apprécierais si cette question n'était pas fermée ou ignorée de quelque manière que ce soit afin qu'elle puisse rester sur les résultats de recherche et aspirer ces vues à la place.
Veedrac

Réponses:

214

Python 3.4 a statistics.median:

Renvoie la médiane (valeur médiane) des données numériques.

Lorsque le nombre de points de données est impair, renvoie le point de données du milieu. Lorsque le nombre de points de données est pair, la médiane est interpolée en prenant la moyenne des deux valeurs médianes:

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

Usage:

import statistics

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

statistics.median(items)
#>>> 3

Il est également très prudent avec les types:

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

from decimal import Decimal
statistics.median(map(Decimal, items))
#>>> Decimal('3')
Veedrac
la source
Parfait, j'ai travaillé pour moi pour l'ajouter à pip3 install itunizerpour ajouter des données médianes aux résultats de la requête. Cheers
jamescampbell
Que faire si vous voulez trouver la médiane d'un tableau trié. Vous ne pouvez donc pas utiliser la fonction intégrée statistics.median car elle ralentira lors du tri à nouveau
GilbertS
2
@GilbertS Ensuite, regardez l'élément du milieu ou faites la moyenne des deux éléments du milieu.
Veedrac le
163

(Marche avec ):

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

Pour , utilisez statistics.median:

>>> from statistics import median
>>> median([5, 2, 3, 8, 9, -2])
4.0
AJ Uppal
la source
9
Bien qu'il n'écrit pas une fonction, c'est toujours une solution plus "pythonique" à mon avis
dartdog
6
@dartdog Pas vraiment; il est déconseillé de forcer à un tableau Numpy sans raison valable. Vous avez forcé les types et, pire, perdu le support des types arbitraires.
Veedrac
1
Points pris, utiles.
dartdog
3
La fonction est cependant beaucoup plus laborieuse qu'elle ne devrait l'être.
Martijn Pieters
3
PEP 450 est un bon argument contre le fait de ne pas utiliser de bibliothèque. Vous finirez par faire une erreur.
Alex Harvey le
51

La fonction sorted () est très utile pour cela. Utilisez la fonction triée pour classer la liste, puis renvoyez simplement la valeur du milieu (ou faites la moyenne des deux valeurs du milieu si la liste contient un nombre pair d'éléments).

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
la source
C'est très inefficace cependant: le tri est beaucoup plus de travail dans le pire des cas (Theta (n lg n)) que la sélection de la médiane (Theta (n)) ...
Jeremy
12

Voici une solution plus propre:

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

Remarque: la réponse a été modifiée pour intégrer la suggestion dans les commentaires.

Batuhan Ulug
la source
7
float(sum(…) / 2)devrait être remplacé par sum(…) / 2.0; sinon, si sum(…)est un entier, vous obtiendrez une version flottante du quotient entier. Par exemple: float(sum([3, 4]) / 2)est 3.0, mais sum([3, 4]) / 2.0est 3.5.
musiphil
Pour être complet, @musiphil: uniquement en python 2, et uniquement si vous ne l'avez pas fait from __future__ import division.
Chris L.Barnes
11

Vous pouvez essayer l' algorithme quickselect si des durées d'exécution moyennes plus rapides sont nécessaires. Quickselect a des performances de cas moyennes (et meilleures) O(n), bien qu'il puisse se terminer par O(n²)une mauvaise journée.

Voici une implémentation avec un pivot choisi au hasard:

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)

Vous pouvez facilement transformer cela en une méthode pour trouver des médianes:

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

C'est très peu optimisé, mais il est peu probable que même une version optimisée surclasse Tim Sort (intégré à CPython sort) car c'est vraiment rapide . J'ai déjà essayé et j'ai perdu.

Veedrac
la source
Alors pourquoi même y penser si sort () est plus rapide?
Max
@Max Si vous utilisez PyPy, ou un type que vous ne pouvez pas sortfacilement, ou si vous souhaitez écrire une extension C pour la vitesse, etc.
Veedrac
10

Bien sûr, vous pouvez utiliser des fonctions intégrées, mais si vous souhaitez créer les vôtres, vous pouvez faire quelque chose comme ça. L'astuce ici est d'utiliser l'opérateur ~ qui retourne un nombre positif en négatif. Par exemple ~ 2 -> -3 et l'utilisation de la valeur négative dans la liste pour en Python comptera les éléments à partir de la fin. Donc, si vous avez mid == 2, cela prendra le troisième élément du début et le troisième élément de la fin.

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

Vous pouvez utiliser le list.sortpour éviter de créer de nouvelles listes avec sortedet trier les listes en place.

De plus, vous ne devez pas utiliser listcomme nom de variable car il masque la propre liste 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
la source
5
Les fonctions utilitaires simples ne devraient probablement pas muter les arguments (surtout si le nom de la fonction est un nom IMO). Utiliser également trié sur .sort () signifie que l'argument n'a pas besoin d'être une liste. Cela peut être n'importe quel itérateur.
Will S
1
Mon point concernait la fonction de mutation de la liste. J'ai mentionné le soutien de tout itérable comme un bel effet secondaire de tri, mais ce n'est pas son principal avantage. Pour ma part, je m'attendrais à ce que la médiane (liste) fonctionne comme presque toutes les autres fonctions intégrées ou mathématiques. next () mute, mais je ne peux penser à aucun autre. La mutation surprise est une douleur dans le cul pour le débogage.
Will S
@WillS, en quoi est-ce une surprise quand c'est documenté? Que faire si vous avez affaire à des données volumineuses ou si vous avez une quantité de mémoire limitée et que vous ne pouvez pas faire une copie de la liste, que faire alors?
Padraic Cunningham
2
Faites en sorte que la fonction s'attende à une liste triée et documentez-la. mylist.sort(); middle(mylist), mais c'est indéniablement une question de goût. Je pense simplement que la mutation en général devrait être réservée aux méthodes dans la mesure du possible. La raison pour laquelle list.sort () renvoie None au lieu de la liste elle-même est de rendre le comportement aussi évident et clair que possible. Tout cacher dans la documentation, c'est comme cacher des éléments en petits caractères.
Will S
Continuons cette discussion dans le chat .
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
warvariuc
la source
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
la source
1
Il semble que votre première ligne de code ait été laissée de côté, vous pouvez résoudre ce problème en éditant votre message et en indentant l'en-tête de la fonction avec 4 espaces.
Johan
4

J'ai posté ma solution à l' implémentation Python de l'algorithme "médiane des médianes" , qui est un peu plus rapide que d'utiliser sort (). Ma solution utilise 15 nombres par colonne, pour une vitesse ~ 5N qui est plus rapide que la vitesse ~ 10N d'utilisation de 5 nombres par colonne. La vitesse optimale est de ~ 4N, mais je peux me tromper.

À la demande de Tom dans son commentaire, j'ai ajouté mon code ici, pour référence. Je pense que la partie critique de la vitesse consiste à utiliser 15 nombres par colonne au lieu 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)
user5818263
la source
3

Voici ce que j'ai trouvé lors de cet exercice dans 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
la source
2

Fonction médiane

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
Юрий Мойдом Киев
la source
2

J'ai eu quelques problèmes avec les listes de valeurs flottantes. J'ai fini par utiliser un extrait de code de python3 statistics.median et fonctionne parfaitement avec des valeurs flottantes sans importations. la 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
Daniel
la source
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
la source
1

J'ai défini une fonction médiane pour une liste de nombres comme

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
la source
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
la source
3
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire concernant la raison et / ou la manière dont ce code répond à la question améliore sa valeur à long terme.
rollstuhlfahrer
1
Je suis vraiment désolé! Je viens de commencer, Stack Overflow, et je ne sais pas comment ajouter un résumé ....
Luke Willey
Cliquez sur le lien "Modifier" sous votre message et ajoutez un résumé, puis enregistrez-le.
Robert Columbia
1

médiane de fonction:

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
la source
1

Au cas où vous auriez besoin d'informations supplémentaires sur la distribution de votre liste, la méthode du centile vous sera probablement utile. Et une valeur médiane correspond au 50e centile d'une liste:

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
la source
1

Une fonction simple pour retourner la médiane de la liste donnée:

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 vous voulez utiliser la bibliothèque, vous pouvez simplement le faire;

import statistics

statistics.median([9, 12, 20, 21, 34, 80])
L'AG
la source
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
la source
0

Une approche plus généralisée pour la médiane (et les centiles) serait:

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
la source
-2

Voici le moyen fastidieux de trouver la médiane sans utiliser la medianfonction:

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
J'aime
la source
Est-ce une sorte de bulle? Pourquoi?
Ry-
pourquoi échangez-vous des valeurs?
ravi tanwar
-3

C'est très 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

Et vous pouvez utiliser la valeur de retour comme ceci median = median(anyList)

Farhan
la source
1
Median vous oblige à trier votre tableau avant de trouver le point médian.
Saurabh Jain
sListrenvoie le tableau trié. Ne renvoie pas la médiane
Farhan