ฉันจะคำนวณเปอร์เซ็นไทล์ด้วย python / numpy ได้อย่างไร


214

มีวิธีที่สะดวกในการคำนวณเปอร์เซนต์ไทล์สำหรับลำดับหรืออาร์เรย์มิติเดียวหรือไม่?

ฉันกำลังมองหาบางสิ่งที่คล้ายกับฟังก์ชันเปอร์เซ็นต์ไทล์ของ Excel

ฉันดูในการอ้างอิงสถิติของ NumPy และไม่พบสิ่งนี้ ทั้งหมดที่ฉันสามารถหาได้คือค่ามัธยฐาน (เปอร์เซ็นไทล์ที่ 50) แต่ไม่ใช่สิ่งที่เฉพาะเจาะจงมากขึ้น


คำถามที่เกี่ยวข้องกับการคำนวณเปอร์เซ็นต์ไทล์จากความถี่: stackoverflow.com/questions/25070086/…
newtover

คำตอบ:


282

คุณอาจสนใจแพ็คเกจแพ็คเกจSciPy มันมีฟังก์ชั่นเปอร์เซนต์ที่คุณติดตามและสินค้าอื่น ๆ อีกมากมาย

percentile() สามารถใช้ได้ในnumpyเกินไป

import numpy as np
a = np.array([1,2,3,4,5])
p = np.percentile(a, 50) # return 50th percentile, e.g median.
print p
3.0

ตั๋วนี้ทำให้ฉันเชื่อว่าพวกเขาจะไม่รวมpercentile()เข้ากับ numpy เร็ว ๆ นี้


2
ขอบคุณ! นั่นคือสิ่งที่ซ่อนอยู่ ฉันตระหนักถึง scipy แต่ฉันเดาว่าฉันคิดว่าสิ่งง่าย ๆ เช่นเปอร์เซนต์จะถูกสร้างขึ้นให้ดูเป็นก้อน
Uri

16
ถึงตอนนี้ฟังก์ชั่นเปอร์เซ็นไทล์มีอยู่ใน numpy: docs.scipy.org/doc/numpy/reference/generated/…
Anaphory

1
คุณสามารถใช้มันเป็นฟังก์ชั่นการรวมเช่นการคำนวณเปอร์เซ็นต์สิบของแต่ละกลุ่มของคอลัมน์ค่าตามคีย์ใช้df.groupby('key')[['value']].agg(lambda g: np.percentile(g, 10))
patricksurry

1
โปรดทราบว่า SciPy แนะนำให้ใช้ np.percentile สำหรับ NumPy 1.9 ขึ้นไป
timdiels

73

โดยวิธีการที่มีการใช้งานจริงของงูหลามฟังก์ชั่นเปอร์เซ็นต์ไทล์ในกรณีที่ไม่ต้องการขึ้นอยู่กับ scipy ฟังก์ชั่นถูกคัดลอกด้านล่าง:

## {{{ http://code.activestate.com/recipes/511478/ (r1)
import math
import functools

def percentile(N, percent, key=lambda x:x):
    """
    Find the percentile of a list of values.

    @parameter N - is a list of values. Note N MUST BE already sorted.
    @parameter percent - a float value from 0.0 to 1.0.
    @parameter key - optional key function to compute value from each element of N.

    @return - the percentile of the values
    """
    if not N:
        return None
    k = (len(N)-1) * percent
    f = math.floor(k)
    c = math.ceil(k)
    if f == c:
        return key(N[int(k)])
    d0 = key(N[int(f)]) * (c-k)
    d1 = key(N[int(c)]) * (k-f)
    return d0+d1

# median is 50th percentile.
median = functools.partial(percentile, percent=0.5)
## end of http://code.activestate.com/recipes/511478/ }}}

54
ฉันเป็นผู้เขียนสูตรอาหารดังกล่าว ผู้แสดงความคิดเห็นใน ASPN ได้ชี้ให้เห็นว่ารหัสเดิมมีข้อบกพร่อง สูตรควรเป็น d0 = key (N [int (f)]) * (ck); d1 = คีย์ (N [int (c)]) * (kf) มันได้รับการแก้ไขใน ASPN
Wai Yip Tung

1
จะpercentileรู้ได้อย่างไรว่าจะใช้เพื่อNอะไร? ไม่ได้ระบุไว้ในการเรียกใช้ฟังก์ชัน
Richard

14
สำหรับผู้ที่ไม่ได้อ่านรหัสก่อนที่จะใช้จะต้องมีการเรียง N
kevin

ฉันสับสนกับการแสดงออกแลมบ์ดา มันทำอะไรและทำยังไง? ฉันรู้ว่าแลมบ์ดาแสดงออกอะไรฉันจึงไม่ถามว่าแลมบ์ดาคืออะไร ฉันถามว่าแลมบ์ดานิพจน์เฉพาะนี้ทำอะไรได้บ้างและมันทำยังไงทีละขั้น? ขอบคุณ!
dsanchez

ฟังก์ชันแลมบ์ดาให้คุณแปลงข้อมูลNก่อนคำนวณเปอร์เซ็นต์ไทล์ สมมติว่าคุณจะมีรายชื่อของ tuples N = [(1, 2), (3, 1), ..., (5, 1)]และคุณต้องการที่จะได้รับเปอร์เซ็นต์ของแรกองค์ประกอบของ tuples key=lambda x: x[0]แล้วคุณเลือก นอกจากนี้คุณยังสามารถนำการแปลง (การเปลี่ยนคำสั่ง) ไปใช้กับองค์ประกอบรายการก่อนคำนวณเปอร์เซ็นต์ไทล์
Elias Strehle


19

ต่อไปนี้เป็นวิธีการทำโดยไม่ใช้ numpy ใช้เพียง python ในการคำนวณเปอร์ไทล์

import math

def percentile(data, percentile):
    size = len(data)
    return sorted(data)[int(math.ceil((size * percentile) / 100)) - 1]

p5 = percentile(mylist, 5)
p25 = percentile(mylist, 25)
p50 = percentile(mylist, 50)
p75 = percentile(mylist, 75)
p95 = percentile(mylist, 95)

2
ใช่คุณต้องเรียงลำดับรายการก่อนหน้า: mylist = sort (... )
Ashkan

12

คำจำกัดความของเปอร์เซ็นไทล์ที่ฉันมักจะเห็นว่าเป็นผลลัพธ์จากค่าจากรายการด้านล่างซึ่งพบค่า P เปอร์เซ็นต์ของค่า ... ซึ่งหมายความว่าผลลัพธ์ต้องมาจากชุดไม่ใช่การแก้ไขระหว่างองค์ประกอบชุด เพื่อให้ได้คุณสามารถใช้ฟังก์ชันที่ง่ายขึ้น

def percentile(N, P):
    """
    Find the percentile of a list of values

    @parameter N - A list of values.  N must be sorted.
    @parameter P - A float value from 0.0 to 1.0

    @return - The percentile of the values.
    """
    n = int(round(P * len(N) + 0.5))
    return N[n-1]

# A = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
# B = (15, 20, 35, 40, 50)
#
# print percentile(A, P=0.3)
# 4
# print percentile(A, P=0.8)
# 9
# print percentile(B, P=0.3)
# 20
# print percentile(B, P=0.8)
# 50

หากคุณต้องการรับค่าจากรายการที่ให้ไว้ที่หรือด้านล่างซึ่ง P เปอร์เซ็นต์ของค่าที่พบแล้วใช้การปรับเปลี่ยนง่าย ๆ นี้:

def percentile(N, P):
    n = int(round(P * len(N) + 0.5))
    if n > 1:
        return N[n-2]
    else:
        return N[0]

หรือด้วยการทำให้เข้าใจง่ายที่แนะนำโดย @ijustlovemath:

def percentile(N, P):
    n = max(int(round(P * len(N) + 0.5)), 2)
    return N[n-2]

ขอบคุณฉันยังคาดหวังเปอร์เซ็นไทล์ / ค่ามัธยฐานให้ผลลัพธ์ที่แท้จริงจากชุดและไม่แก้ไข
hansaplast

1
สวัสดี @mpounsett ขอบคุณสำหรับรหัสบน ทำไมเปอร์เซ็นต์ไทล์ของคุณถึงคืนค่าจำนวนเต็มเสมอ? ฟังก์ชันเปอร์เซ็นไทล์ควรส่งคืนไทล์ไทล์ไทล์ N ของรายการที่มีค่าซึ่งอาจเป็นจำนวนลอยได้เช่นกัน ยกตัวอย่างเช่น Excel ฟังก์ชันส่งกลับเปอร์เซนต์ต่อไปนี้สำหรับตัวอย่างส่วนบนของคุณ:PERCENTILE , , , 3.7 = percentile(A, P=0.3)0.82 = percentile(A, P=0.8)20 = percentile(B, P=0.3)42 = percentile(B, P=0.8)
marco

1
มันอธิบายในประโยคแรก คำจำกัดความทั่วไปของเปอร์เซ็นไทล์คือตัวเลขในซีรี่ย์ด้านล่างซึ่ง P เปอร์เซ็นของค่าในซีรีส์นั้นถูกค้นพบ เนื่องจากเป็นหมายเลขดัชนีของรายการในรายการจึงไม่สามารถลอยได้
mpounsett

สิ่งนี้ใช้ไม่ได้กับเปอร์เซ็นไทล์ 0 ของ ส่งคืนค่าสูงสุด การแก้ไขอย่างรวดเร็วคือการห่อn = int(...)ในmax(int(...), 1)ฟังก์ชั่น
ijustlovemath

เพื่ออธิบายชัดเจนคุณหมายถึงในตัวอย่างที่สอง? ฉันได้รับ 0 มากกว่าค่าสูงสุด ข้อผิดพลาดเป็นจริงในข้ออื่น .. ฉันพิมพ์หมายเลขดัชนีมากกว่าค่าที่ฉันตั้งใจจะ การตัดการกำหนด 'n' ในการเรียก max () จะแก้ไขได้เช่นกัน แต่คุณต้องการให้ค่าที่สองเป็น 2 ไม่ใช่ 1 จากนั้นคุณสามารถกำจัดโครงสร้าง if / else ทั้งหมดและเพียงพิมพ์ผลลัพธ์ของ N [n-2] เปอร์เซ็นไทล์ที่ 0 ทำงานได้ดีในตัวอย่างแรกส่งคืน '1' และ '15' ตามลำดับ
mpounsett

8

เริ่มต้นPython 3.8ไลบรารีมาตรฐานมาพร้อมกับquantilesฟังก์ชั่นซึ่งเป็นส่วนหนึ่งของstatisticsโมดูล:

from statistics import quantiles

quantiles([1, 2, 3, 4, 5], n=100)
# [0.06, 0.12, 0.18, 0.24, 0.3, 0.36, 0.42, 0.48, 0.54, 0.6, 0.66, 0.72, 0.78, 0.84, 0.9, 0.96, 1.02, 1.08, 1.14, 1.2, 1.26, 1.32, 1.38, 1.44, 1.5, 1.56, 1.62, 1.68, 1.74, 1.8, 1.86, 1.92, 1.98, 2.04, 2.1, 2.16, 2.22, 2.28, 2.34, 2.4, 2.46, 2.52, 2.58, 2.64, 2.7, 2.76, 2.82, 2.88, 2.94, 3.0, 3.06, 3.12, 3.18, 3.24, 3.3, 3.36, 3.42, 3.48, 3.54, 3.6, 3.66, 3.72, 3.78, 3.84, 3.9, 3.96, 4.02, 4.08, 4.14, 4.2, 4.26, 4.32, 4.38, 4.44, 4.5, 4.56, 4.62, 4.68, 4.74, 4.8, 4.86, 4.92, 4.98, 5.04, 5.1, 5.16, 5.22, 5.28, 5.34, 5.4, 5.46, 5.52, 5.58, 5.64, 5.7, 5.76, 5.82, 5.88, 5.94]
quantiles([1, 2, 3, 4, 5], n=100)[49] # 50th percentile (e.g median)
# 3.0

quantilesผลตอบแทนสำหรับการจัดจำหน่ายที่ได้รับdistรายชื่อของn - 1จุดตัดแยกnช่วงเวลา quantile (ส่วนหนึ่งของการdistเข้ามาในnช่วงเวลาที่ต่อเนื่องกับความน่าจะเป็นเท่ากัน):

stats.quantiles (dist, *, n = 4, method = 'exclusive')

ซึ่งnในกรณีของเรา ( percentiles) 100เป็น



2

ในการคำนวณเปอร์เซนต์ไทล์ของซีรีย์ให้รัน:

from scipy.stats import rankdata
import numpy as np

def calc_percentile(a, method='min'):
    if isinstance(a, list):
        a = np.asarray(a)
    return rankdata(a, method=method) / float(len(a))

ตัวอย่างเช่น:

a = range(20)
print {val: round(percentile, 3) for val, percentile in zip(a, calc_percentile(a))}
>>> {0: 0.05, 1: 0.1, 2: 0.15, 3: 0.2, 4: 0.25, 5: 0.3, 6: 0.35, 7: 0.4, 8: 0.45, 9: 0.5, 10: 0.55, 11: 0.6, 12: 0.65, 13: 0.7, 14: 0.75, 15: 0.8, 16: 0.85, 17: 0.9, 18: 0.95, 19: 1.0}

1

ในกรณีที่คุณต้องการคำตอบให้เป็นสมาชิกของแถวลำดับอินพุต:

เพียงเพื่อเพิ่มฟังก์ชั่นเปอร์เซ็นต์ไทล์ในจำนวนเป็นค่าเริ่มต้นคำนวณเอาท์พุทเป็นค่าเฉลี่ยถ่วงน้ำหนักเชิงเส้นของสองรายการที่อยู่ใกล้เคียงในเวกเตอร์การป้อนข้อมูล ในบางกรณีผู้คนอาจต้องการให้เปอร์เซ็นต์ไทล์คืนเป็นองค์ประกอบที่แท้จริงของเวกเตอร์ในกรณีนี้ตั้งแต่ v1.9.0 เป็นต้นไปคุณสามารถใช้ตัวเลือก "การแก้ไข" ด้วย "ต่ำ", "สูง" หรือ "ใกล้ที่สุด"

import numpy as np
x=np.random.uniform(10,size=(1000))-5.0

np.percentile(x,70) # 70th percentile

2.075966046220879

np.percentile(x,70,interpolation="nearest")

2.0729677997904314

รายการหลังเป็นรายการจริงในเวกเตอร์ในขณะที่รายการแรกคือการแก้ไขเชิงเส้นของรายการเวกเตอร์สองรายการที่ล้อมรอบเปอร์เซ็นต์ไทล์


0

สำหรับซีรีส์: ใช้อธิบายฟังก์ชั่น

สมมติว่าคุณมี df โดยมีคอลัมน์การขายและรหัสดังต่อไปนี้ คุณต้องการคำนวณเปอร์เซนต์ไทล์สำหรับการขายจากนั้นก็ใช้งานได้เช่นนี้

df['sales'].describe(percentiles = [0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1])

0.0: .0: minimum
1: maximum 
0.1 : 10th percentile and so on

0

วิธีที่สะดวกในการคำนวณเปอร์เซ็นต์ไทล์สำหรับลำดับหรือเมทริกซ์หนึ่งมิติคือการใช้ numpy.percentile < https://docs.scipy.org/doc/numpy/reference/generated/numpy.percentile.html > ตัวอย่าง:

import numpy as np

a = np.array([0,1,2,3,4,5,6,7,8,9,10])
p50 = np.percentile(a, 50) # return 50th percentile, e.g median.
p90 = np.percentile(a, 90) # return 90th percentile.
print('median = ',p50,' and p90 = ',p90) # median =  5.0  and p90 =  9.0

อย่างไรก็ตามหากมีค่า NaN ในข้อมูลของคุณฟังก์ชั่นด้านบนจะไม่เป็นประโยชน์ ฟังก์ชันที่แนะนำให้ใช้ในกรณีนั้นคือฟังก์ชัน numpy.nanpercentile < https://docs.scipy.org/doc/numpy/reference/generated/numpy.nanpercentile.html > function:

import numpy as np

a_NaN = np.array([0.,1.,2.,3.,4.,5.,6.,7.,8.,9.,10.])
a_NaN[0] = np.nan
print('a_NaN',a_NaN)
p50 = np.nanpercentile(a_NaN, 50) # return 50th percentile, e.g median.
p90 = np.nanpercentile(a_NaN, 90) # return 90th percentile.
print('median = ',p50,' and p90 = ',p90) # median =  5.5  and p90 =  9.1

ในสองตัวเลือกที่นำเสนอข้างต้นคุณยังคงสามารถเลือกโหมดการแก้ไข ทำตามตัวอย่างด้านล่างเพื่อความเข้าใจที่ง่ายขึ้น

import numpy as np

b = np.array([1,2,3,4,5,6,7,8,9,10])
print('percentiles using default interpolation')
p10 = np.percentile(b, 10) # return 10th percentile.
p50 = np.percentile(b, 50) # return 50th percentile, e.g median.
p90 = np.percentile(b, 90) # return 90th percentile.
print('p10 = ',p10,', median = ',p50,' and p90 = ',p90)
#p10 =  1.9 , median =  5.5  and p90 =  9.1

print('percentiles using interpolation = ', "linear")
p10 = np.percentile(b, 10,interpolation='linear') # return 10th percentile.
p50 = np.percentile(b, 50,interpolation='linear') # return 50th percentile, e.g median.
p90 = np.percentile(b, 90,interpolation='linear') # return 90th percentile.
print('p10 = ',p10,', median = ',p50,' and p90 = ',p90)
#p10 =  1.9 , median =  5.5  and p90 =  9.1

print('percentiles using interpolation = ', "lower")
p10 = np.percentile(b, 10,interpolation='lower') # return 10th percentile.
p50 = np.percentile(b, 50,interpolation='lower') # return 50th percentile, e.g median.
p90 = np.percentile(b, 90,interpolation='lower') # return 90th percentile.
print('p10 = ',p10,', median = ',p50,' and p90 = ',p90)
#p10 =  1 , median =  5  and p90 =  9

print('percentiles using interpolation = ', "higher")
p10 = np.percentile(b, 10,interpolation='higher') # return 10th percentile.
p50 = np.percentile(b, 50,interpolation='higher') # return 50th percentile, e.g median.
p90 = np.percentile(b, 90,interpolation='higher') # return 90th percentile.
print('p10 = ',p10,', median = ',p50,' and p90 = ',p90)
#p10 =  2 , median =  6  and p90 =  10

print('percentiles using interpolation = ', "midpoint")
p10 = np.percentile(b, 10,interpolation='midpoint') # return 10th percentile.
p50 = np.percentile(b, 50,interpolation='midpoint') # return 50th percentile, e.g median.
p90 = np.percentile(b, 90,interpolation='midpoint') # return 90th percentile.
print('p10 = ',p10,', median = ',p50,' and p90 = ',p90)
#p10 =  1.5 , median =  5.5  and p90 =  9.5

print('percentiles using interpolation = ', "nearest")
p10 = np.percentile(b, 10,interpolation='nearest') # return 10th percentile.
p50 = np.percentile(b, 50,interpolation='nearest') # return 50th percentile, e.g median.
p90 = np.percentile(b, 90,interpolation='nearest') # return 90th percentile.
print('p10 = ',p10,', median = ',p50,' and p90 = ',p90)
#p10 =  2 , median =  5  and p90 =  9

หากอาร์เรย์อินพุตของคุณประกอบด้วยค่าจำนวนเต็มเท่านั้นคุณอาจสนใจคำตอบเปอร์เซ็นเป็นจำนวนเต็ม หากเป็นเช่นนั้นให้เลือกโหมดการแก้ไขเช่น 'ต่ำกว่า', 'สูงกว่า' หรือ 'ใกล้ที่สุด'

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