Как отрезать ненужные края бункера?
Мой вопрос такой же, как и предыдущий:
Объединение с нулевыми значениями в pandas
однако, я все же хочу включить значения 0 в фрактал. Есть ли способ сделать это? Другими словами, если у меня есть 600 значений, 50% из которых равны 0, а остальным - от 1 до 100, как бы классифицировать все значения 0 в фрактале 1, а затем остальные ненулевые значения в метрических меток с 2 по 10 (при условии, что я хочу 10 фракций). Могу ли я преобразовать 0 в nan, qcut оставшиеся не-нано данные в 9 фракций (от 1 до 9), затем добавить 1 к каждой метке (теперь от 2 до 10) и пометить все значения 0 как фрактали 1 вручную? Даже это сложно, потому что в моих данных, помимо 600 значений, у меня также есть еще пара сотен, которые уже могут быть наном, прежде чем я переконвертирую 0s в nan.
Обновление 1/26/14:
Я придумал следующее временное решение. Однако проблема с этим кодом заключается в том, что если высокочастотное значение не находится на краях распределения, то оно добавляет дополнительный бит в середине существующего набора бункеров и бросает все немного (или много).
def fractile_cut(ser, num_fractiles):
num_valid = ser.valid().shape[0]
remain_fractiles = num_fractiles
vcounts = ser.value_counts()
high_freq = []
i = 0
while vcounts.iloc[i] > num_valid/ float(remain_fractiles):
curr_val = vcounts.index[i]
high_freq.append(curr_val)
remain_fractiles -= 1
num_valid = num_valid - vcounts[i]
i += 1
curr_ser = ser.copy()
curr_ser = curr_ser[~curr_ser.isin(high_freq)]
qcut = pd.qcut(curr_ser, remain_fractiles, retbins=True)
qcut_bins = qcut[1]
all_bins = list(qcut_bins)
for val in high_freq:
bisect.insort(all_bins, val)
cut = pd.cut(ser, bins=all_bins)
ser_fractiles = pd.Series(cut.labels + 1, index=ser.index)
return ser_fractiles
Ответы
Ответ 1
Вы спрашиваете о биннинге с неповторимыми краями ящиков, для которых у меня довольно простой ответ. В случае вашего примера ваше намерение и поведение qcut расходятся, где в функции pandas.tools.tile.qcut
, где определены ячейки:
bins = algos.quantile(x, quantiles)
Что из-за того, что ваши данные равны 50% 0, заставляет бины возвращаться с несколькими краями ящиков при значении 0 для любого значения квантилей, большего, чем 2. Я вижу два возможных разрешения. Во-первых, пространство фрактали делится равномерно, заполняя все 0s, но не только 0s, в первом ящике. Во втором случае пространство фрактали равномерно делится на значения, превышающие 0, забивая все 0s и только 0s в первом бункере.
import numpy as np
import pandas as pd
import pandas.core.algorithms as algos
from pandas import Series
В обоих случаях я создам некоторые случайные данные образца, соответствующие вашему описанию 50% нулей, а оставшиеся значения между 1 и 100
zs = np.zeros(300)
rs = np.random.randint(1, 100, size=300)
arr=np.concatenate((zs, rs))
ser = Series(arr)
Решение 1: bin 1 содержит как 0s, так и низкие значения
bins = algos.quantile(np.unique(ser), np.linspace(0, 1, 11))
result = pd.tools.tile._bins_to_cuts(ser, bins, include_lowest=True)
Результат
In[61]: result.value_counts()
Out[61]:
[0, 9.3] 323
(27.9, 38.2] 37
(9.3, 18.6] 37
(88.7, 99] 35
(57.8, 68.1] 32
(68.1, 78.4] 31
(78.4, 88.7] 30
(38.2, 48.5] 27
(48.5, 57.8] 26
(18.6, 27.9] 22
dtype: int64
Решение 2: bin1 содержит только 0s
mx = np.ma.masked_equal(arr, 0, copy=True)
bins = algos.quantile(arr[~mx.mask], np.linspace(0, 1, 11))
bins = np.insert(bins, 0, 0)
bins[1] = bins[1]-(bins[1]/2)
result = pd.tools.tile._bins_to_cuts(arr, bins, include_lowest=True)
Результат:
In[133]: result.value_counts()
Out[133]:
[0, 0.5] 300
(0.5, 11] 32
(11, 18.8] 28
(18.8, 29.7] 30
(29.7, 39] 35
(39, 50] 26
(50, 59] 31
(59, 71] 31
(71, 79.2] 27
(79.2, 90.2] 30
(90.2, 99] 30
dtype: int64
Есть работа, которая может быть выполнена для решения 2, чтобы сделать ее немного красивее, я думаю, но вы можете видеть, что маскированный массив - полезный инструмент для достижения ваших целей.
Ответ 2
Другой способ сделать это - ввести минимальное количество шума, которое искусственно создаст уникальные края бункера. Вот пример:
a = pd.Series(range(100) + ([0]*20))
def jitter(a_series, noise_reduction=1000000):
return (np.random.random(len(a_series))*a_series.std()/noise_reduction)-(a_series.std()/(2*noise_reduction))
# and now this works by adding a little noise
a_deciles = pd.qcut(a + jitter(a), 10, labels=False)
мы можем восстановить исходную ошибку, используя что-то вроде этого:
a_deciles = pd.qcut(a, 10, labels=False)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/site-packages/pandas/tools/tile.py", line 173, in qcut
precision=precision, include_lowest=True)
File "/usr/local/lib/python2.7/site-packages/pandas/tools/tile.py", line 192, in _bins_to_cuts
raise ValueError('Bin edges must be unique: %s' % repr(bins))
ValueError: Bin edges must be unique: array([ 0. , 0. , 0. , 3.8 ,
11.73333333, 19.66666667, 27.6 , 35.53333333,
43.46666667, 51.4 , 59.33333333, 67.26666667,
75.2 , 83.13333333, 91.06666667, 99. ])
Ответ 3
Проблема pandas.qcut выбирает ячейки, чтобы иметь одинаковое количество записей в каждом бункере/квантиле, но одно и то же значение не может попадать в несколько бункеров/квантов. pandas. p >
Решения:
1 - Используйте pandas >= 0.20.0, у которого это исправление. Они добавили опцию duplicates='raise'|'drop'
, чтобы контролировать, нужно ли поднимать дублированные ребра или бросать их, что приведет к меньшему количеству ящиков, чем указано, и к некоторым большим (с большим количеством элементов), чем другие.
2 - используйте pandas.cut, который выбирает ячейки для равномерного распределения в соответствии с самими значениями, тогда как pandas.qcut выбирает ячейки, чтобы иметь одинаковое количество записей в каждом бункере
3 - Уменьшить количество квантилей. Меньше квантилей означает больше элементов на квантиль.
4 - Укажите диапазон пользовательских квантилей, например. [0,.50,.75, 1.], чтобы получить неравное количество элементов на квантиль.
5 - ранжируйте свои данные с помощью DataFrame.rank(method = 'first') и передайте эти данные в pd.qcut. Таким образом, вы можете иметь одинаковые значения в разных квантилях, которые могут быть правильными или нет в зависимости от ваших намерений.
если следующее генерирует "ValueError: края бина должны быть уникальными"
pd.qcut(df, nbins) <-- this generates "ValueError: Bin edges must be unique"
Вместо этого вы можете использовать это:
pd.qcut(df.rank(method='first'), nbins)
Ответ 4
У меня было много проблем с qcut, так что я использовал функцию Series.rank в сочетании с созданием собственных бункеров с использованием этих результатов. Мой код находится на Github:
https://gist.github.com/ashishsingal1/e1828ffd1a449513b8f8
Ответ 5
Если вы хотите принудительно использовать одинаковые буферы, даже при наличии повторяющихся значений, вы можете использовать следующий двухэтапный процесс:
- Оцените свои значения, используя метод = 'first', чтобы python присваивал уникальный рейтинг всем вашим записям. Если есть дублирующее значение (т.е. Привязка в ранге), этот метод выберет первую запись, в которую он входит, и ранг в этом порядке.
df['rank'] = df['value'].rank(method='first')
- Используйте qcut в ранге для определения квантов с равным размером. Ниже пример создает deciles (bins = 10).
df['decile'] = pd.qcut(df['rank'].values, 10).codes
Ответ 6
Как было сказано выше, ранжирование отлично работает с pd.qcut()
Ниже приведен пример использования ранжирования.
Ранее:
pd.qcut(Dataframe['any_categorical_feature'],no_of_bins)
дает ошибку.
После ранжирования:
pd.qcut(Dataframe['any_categorical_feature'].rank(method='first'),no_of_bins)
будет работать без ошибок.