¿Cómo calculo los percentiles con python / numpy?

214

¿Hay una manera conveniente de calcular percentiles para una secuencia o matriz numpy unidimensional?

Estoy buscando algo similar a la función de percentil de Excel.

Miré en la referencia de estadísticas de NumPy, y no pude encontrar esto. Todo lo que pude encontrar es la mediana (percentil 50), pero no algo más específico.

Uri
fuente
Una pregunta relacionada sobre el cálculo de percentiles de frecuencias: stackoverflow.com/questions/25070086/…
newtover

Respuestas:

282

Quizás te interese el paquete SciPy Stats . Tiene la función de percentil que busca y muchas otras ventajas estadísticas.

percentile() está disponible en numpytambién.

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

Este boleto me lleva a creer que no se integrarán percentile()en numpy pronto.

Jon W
fuente
2
¡Gracias! Entonces ahí es donde se ha estado escondiendo. Era consciente de scipy pero supongo que asumí cosas simples como los percentiles se convertirían en numpy.
Uri
16
Por ahora, existe una función de percentil en numpy: docs.scipy.org/doc/numpy/reference/generated/…
Anaphory
1
También puede usarlo como una función de agregación, por ejemplo, para calcular el décimo percentil de cada grupo de una columna de valor por clave, usedf.groupby('key')[['value']].agg(lambda g: np.percentile(g, 10))
patricksurry el
1
Tenga en cuenta que SciPy recomienda usar np.percentile para NumPy 1.9 y superior
timdiels
73

Por cierto, hay una implementación de Python pura de la función de percentil , en caso de que uno no quiera depender de scipy. La función se copia a continuación:

## {{{ 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/ }}}
Boris Gorelik
fuente
54
Soy el autor de la receta anterior. Un comentarista en ASPN ha señalado que el código original tiene un error. La fórmula debe ser d0 = clave (N [int (f)]) * (ck); d1 = clave (N [int (c)]) * (kf). Se ha corregido en ASPN.
Wai Yip Tung el
1
¿Cómo percentilesabe para qué usar N? No se especifica en la llamada a la función.
Richard
14
para aquellos que ni siquiera leyeron el código, antes de usarlo, N debe ser ordenado
kevin
Estoy confundido por la expresión lambda. ¿Qué hace y cómo lo hace? Sé qué son las expresiones lambda, así que no pregunto qué es lambda. Me pregunto qué hace esta expresión lambda específica y cómo lo hace, paso a paso. ¡Gracias!
dsanchez
La función lambda le permite transformar los datos Nantes de calcular un percentil. Digamos que en realidad tienes una lista de tuplas N = [(1, 2), (3, 1), ..., (5, 1)]y quieres obtener el percentil del primer elemento de las tuplas, luego eliges key=lambda x: x[0]. También puede aplicar alguna transformación (cambio de orden) a los elementos de la lista antes de calcular un percentil.
Elias Strehle
26
import numpy as np
a = [154, 400, 1124, 82, 94, 108]
print np.percentile(a,95) # gives the 95th percentile
Richie
fuente
19

Aquí se explica cómo hacerlo sin numpy, usando solo python para calcular el percentil.

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)
Ashkan
fuente
2
Sí, debe ordenar la lista antes: mylist = sorted (...)
Ashkan
12

La definición de percentil que generalmente veo espera como resultado el valor de la lista suministrada debajo del cual se encuentra el porcentaje de P ... lo que significa que el resultado debe ser del conjunto, no una interpolación entre los elementos del conjunto. Para obtener eso, puede usar una función más simple.

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

Si prefiere obtener el valor de la lista provista en o por debajo del cual se encuentra el P por ciento de los valores, utilice esta simple modificación:

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 simplificación sugerida por @ijustlovemath:

def percentile(N, P):
    n = max(int(round(P * len(N) + 0.5)), 2)
    return N[n-2]
mpounsett
fuente
gracias, yo también esperan percentil / mediana para dar lugar a los valores reales de los conjuntos y no interpolaciones
Hansaplast
1
Hola @mpounsett. Gracias por el código superior. ¿Por qué su percentil siempre devuelve valores enteros? La función de percentil debería devolver el percentil N de una lista de valores, y este también puede ser un número flotante. Por ejemplo, el Excel PERCENTILEfunción devuelve los siguientes percentiles para sus ejemplos superiores: 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
Se explica en la primera oración. La definición más común de percentil es que es el número en una serie por debajo del cual se encuentra el P por ciento de los valores de la serie. Como ese es el número de índice de un elemento en una lista, no puede ser flotante.
mpounsett
Esto no funciona para el percentil 0. Devuelve el valor máximo. Una solución rápida sería envolver el n = int(...)en una max(int(...), 1)función
ijustlovemath
Para aclarar, ¿te refieres al segundo ejemplo? Obtengo 0 en lugar del valor máximo. El error está realmente en la cláusula else. Imprimí el número de índice en lugar del valor que pretendía. Ajustar la asignación de 'n' en una llamada max () también lo arreglaría, pero desearía que el segundo valor fuera 2, no 1. Entonces podría eliminar toda la estructura if / else e imprimir el resultado de N [n-2]. El percentil 0 funciona bien en el primer ejemplo, devolviendo '1' y '15' respectivamente.
mpounsett
8

Comenzando Python 3.8, la biblioteca estándar viene con la quantilesfunción como parte del statisticsmódulo:

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

quantilesdevuelve para una distribución dada distuna lista de n - 1puntos de corte que separan los nintervalos cuantiles (división de disten nintervalos continuos con igual probabilidad):

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

donde n, en nuestro caso ( percentiles) es 100.

Xavier Guihot
fuente
6

compruebe el módulo scipy.stats:

 scipy.stats.scoreatpercentile
Evert
fuente
2

Para calcular el percentil de una serie, ejecute:

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

Por ejemplo:

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}
Roei Bahumi
fuente
1

En caso de que necesite la respuesta para ser miembro de la matriz numpy de entrada:

Solo para agregar que la función de percentil en numpy por defecto calcula la salida como un promedio lineal ponderado de las dos entradas vecinas en el vector de entrada. En algunos casos, las personas pueden desear que el percentil devuelto sea un elemento real del vector, en este caso, desde v1.9.0 en adelante, puede usar la opción "interpolación", ya sea con "más bajo", "más alto" o "más cercano".

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

La última es una entrada real en el vector, mientras que la primera es una interpolación lineal de dos entradas de vector que bordean el percentil.

Adrian Tompkins
fuente
0

para una serie: funciones de descripción utilizadas

supongamos que tiene df con las siguientes columnas de ventas e id. desea calcular percentiles para ventas, entonces funciona así,

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
ashwini
fuente
0

Una manera conveniente de calcular los percentiles para una secuencia o matriz numpy unidimensional es usando numpy.percentile < https://docs.scipy.org/doc/numpy/reference/generated/numpy.percentile.html >. Ejemplo:

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

Sin embargo, si hay algún valor de NaN en sus datos, la función anterior no será útil. La función recomendada para usar en ese caso es la función 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

En las dos opciones presentadas anteriormente, aún puede elegir el modo de interpolación. Siga los ejemplos a continuación para una comprensión más fácil.

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

Si su matriz de entrada solo consta de valores enteros, es posible que le interese la respuesta porcentual como un entero. Si es así, elija el modo de interpolación como 'más bajo', 'más alto' o 'más cercano'.

Italo Gervasio
fuente