Como calculo percentis com python / numpy?


214

Existe uma maneira conveniente de calcular percentis para uma sequência ou matriz numpy unidimensional?

Estou procurando algo semelhante à função de percentil do Excel.

Procurei na referência de estatísticas da NumPy e não consegui encontrar isso. Tudo o que pude encontrar é a mediana (percentil 50), mas não algo mais específico.


Uma questão relacionada ao cálculo de percentis a partir de frequências: stackoverflow.com/questions/25070086/…
— newtover 10/10/19

Respostas:


282

Você pode estar interessado no pacote SciPy Stats . Tem a função de percentil que você procura e muitas outras vantagens estatísticas.

percentile() está disponível em numpytambém.

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

Esse ingresso me leva a acreditar que eles não se integrarão percentile()ao numpy tão cedo.


2
Obrigado! Então é aí que está se escondendo. Eu estava ciente de scipy, mas acho que assumi que coisas simples, como percentis, seriam transformadas em entorpecentes.
— Uri

16
Até agora, uma função percentil existe no numpy: docs.scipy.org/doc/numpy/reference/generated/...
— Anaphory

1
É possível utilizá-lo como uma função de agregação, bem como, por exemplo, para calcular a décima percentual de cada grupo de uma coluna de valor por chave, de usodf.groupby('key')[['value']].agg(lambda g: np.percentile(g, 10))
— patricksurry

1
Observe que o SciPy recomenda usar o np.percentile para o NumPy 1.9 e superior
— timdiels

73

A propósito, existe uma implementação pura em Python da função percentil , caso não se queira depender de scipy. A função é copiada abaixo:

## {{{ 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/ }}}

53
Eu sou o autor da receita acima. Um comentarista do ASPN apontou que o código original tem um bug. A fórmula deve ser d0 = chave (N [int (f)]) * (ck); d1 = tecla (N [int (c)]) * (kf). Foi corrigido no ASPN.
— Wai Yip Tung

1
Como percentilesabe o que usar N? Não está especificado na chamada de função.
— Richard

14
para aqueles que nem sequer ler o código, antes de o utilizar, N deve ser ordenada
— kevin

Estou confuso com a expressão lambda. O que faz e como faz? Eu sei o que são expressões lambda, então não estou perguntando o que é lambda. Estou perguntando o que essa expressão lambda específica faz e como está sendo, passo a passo? Obrigado!
— dsanchez

A função lambda permite transformar os dados Nantes de calcular um percentil. Digamos que você realmente tenha uma lista de tuplas N = [(1, 2), (3, 1), ..., (5, 1)]e deseje obter o percentil do primeiro elemento das tuplas e escolha key=lambda x: x[0]. Você também pode aplicar alguma transformação (alteração de ordem) aos elementos da lista antes de calcular um percentil.
— Elias Strehle 25/11/19

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

19

Veja como fazê-lo sem numpy, usando apenas python para calcular o 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)

2
Sim, você tem que ordenar a lista antes: = myList ordenados (...)
— Ashkan

12

A definição de percentil que normalmente vejo espera como resultado o valor da lista fornecida abaixo da qual P% dos valores são encontrados ... o que significa que o resultado deve ser do conjunto, não uma interpolação entre os elementos do conjunto. Para conseguir isso, você pode usar uma função mais simples.

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 você preferir obter o valor da lista fornecida igual ou inferior a P% dos valores, use esta modificação simples:

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

Ou com a simplificação sugerida por @ijustlovemath:

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

obrigado, eu também esperam percentil / médio para resultar valores reais dos conjuntos e não interpolações
— Hansaplast

1
Oi @mpounsett. Obrigado pelo código superior. Por que seu percentil sempre retorna valores inteiros? A função percentil deve retornar o N-ésimo percentil de uma lista de valores, e também pode ser um número flutuante. Por exemplo, o Excel PERCENTILEfunção retorna os seguintes percentuais para os seus exemplos 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
É explicado na primeira frase. A definição mais comum de percentil é que é o número de uma série abaixo do qual P% dos valores da série são encontrados. Como esse é o número de índice de um item em uma lista, ele não pode ser um flutuador.
— mpounsett

Isso não funciona para o 0'th percentil. Retorna o valor máximo. Uma solução rápida seria n = int(...)max(int(...), 1)
— agrupar

Para esclarecer, você quer dizer no segundo exemplo? Recebo 0 em vez do valor máximo. O bug está na cláusula else. Imprimi o número do índice e não o valor que pretendia. O agrupamento da atribuição de 'n' em uma chamada max () também o corrigia, mas você deseja que o segundo valor seja 2, não 1. Você pode eliminar toda a estrutura if / else e apenas imprimir o resultado de N [n-2] O percentil 0 funciona bem no primeiro exemplo, retornando '1' e '15' respectivamente.
— mpounsett

8

Iniciando Python 3.8, a biblioteca padrão vem com a quantilesfunção como parte do 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

quantilesretorna para uma determinada distribuição distuma lista de n - 1pontos de corte que separam os nintervalos quantílicos (divisão de distem nintervalos contínuos com igual probabilidade):

stattics.quantiles (dist, *, n = 4, método = 'exclusivo')

onde n, no nosso caso ( percentiles) é 100.


6

verifique o módulo scipy.stats:

 scipy.stats.scoreatpercentile

2

Para calcular o percentil de uma série, execute:

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 exemplo:

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

Caso você precise da resposta para ser um membro da matriz numpy de entrada:

Apenas para adicionar que a função percentil em numpy, por padrão, calcula a saída como uma média ponderada linear das duas entradas vizinhas no vetor de entrada. Em alguns casos, as pessoas podem querer que o percentil retornado seja um elemento real do vetor; nesse caso, a partir da v1.9.0, você pode usar a opção "interpolação", com "inferior", "superior" ou "mais próximo".

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

O último é uma entrada real no vetor, enquanto o primeiro é uma interpolação linear de duas entradas do vetor que limitam o percentil


0

para uma série: usado para descrever funções

suponha que você tenha df com as seguintes colunas sales e id. você deseja calcular percentis para vendas, funciona assim,

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

Uma maneira conveniente de calcular percentis para uma sequência ou matriz numpy unidimensional é usando numpy.percentile < https://docs.scipy.org/doc/numpy/reference/generated/numpy.percentile.html >. Exemplo:

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

No entanto, se houver algum valor de NaN em seus dados, a função acima não será útil. A função recomendada para uso nesse caso é a função 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

Nas duas opções apresentadas acima, você ainda pode escolher o modo de interpolação. Siga os exemplos abaixo para entender melhor.

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 sua matriz de entrada consistir apenas em valores inteiros, você poderá estar interessado na resposta do percentil como um número inteiro. Nesse caso, escolha o modo de interpolação, como 'inferior', 'superior' ou 'mais próximo'.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.