Come calcolo i percentili con python / numpy?


214

Esiste un modo conveniente per calcolare percentili per una sequenza o una matrice numpy monodimensionale?

Sto cercando qualcosa di simile alla funzione percentile di Excel.

Ho cercato nel riferimento statistico di NumPy e non sono riuscito a trovarlo. Tutto quello che ho potuto trovare è la mediana (50 ° percentile), ma non qualcosa di più specifico.


Una questione collegata sul calcolo del percentile di frequenze: stackoverflow.com/questions/25070086/...
newtover

Risposte:


282

Potresti essere interessato al pacchetto SciPy Stats . Ha la funzione percentile che stai cercando e molte altre chicche statistiche.

percentile() è disponibile in numpytroppo.

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

Questo biglietto mi porta a credere che presto non si integreranno percentile()in intorpidimento.


2
Grazie! Ecco dove si è nascosto. Ero a conoscenza di Scipy ma immagino di aver supposto cose semplici come percentili sarebbero state costruite in numpy.
Uri

16
Ormai esiste una funzione percentile in numpy: docs.scipy.org/doc/numpy/reference/generated/…
Anaphory

1
Puoi usarlo anche come una funzione di aggregazione, ad es. Per calcolare il decimo percentile di ciascun gruppo di una colonna di valore in base alla chiave, usadf.groupby('key')[['value']].agg(lambda g: np.percentile(g, 10))
patricksurry

1
Nota che SciPy consiglia di utilizzare np.percentile per NumPy 1.9 e versioni successive
timdiels,

73

A proposito, esiste un'implementazione pure-Python della funzione percentile , nel caso in cui non si voglia dipendere da scipy. La funzione viene copiata di seguito:

## {{{ 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
Sono l'autore della ricetta sopra. Un commentatore in ASPN ha sottolineato che il codice originale ha un bug. La formula dovrebbe essere d0 = chiave (N [int (f)]) * (ck); d1 = chiave (N [int (c)]) * (kf). È stato corretto su ASPN.
Wai Yip Tung,

1
Come fa a percentilesapere cosa usare N? Non è specificato nella chiamata di funzione.
Richard,

14
per coloro che non hanno nemmeno letto il codice, prima di usarlo, N deve essere ordinato
kevin

Sono confuso dall'espressione lambda. Cosa fa e come lo fa? So che espressione Lambda sono quindi non sto chiedendo cosa sia Lambda. Sto chiedendo cosa fa questa specifica espressione lambda e come lo fa, passo dopo passo? Grazie!
dsanchez,

La funzione lambda consente di trasformare i dati Nprima di calcolare un percentile. Supponi di avere effettivamente un elenco di tuple N = [(1, 2), (3, 1), ..., (5, 1)]e desideri ottenere il percentile del primo elemento delle tuple, quindi scegli key=lambda x: x[0]. È inoltre possibile applicare una trasformazione (modifica dell'ordine) agli elementi dell'elenco prima di calcolare un percentile.
Elias Strehle,

26
import numpy as np
a = [154, 400, 1124, 82, 94, 108]
print np.percentile(a,95) # gives the 95th percentile

19

Ecco come farlo senza intorpidimento, usando solo Python per calcolare il percentile.

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
Sì, devi prima ordinare l'elenco: mylist = ordinato (...)
Ashkan,

12

La definizione di percentile che di solito vedo prevede come risultato il valore dall'elenco fornito sotto il quale si trova la percentuale P di valori ... il che significa che il risultato deve essere dall'insieme, non un'interpolazione tra gli elementi dell'insieme. Per ottenere ciò, è possibile utilizzare una funzione più semplice.

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

Se preferisci ottenere il valore dall'elenco fornito in corrispondenza o al di sotto del quale si trova la percentuale P di valori, utilizza questa semplice modifica:

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

O con la semplificazione suggerita da @ijustlovemath:

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

grazie, mi aspetto anche che percentile / mediana risultino valori effettivi dagli insiemi e non interpolazioni
hansaplast

1
Ciao @mpounsett. Grazie per il codice superiore. Perché il tuo percentile restituisce sempre valori interi? La funzione percentile dovrebbe restituire l'N-esimo percentile di un elenco di valori e anche questo può essere un numero float. Ad esempio, l'Excel PERCENTILEfunzione restituisce i seguenti percentili per i vostri esempi superiori: 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
È spiegato nella prima frase. La definizione più comune di percentile è che è il numero in una serie al di sotto del quale si trova il P percento dei valori nella serie. Poiché quello è il numero indice di un elemento in un elenco, non può essere un float.
mpounsett,

Questo non funziona per lo 0 ° percentile. Restituisce il valore massimo. Una soluzione rapida sarebbe quella di avvolgere n = int(...)in una max(int(...), 1)funzione
ijustlovemath,

Per chiarire, intendi nel secondo esempio? Ottengo 0 anziché il valore massimo. Il bug è in realtà nella clausola else .. Ho stampato il numero di indice anziché il valore che intendevo. Avvolgere l'assegnazione di 'n' in una chiamata max () lo risolverebbe anche, ma vorresti che il secondo valore fosse 2, non 1. Potresti quindi eliminare l'intera struttura if / else e stampare il risultato di N [n-2]. Il 0 ° percentile funziona bene nel primo esempio, restituendo rispettivamente '1' e '15'.
mpounsett,

8

A partire Python 3.8dalla libreria standard viene fornita la quantilesfunzione come parte del statisticsmodulo:

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

quantilesrestituisce per una data distribuzione distun elenco di n - 1punti di taglio che separano gli nintervalli quantili (divisione distin nintervalli continui con uguale probabilità):

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

dove n, nel nostro caso ( percentiles) è 100.


6

controlla il modulo scipy.stats:

 scipy.stats.scoreatpercentile

2

Per calcolare il percentile di una serie, eseguire:

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

Per esempio:

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

Nel caso in cui sia necessario che la risposta sia un membro dell'array numpy di input:

Solo per aggiungere che la funzione percentile in numpy per impostazione predefinita calcola l'output come media ponderata lineare delle due voci vicine nel vettore di input. In alcuni casi, le persone potrebbero volere che il percentile restituito sia un elemento reale del vettore, in questo caso, dalla v1.9.0 in poi è possibile utilizzare l'opzione "interpolazione", con "inferiore", "superiore" o "più vicino".

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

Quest'ultima è una voce effettiva nel vettore, mentre la prima è un'interpolazione lineare di due voci vettoriali che delimitano il percentile


0

per una serie: usato descrivi le funzioni

supponiamo di avere df con le seguenti colonne sales e id. vuoi calcolare percentili per le vendite, quindi funziona così,

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

Un modo conveniente per calcolare percentili per una sequenza o matrice numpy unidimensionale è utilizzare numpy.percentile < https://docs.scipy.org/doc/numpy/reference/generated/numpy.percentile.html >. Esempio:

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

Tuttavia, se è presente un valore NaN nei dati, la funzione precedente non sarà utile. La funzione consigliata da utilizzare in quel caso è la funzione numpy.nanpercentile < https://docs.scipy.org/doc/numpy/reference/generated/numpy.nanpercentile.html >:

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

Nelle due opzioni sopra presentate, puoi comunque scegliere la modalità di interpolazione. Segui gli esempi seguenti per una comprensione più semplice.

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

Se il tuo array di input è costituito solo da valori interi, potresti essere interessato alla risposta percentile come intero. In tal caso, selezionare la modalità di interpolazione come "inferiore", "superiore" o "più vicino".

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.