web-dev-qa-db-fra.com

Comment obtenir le centile d'une ligne dans une trame de données pandas?

Example DataFrame Values -  

0     78
1     38
2     42
3     48
4     31
5     89
6     94
7    102
8    122
9    122  

stats.percentileofscore(temp['INCOME'].values, 38, kind='mean')
15.0

stats.percentileofscore(temp['INCOME'].values, 38, kind='strict')
10.0

stats.percentileofscore(temp['INCOME'].values, 38, kind='weak')
20.0

stats.percentileofscore(temp['INCOME'].values, 38, kind='rank')
20.0

temp['INCOME'].rank(pct=True)
1    0.20 (Only showing the 38 value index)

temp['INCOME'].quantile(0.11)
37.93

temp['INCOME'].quantile(0.12)
38.31999999999999

Based on the results above, you can see none of the methods are consistent
with the pd.quantiles() method.

J'ai besoin d'obtenir le centile pour une colonne pour chaque ligne dans un cadre de données (255 millions de lignes) mais je ne trouve aucune fonction/méthode qui renvoie la méthode 'interpolation linéaire' qu'ils utilisent dans pd.quantile & np.percentile.

J'ai essayé les méthodes/fonctions suivantes -

.rank(pct=True)

Cette méthode ne renvoie que les valeurs classées dans l'ordre, sans utiliser la méthode centile que je recherche. Incompatible avec pd.quantiles

scipy.stats.percentileofscore  

Cette méthode est presque plus proche de ce que je recherche, mais n'est toujours pas 100% cohérente avec la méthode d '"interpolation linéaire" pour une raison quelconque. Question liée à ce problème sans vraie réponse

J'ai parcouru toutes les réponses SO liées à cette question, mais aucune d'entre elles n'utilise la même méthode d'interpolation que je dois utiliser, veuillez donc ne pas la marquer comme doublon, sauf si vous pouvez vérifier ils utilisent la même méthode.

À ce stade, ma dernière option consiste à trouver les seuils de coupure pour tous les 100 centiles et à l'appliquer de cette façon ou à calculer l'interpolation linéaire moi-même, mais cela semble très inefficace et prendra une éternité pour s'appliquer aux enregistrements 255M.

Avez-vous d'autres suggestions à faire?

Merci!

7
bbennett36

TL; DR

Utilisation

sz = temp['INCOME'].size-1
temp['PCNT_LIN'] = temp['INCOME'].rank(method='max').apply(lambda x: 100.0*(x-1)/sz)

   INCOME    PCNT_LIN
0      78   44.444444
1      38   11.111111
2      42   22.222222
3      48   33.333333
4      31    0.000000
5      89   55.555556
6      94   66.666667
7     102   77.777778
8     122  100.000000
9     122  100.000000

Répondre

C'est en fait très simple, une fois que vous avez compris la mécanique. Lorsque vous recherchez le centile d'un score, vous avez déjà les scores dans chaque ligne. La seule étape qui reste consiste à comprendre que vous avez besoin d'un centile de nombres qui sont inférieurs ou égaux à la valeur sélectionnée. C'est exactement ce que les paramètres kind = 'faible' de scipy.stats.percentileofscore() et méthode = 'moyen' de DataFrame.rank() faire. Pour l'inverser, exécutez Series.quantile() avec interpolation = 'lower'.

Ainsi, le comportement de la scipy.stats.percentileofscore(), Series.rank() et Series.quantile() est cohérent, voir ci-dessous:

In[]:
temp = pd.DataFrame([  78, 38, 42, 48, 31, 89, 94, 102, 122, 122], columns=['INCOME'])
temp['PCNT_RANK']=temp['INCOME'].rank(method='max', pct=True)
temp['POF']  = temp['INCOME'].apply(lambda x: scipy.stats.percentileofscore(temp['INCOME'], x, kind='weak'))
temp['QUANTILE_VALUE'] = temp['PCNT_RANK'].apply(lambda x: temp['INCOME'].quantile(x, 'lower'))
temp['RANK']=temp['INCOME'].rank(method='max')
sz = temp['RANK'].size - 1 
temp['PCNT_LIN'] = temp['RANK'].apply(lambda x: (x-1)/sz)
temp['CHK'] = temp['PCNT_LIN'].apply(lambda x: temp['INCOME'].quantile(x))

temp

Out[]:
   INCOME  PCNT_RANK    POF  QUANTILE_VALUE  RANK  PCNT_LIN    CHK
0      78        0.5   50.0              78   5.0  0.444444   78.0
1      38        0.2   20.0              38   2.0  0.111111   38.0
2      42        0.3   30.0              42   3.0  0.222222   42.0
3      48        0.4   40.0              48   4.0  0.333333   48.0
4      31        0.1   10.0              31   1.0  0.000000   31.0
5      89        0.6   60.0              89   6.0  0.555556   89.0
6      94        0.7   70.0              94   7.0  0.666667   94.0
7     102        0.8   80.0             102   8.0  0.777778  102.0
8     122        1.0  100.0             122  10.0  1.000000  122.0
9     122        1.0  100.0             122  10.0  1.000000  122.0

Maintenant dans une colonne PCNT_RANK Vous obtenez un rapport de valeurs plus petites ou égales à celle d'une colonne INCOME. Mais si vous voulez le ratio "interpolé", c'est dans la colonne PCNT_LIN. Et comme vous utilisez Series.rank() pour les calculs, c'est assez rapide et vous crunch 255M nombres en quelques secondes.


Ici, je vais expliquer comment vous obtenez la valeur en utilisant quantile() avec linear interpolation:

temp['INCOME'].quantile(0.11)
37.93

Nos données temp['INCOME'] N'ont que dix valeurs. Selon la formule de votre lien vers Wiki le rang du 11ème centile est

rank = 11*(10-1)/100 + 1 = 1.99

La partie tronquée du rank est 1, ce qui correspond à la valeur 31, et la valeur avec le rang 2 (c'est-à-dire le bac suivant) est 38. La valeur de fraction est la partie fractionnaire du rang. Cela conduit au résultat:

 31 + (38-31)*(0.99) = 37.93

Pour les valeurs elles-mêmes, la partie fraction doit être nulle, il est donc très facile de faire le calcul inverse pour obtenir le centile:

p = (rank - 1)*100/(10 - 1)

J'espère avoir clarifié les choses.

12
igrinis

Cela semble fonctionner:

A = np.sort(temp['INCOME'].values)
np.interp(sample, A, np.linspace(0, 1, len(A)))

Par exemple:

>>> temp.INCOME.quantile(np.interp([37.5, 38, 122, 121], A, np.linspace(0, 1, len(A))))
0.103175     37.5
0.111111     38.0
1.000000    122.0
0.883333    121.0
Name: INCOME, dtype: float64

Veuillez noter que cette stratégie n'a de sens que si vous souhaitez interroger un nombre suffisamment important de valeurs. Sinon, le tri est trop cher.

1
Paul Panzer

Prenons le cadre de données ci-dessous:

DataFrame

Afin d'obtenir le centile d'une colonne dans pandas Dataframe, nous utilisons le code suivant:

 survey['Nationality'].value_counts(normalize='index')

Production:

USA 0.333333

Chine 0,250000

Inde 0,250000

Bangadesh 0.166667

Nom: Nationalité, dtype: float64

Afin d'obtenir le centile d'une colonne dans pandas Dataframe par rapport à une autre colonne catégorielle

pd.crosstab(survey.Sex,survey.Handedness,normalize = 'index')

La sortie serait quelque chose comme ci-dessous

Sortie

0
Randel Rodrigues