การค้นหาค่ามัธยฐานของรายการใน 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,000ครั้ง ฉันยินดีถ้าคำถามนี้ไม่ได้ถูกปิดหรือลบล้างไม่ว่าในลักษณะใดเพื่อให้สามารถอยู่ในผลการค้นหาและทำให้มุมมองเหล่านั้นแย่ลงแทน
Veedrac

คำตอบ:


214

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

ถ้าคุณต้องการหาค่ามัธยฐานของอาร์เรย์ที่เรียงลำดับ ดังนั้นคุณจึงไม่สามารถใช้ฟังก์ชันสถิติในตัวได้เนื่องจากมันจะช้าลงในขณะที่จัดเรียงอีกครั้ง
GilbertS

2
@GilbertS จากนั้นดูองค์ประกอบกลางหรือค่าเฉลี่ยสองกลาง
Veedrac

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
ในขณะที่มันไม่ได้เขียนฟังก์ชั่นก็ยังคงเป็น "pythonic" การแก้ปัญหา IMHO
dartdog

6
@dartdog ไม่จริง ๆ ; ไม่สามารถบีบบังคับให้อาร์เรย์ Numpy โดยไม่มีเหตุผลที่ดี คุณได้บีบบังคับประเภทและยิ่งกว่านั้นการสนับสนุนประเภทโดยพลการ
Veedrac

1
คะแนนที่ได้รับมีประโยชน์
dartdog

3
แม้ว่าฟังก์ชั่นจะลำบากกว่าที่คิดไว้มาก
Martijn Pieters

3
PEP 450สร้างข้อโต้แย้งที่ดีว่าไม่ได้ใช้ห้องสมุด ในที่สุดคุณจะทำผิดพลาด
Alex Harvey

51

ฟังก์ชัน sort () มีประโยชน์มากสำหรับสิ่งนี้ ใช้ฟังก์ชั่นเรียงลำดับเพื่อเรียงลำดับรายการจากนั้นเพียงคืนค่ากลาง (หรือเฉลี่ยสองค่ากลางหากรายการมีจำนวนองค์ประกอบเท่ากัน)

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: เฉพาะในหลาม 2 from __future__ import divisionและเฉพาะถ้าคุณยังไม่ได้ทำ
Chris L. Barnes

11

คุณสามารถลอง อัลกอริธึมquickselectหากต้องการเวลาในการรันตัวพิมพ์เล็กและตัวพิมพ์ใหญ่โดยเร็ว 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) เพราะมันเร็วมาก ฉันลองมาก่อนแล้วก็แพ้


ดังนั้นทำไมถึงคิดเกี่ยวกับเรื่องนี้ถ้า sort () เร็วขึ้น?
Max

@Max หากคุณกำลังใช้ PyPy หรือชนิดบางอย่างที่คุณไม่สามารถsortได้อย่างง่ายดายหรือไม่เต็มใจที่จะเขียนนามสกุล C สำหรับความเร็ว ฯลฯ
Veedrac

10

แน่นอนว่าคุณสามารถใช้ build in function แต่ถ้าคุณต้องการสร้างของคุณเองคุณสามารถทำสิ่งนี้ได้ เคล็ดลับที่นี่คือการใช้ตัวดำเนินการ ~ ที่พลิกจำนวนบวกเป็นลบ ตัวอย่างเช่น ~ 2 -> -3 และการใช้ค่าลบสำหรับรายการใน Python จะนับรายการจากท้ายสุด ดังนั้นถ้าคุณมีค่ากลาง == 2 มันจะใช้องค์ประกอบที่สามจากจุดเริ่มต้นและรายการที่สามจากจุดสิ้นสุด

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

8

คุณสามารถใช้list.sortเพื่อหลีกเลี่ยงการสร้างรายการใหม่ด้วยsortedและเรียงลำดับรายการในสถานที่

นอกจากนี้คุณไม่ควรใช้listเป็นชื่อตัวแปรเป็นมันเงาของตัวเองหลามรายการ

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 () หมายความว่าอาร์กิวเมนต์ไม่จำเป็นต้องเป็นรายการ มันอาจเป็นตัววนซ้ำใด ๆ ก็ได้
Will S

1
ประเด็นของฉันเกี่ยวกับฟังก์ชั่นการปิดรายการ ฉันกล่าวถึงการสนับสนุนใด ๆ iterable เป็นผลข้างเคียงที่ดีของการเรียงลำดับ แต่นั่นไม่ใช่ประโยชน์หลัก ฉันคนหนึ่งคาดหวังว่าค่ามัธยฐาน (รายการ) จะทำงานเหมือนกับ builtins หรือฟังก์ชันทางคณิตศาสตร์อื่น ๆ เกือบทั้งหมด next () กลายพันธุ์ แต่ฉันไม่สามารถนึกถึงผู้อื่นได้ การกลายพันธุ์ที่ทำให้ประหลาดใจคือความเจ็บปวดในตูดเพื่อแก้ไขข้อบกพร่อง
Will S

@ WillS มันจะแปลกใจอย่างไรเมื่อมีการบันทึกไว้? ถ้าคุณจัดการกับข้อมูลขนาดใหญ่หรือคุณมีหน่วยความจำในปริมาณที่ จำกัด และคุณไม่สามารถทำสำเนาของรายการได้จะทำอย่างไร?
Padraic Cunningham

2
ทำให้ฟังก์ชั่นที่คาดว่าจะเรียงลำดับรายการและเอกสารที่ mylist.sort(); middle(mylist)แต่มันก็เป็นเรื่องของรสชาติอย่างปฏิเสธไม่ได้ ฉันแค่คิดว่าการกลายพันธุ์โดยทั่วไปควรจะสงวนไว้สำหรับวิธีการเท่าที่จะทำได้ เหตุผล list.sort () ส่งคืน None แทนที่จะเป็นรายการเองเพื่อทำให้พฤติกรรมชัดเจนและชัดเจนที่สุด การซ่อนทุกอย่างในเอกสารเหมือนกับการซ่อนสิ่งต่าง ๆ ในสิ่งพิมพ์ขนาดเล็ก
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
ดูเหมือนว่ารหัสบรรทัดแรกของคุณจะหมดคุณสามารถแก้ไขได้โดยแก้ไขโพสต์และเยื้องส่วนหัวของฟังก์ชันด้วยช่องว่าง 4 ช่อง
Johan

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

ฉันมีปัญหาบางอย่างกับรายการค่าลอย ฉันสิ้นสุดการใช้โค้ดจาก python3 statistics.medianและมีการทำงานที่สมบูรณ์แบบที่มีค่าลอยโดยไม่ต้องนำเข้า แหล่ง

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
ฉันเสียใจมาก! ฉันเพิ่งเริ่มต้น, Stack มากเกินและผมไม่ทราบวิธีการเพิ่มสรุป ....
ลุควิลลี

คลิกลิงก์ "แก้ไข" ใต้โพสต์ของคุณและเพิ่มข้อมูลสรุปจากนั้นบันทึก
Robert Columbia

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

ฟองเรียงนี้หรือไม่ ทำไม?
Ry-

ทำไมคุณถึงแลกเปลี่ยนค่า
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
ค่ามัธยฐานกำหนดให้คุณต้องเรียงลำดับอาร์เรย์ของคุณก่อนที่จะหาจุดกึ่งกลาง
Saurabh Jain

sListส่งคืนอาร์เรย์ที่เรียงลำดับ ไม่คืนค่ามัธยฐาน
Farhan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.