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!
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
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.
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.
Prenons le cadre de données ci-dessous:
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