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.

Uri
fonte
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.

Jon W
fonte
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/ }}}
Boris Gorelik
fonte
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
richie
fonte
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)
Ashkan
fonte
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]
mpounsett
fonte
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.

Xavier Guihot
fonte
6

verifique o módulo scipy.stats:

 scipy.stats.scoreatpercentile
Evert
fonte
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}
Roei Bahumi
fonte
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

Adrian Tompkins
fonte
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
ashwini
fonte
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'.

Italo Gervasio
fonte