Установите значение 90-го процентиля для каждого столбца в DataFrame.

Я работаю с данными, которые выглядят как DataFrame, описанный

df = pd.DataFrame({'AAA': [1,1,1,2,2,2,3,3], 'BBB': [2,1,3,4,6,1,2,3]})

Что я хотел бы сделать, так это установить значение округления (90%), если значение превышает 90-й процентиль. Так что это похоже на ограничение максимума до 90-го процентиля.

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

Я могу получить значение 90-го процентиля, используя:

df.describe(percentiles=[.9])

Таким образом, для столбца BBB 6 больше, чем 4,60 (90-й процентиль), поэтому его необходимо изменить на 5 (округление 4,60).

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


person Yantraguru    schedule 27.04.2015    source источник


Ответы (2)


Одним из методов векторизации может быть объединение np.minimum и df.quantile:

>>> np.minimum(df, df.quantile(0.9))
   AAA  BBB
0    1  2.0
1    1  1.0
2    1  3.0
3    2  4.0
4    2  4.6
5    2  1.0
6    3  2.0
7    3  3.0

Для большего увеличения скорости используйте:

np.minimum(df, np.percentile(df, 90, axis=0))

df.quantile кажется медленнее, чем np.percentile (возможно, потому, что он возвращает серию, а не простой массив NumPy).

person Alex Riley    schedule 27.04.2015
comment
Не знаю почему, но вы могли заметить, что в опубликованных тестах этот метод кажется медленнее, чем метод применения. +1 за более чистый однострочный. - person Zero; 27.04.2015
comment
Кажется, что np.percentile немного быстрее (в 3 раза), чем df.quantile - возможно, потому, что последний возвращает серию. - person Alex Riley; 27.04.2015
comment
просто для более короткой версии принял это как ответ, в противном случае для меня оба работают, .. спасибо. - person Yantraguru; 27.04.2015

Один из способов сделать это: применить clip_upper() к значению 90-го процентиля np.percentile(x, 90) для каждого столбца.

In [242]: df.apply(lambda x: x.clip_upper(np.percentile(x, 90)))
Out[242]:
   AAA  BBB
0    1  2.0
1    1  1.0
2    1  3.0
3    2  4.0
4    2  4.6
5    2  1.0
6    3  2.0
7    3  3.0

Я предполагал, что элегантное решение @ajcr будет быстрее, чем apply. Но

Ниже контрольных показателей для len(df) ~ 130K

In [245]: %timeit df.apply(lambda x: x.clip_upper(np.percentile(x, 90)))
100 loops, best of 3: 7.49 ms per loop

In [246]: %timeit np.minimum(df, df.quantile(0.9))
100 loops, best of 3: 11.1 ms per loop

И для len(df) ~ 1M

In [248]: %timeit df.apply(lambda x: x.clip_upper(np.percentile(x, 90)))
10 loops, best of 3: 54.5 ms per loop

In [249]: %timeit np.minimum(df, df.quantile(0.9))
10 loops, best of 3: 73.9 ms per loop
person Zero    schedule 27.04.2015
comment
Спасибо за тайминги! Я думал о том же, но, только что проверив тайминги на своей машине, оказалось, что apply иногда это может быть удивительно :-) - person Alex Riley; 27.04.2015
comment
(На самом деле похоже, что np.percentile является причиной большей скорости...) - person Alex Riley; 27.04.2015