Как получить процентиль для строки в кадре данных панд? - PullRequest
0 голосов
/ 11 июня 2018
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.

Мне нужно получить процентиль для одного столбца для каждой строки в кадре данных (255M строк), но не могу найти функции / методы, которые возвращают метод 'линейной интерполяции' , который онииспользовать в pd.quantile & np.percentile.

Я пробовал следующие методы / функции -

.rank(pct=True)

Этот метод возвращает только ранжированные значения по порядку, не используя метод процентиля, который я ищу.Несовместимо с pd.quantiles

scipy.stats.percentileofscore  

Этот метод почти приближается к тому, что я ищу, но по какой-то причине все еще не на 100% совместим с методом «линейной интерполяции». Смежный вопрос к этой проблеме без реального ответа

Я просмотрел каждый SO-ответ, связанный с этим вопросом, но ни один из них не использует тот же метод интерполяции, который мне нужен, чтобыпожалуйста, не отмечайте это как дубликат, если вы не можете проверить, что они используют тот же метод.

На данный момент мой последний вариант - просто найти срезы бина для всех 100 процентилей и применить их таким образом или самостоятельно рассчитать линейную интерполяцию, но это кажется очень неэффективным и потребуется вечно для применения к 255М записям,

Есть еще предложения?

Спасибо!

Ответы [ 2 ]

0 голосов
/ 18 июня 2018

TL;DR

Использование

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

Ответ

Это действительно очень просто, если вы понимаете механику.Когда вы ищете процентиль балла, у вас уже есть баллы в каждом ряду.Единственным оставшимся шагом является понимание того, что вам нужен процентиль чисел, которые меньше или равны выбранному значению.Это именно то, что делают параметры kind = 'слабый' из scipy.stats.percentileofscore() и method = 'средний' из DataFrame.rank().Чтобы инвертировать его, запустите Series.quantile() с interpolation = 'lower' .

Итак, поведение scipy.stats.percentileofscore(), Series.rank() и Series.quantile() равно соответствует, см. Ниже:

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

Теперь в столбце PCNT_RANK вы получите соотношение значений, которые меньше или равны значению в столбце INCOME.Но если вам нужно «интерполированное» соотношение, оно находится в столбце PCNT_LIN.И поскольку вы используете Series.rank() для вычислений, это довольно быстро и за 255 миллионов вы за несколько секунд обработаете вас.


Здесь я объясню, как вы получаете значение от использования quantile() с linearинтерполяция:

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

Наши данные temp['INCOME'] имеют только десять значений.Согласно формуле из вашей ссылки на Wiki ранг 11-го процентиля равен

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

Усеченная часть rank равна 1, что соответствует значению31, и значение с рангом 2 (то есть следующим бином) равно 38. Значение fraction является дробной частью ранга .Это приводит к результату:

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

Для самих значений часть fraction должна быть нулевой, поэтому очень легко выполнить обратный расчет, чтобы получить процентиль:

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

Надеюсь, я прояснил это.

0 голосов
/ 14 июня 2018

Кажется, это работает:

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

Например:

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

Обратите внимание, что эта стратегия имеет смысл, только если вы хотите запросить достаточно большое количество значений.В противном случае сортировка будет слишком дорогой.

...