У меня есть фрейм данных Pandas, в котором каждый столбец представляет собой отдельное свойство, а каждая строка содержит значение свойств на определенную дату:
import pandas as pd
dfstr = \
''' AC BO C CCM CL CRD CT DA GC GF
2010-01-19 0.844135 -0.194530 -0.231046 0.245615 -0.581238 -0.593562 0.057288 0.655903 0.823997 0.221920
2010-01-20 -0.204845 -0.225876 0.835611 -0.594950 -0.607364 0.042603 0.639168 0.816524 0.210653 0.237833
2010-01-21 0.824852 -0.216449 -0.220136 0.234343 -0.611756 -0.624060 0.028295 0.622516 0.811741 0.201083'''
df = pd.read_csv(pd.compat.StringIO(dfstr), sep='\s+')
Используя метод rank
, я могу найти процентильный ранг каждого свойства по отношению к определенной дате:
df.rank(axis=1, pct=True)
Выход:
AC BO C CCM CL CRD CT DA GC GF
2010-01-19 1.0 0.4 0.3 0.7 0.2 0.1 0.5 0.8 0.9 0.6
2010-01-20 0.4 0.3 1.0 0.2 0.1 0.5 0.8 0.9 0.6 0.7
2010-01-21 1.0 0.4 0.3 0.7 0.2 0.1 0.5 0.8 0.9 0.6
Вместо этого я хотел бы получить квантильный (например, квартиль, квинтиль, дециль и т. д.) ранг каждого свойства. Например, для квинтильного ранга моим желаемым результатом будет:
AC BO C CCM CL CRD CT DA GC GF
2010-01-19 5 2 2 4 1 1 3 4 5 3
2010-01-20 2 2 5 1 1 3 4 5 3 4
2010-01-21 5 2 2 4 1 1 3 4 5 3
Я мог бы что-то упустить, но, похоже, нет встроенного способа сделать такое квантильное ранжирование с Pandas. Какой самый простой способ получить желаемый результат?
@QuangHoang Да, это удивительно сложно. Кроме того, я думаю, что могут быть некоторые дополнительные крайние случаи, которые необходимо учитывать, если есть повторяющиеся или отсутствующие данные.
mul
и np.ceil
Вы были довольно близки с рангом. Просто умножьте на 5 на .mul
, чтобы получить желаемый квантиль, также округлив на np.ceil
:
np.ceil(df.rank(axis=1, pct=True).mul(5))
Output
AC BO C CCM CL CRD CT DA GC GF
2010-01-19 5.0 2.0 2.0 4.0 1.0 1.0 3.0 4.0 5.0 3.0
2010-01-20 2.0 2.0 5.0 1.0 1.0 3.0 4.0 5.0 3.0 4.0
2010-01-21 5.0 2.0 2.0 4.0 1.0 1.0 3.0 4.0 5.0 3.0
Если вам нужны целые числа, используйте astype
:
np.ceil(df.rank(axis=1, pct=True).mul(5)).astype(int)
Или даже лучше
Начиная с версии панд 0.24.0 у нас есть тип целое число, допускающее значение NULL: Int64
.
Итак, мы можем использовать:
np.ceil(df.rank(axis=1, pct=True).mul(5)).astype('Int64')
Output
AC BO C CCM CL CRD CT DA GC GF
2010-01-19 5 2 2 4 1 1 3 4 5 3
2010-01-20 2 2 5 1 1 3 4 5 3 4
2010-01-21 5 2 2 4 1 1 3 4 5 3
scipy.stats.percentileofscore
d = df.apply(lambda x: [np.ceil(stats.percentileofscore(x, a, 'rank')*0.05) for a in x], axis=1).values
pd.DataFrame(data=np.concatenate(d).reshape(d.shape[0], len(d[0])),
columns=df.columns,
dtype='int',
index=df.index)
Output
AC BO C CCM CL CRD CT DA GC GF
2010-01-19 5 2 2 4 1 1 3 4 5 3
2010-01-20 2 2 5 1 1 3 4 5 3 4
2010-01-21 5 2 2 4 1 1 3 4 5 3
Ах, мило. Я не знал, что np.ceil
просто будет работать с фреймом данных без дальнейшего принуждения.
Да, так как базовые данные DataFrames являются массивами. Таким образом, вы можете применить к ним функцию numpy
.
Просто для вашего удобства я знал, что однажды использовал метод из модуля scipy
. Добавлен еще один метод @tel, генерирующий тот же результат.
Аккуратный. Я знал, что кадры данных обертывают массивы Numpy, но мне интересно, какую хитрость придумали разработчики Numpy/Panda, которые позволяют np.ceil
возвращать желаемый тип (т.е. pd.DataFrame
) из np.ceil
вместо стандартного np.ndarray
.
Кроме того, одна маленькая придирка: .astype(int)
не работает, когда у вас отсутствуют данные, так как NaN
— это число с плавающей запятой. Хорошей новостью является то, что пока это единственный сбой в крайних случаях, который я обнаружил с вашими решениями.
Да, хорошее замечание по поводу NaN
, добавлено решение с типом nullable integer
. @тел
Интересует однострочное решение. Хотя, как только вы получили ранг на
percentile
, получение квартиля и так далее — это просто еще одна строкаmap
.