Comment calculer les centiles avec python / numpy?


214

Existe-t-il un moyen pratique de calculer les centiles pour une séquence ou un tableau numpy unidimensionnel?

Je recherche quelque chose de similaire à la fonction percentile d'Excel.

J'ai regardé dans la référence de statistiques de NumPy et je n'ai pas pu trouver cela. Tout ce que j'ai pu trouver, c'est la médiane (50e centile), mais pas quelque chose de plus spécifique.


Une question connexe sur le calcul des centiles à partir des fréquences: stackoverflow.com/questions/25070086/…
newtover

Réponses:


283

Vous pourriez être intéressé par le package SciPy Stats . Il a la fonction percentile que vous recherchez et de nombreux autres atouts statistiques.

percentile() est également disponible en 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

Ce ticket me fait croire qu'ils ne s'intégreront pas percentile()dans numpy de sitôt.


2
Je vous remercie! C'est donc là qu'il se cachait. J'étais au courant de scipy mais je suppose que je supposais que des choses simples comme les centiles seraient intégrées à numpy.
Uri

16
À l'heure actuelle, une fonction centile existe dans numpy: docs.scipy.org/doc/numpy/reference/generated/…
Anaphory

1
Vous pouvez également l'utiliser comme fonction d'agrégation, par exemple pour calculer le dixième centile de chaque groupe d'une colonne de valeur par clé, utilisezdf.groupby('key')[['value']].agg(lambda g: np.percentile(g, 10))
patricksurry

1
Notez que SciPy recommande d'utiliser np.percentile pour NumPy 1.9 et supérieur
timdiels

73

Soit dit en passant, il existe une implémentation pure-Python de la fonction percentile , au cas où l'on ne voudrait pas dépendre de scipy. La fonction est copiée ci-dessous:

## {{{ 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
Je suis l'auteur de la recette ci-dessus. Un commentateur en ASPN a souligné que le code d'origine avait un bogue. La formule doit être d0 = clé (N [int (f)]) * (ck); d1 = clé (N [int (c)]) * (kf). Il a été corrigé sur ASPN.
Wai Yip Tung

1
Comment percentilesait quoi utiliser N? Il n'est pas spécifié dans l'appel de fonction.
Richard

14
pour ceux qui n'ont même pas lu le code, avant de l'utiliser, N doit être trié
kevin

Je suis confus par l'expression lambda. Que fait-il et comment le fait-il? Je sais ce qu'est l'expression lambda donc je ne demande pas ce qu'est lambda. Je demande ce que fait cette expression lambda spécifique et comment le fait-elle, étape par étape? Merci!
dsanchez

La fonction lambda vous permet de transformer les données Navant de calculer un centile. Supposons que vous ayez réellement une liste de tuples N = [(1, 2), (3, 1), ..., (5, 1)]et que vous vouliez obtenir le centile du premier élément des tuples, alors vous choisissez key=lambda x: x[0]. Vous pouvez également appliquer une transformation (qui change l'ordre) aux éléments de la liste avant de calculer un centile.
Elias Strehle

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

19

Voici comment le faire sans numpy, en utilisant uniquement python pour calculer le centile.

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
Oui, vous devez trier la liste avant: mylist = sorted (...)
Ashkan

12

La définition du centile que je vois habituellement attend comme résultat la valeur de la liste fournie en dessous de laquelle P pourcentage des valeurs sont trouvées ... ce qui signifie que le résultat doit être de l'ensemble, pas une interpolation entre les éléments de l'ensemble. Pour l'obtenir, vous pouvez utiliser une fonction plus 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 vous préférez obtenir la valeur de la liste fournie à laquelle ou en dessous de laquelle P% des valeurs sont trouvées, utilisez cette simple modification:

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

Ou avec la simplification suggérée par @ijustlovemath:

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

merci, je m'attends également à ce que le centile / la médiane donne des valeurs réelles des ensembles et non des interpolations
hansaplast

1
Salut @mpounsett. Merci pour le code supérieur. Pourquoi votre centile renvoie-t-il toujours des valeurs entières? La fonction percentile doit renvoyer le N-ème percentile d'une liste de valeurs, et cela peut aussi être un nombre flottant. Par exemple, l'Excel PERCENTILEfonction retourne les percentiles suivants pour vos exemples supérieurs: 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
C'est expliqué dans la première phrase. La définition la plus courante du centile est qu'il s'agit du nombre dans une série en dessous duquel se trouvent P pour cent des valeurs de la série. Comme il s'agit du numéro d'index d'un élément dans une liste, il ne peut pas être un flottant.
mpounsett

Cela ne fonctionne pas pour le 0e centile. Il renvoie la valeur maximale. Une solution rapide serait d'envelopper la fonction n = int(...)dans une max(int(...), 1)fonction
ijustlovemath

Pour clarifier, voulez-vous dire dans le deuxième exemple? J'obtiens 0 plutôt que la valeur maximale. Le bogue est en fait dans la clause else .. J'ai imprimé le numéro d'index plutôt que la valeur que je voulais. Envelopper l'affectation de 'n' dans un appel max () le corrigerait également, mais vous voudriez que la deuxième valeur soit 2, pas 1. Vous pouvez alors éliminer toute la structure if / else et simplement imprimer le résultat de N [n-2]. Le 0e centile fonctionne bien dans le premier exemple, renvoyant respectivement «1» et «15».
mpounsett

8

Au départ Python 3.8, la bibliothèque standard est livrée avec la quantilesfonction dans le cadre du statisticsmodule:

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

quantilesrenvoie pour une distribution donnée distune liste de n - 1points de coupure séparant les nintervalles quantiles (division de disten nintervalles continus à probabilité égale):

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

n, dans notre cas ( percentiles) est 100.


6

vérifiez le module scipy.stats:

 scipy.stats.scoreatpercentile

2

Pour calculer le centile d'une série, exécutez:

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

Par exemple:

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

Si vous avez besoin de la réponse pour être membre du tableau numpy d'entrée:

Juste pour ajouter que la fonction percentile dans numpy calcule par défaut la sortie en tant que moyenne pondérée linéaire des deux entrées voisines dans le vecteur d'entrée. Dans certains cas, les gens peuvent souhaiter que le centile renvoyé soit un élément réel du vecteur, dans ce cas, à partir de la version 1.9.0, vous pouvez utiliser l'option "interpolation", avec "inférieur", "supérieur" ou "le plus proche".

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

Ce dernier est une entrée réelle dans le vecteur, tandis que le premier est une interpolation linéaire de deux entrées de vecteur qui bordent le centile


0

pour une série: utilisé décrire les fonctions

supposons que vous ayez df avec les colonnes suivantes sales et id. vous voulez calculer les centiles pour les ventes, alors cela fonctionne comme ceci,

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 moyen pratique de calculer les centiles pour une séquence ou matrice numpy unidimensionnelle consiste à utiliser numpy.percentile < https://docs.scipy.org/doc/numpy/reference/generated/numpy.percentile.html >. Exemple:

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

Cependant, s'il existe une valeur NaN dans vos données, la fonction ci-dessus ne sera pas utile. La fonction recommandée à utiliser dans ce cas est la fonction 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

Dans les deux options présentées ci-dessus, vous pouvez toujours choisir le mode d'interpolation. Suivez les exemples ci-dessous pour une meilleure compréhension.

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 votre tableau d'entrée se compose uniquement de valeurs entières, vous pourriez être intéressé par la réponse en pourcentage en tant qu'entier. Si c'est le cas, choisissez le mode d'interpolation tel que «inférieur», «supérieur» ou «le plus proche».

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.