我的问题与前一个问题相同:
但是,我仍然希望将0值包含在一个分数中.有没有办法做到这一点?换句话说,如果我有600个值,其中50%为0,其余值为1到100之间,我将如何对fractile 1中的所有0值进行分类,然后对其余的非零值进行分类在分数标签2到10(假设我想要10个fractiles).我可以将0转换为nan,将剩余的非纳米数据切换为9个fractiles(1到9),然后在每个标签上添加1(现在为2到10)并将所有0值标记为手动分离1?即使这很棘手,因为在我的数据集中除了600个值之外,我还有另外几百个在将0转换为nan之前可能已经是nan.
2014年1月26日更新:
我提出了以下临时解决方案.但是,这个代码的问题是,如果高频值不在分布的边缘,那么它会在现有的一组箱子的中间插入一个额外的箱子,并抛弃一切(或很多)一切.
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
Run Code Online (Sandbox Code Playgroud)
luc*_*uca 57
问题是 pandas.qcut选择了bin,这样你在每个bin/quantile中都有相同数量的记录,但是相同的值不能落在多个bin/quantiles中.
解决方案是:
1 - 使用具有此修复程序的pandas> = 0.20.0.他们添加了一个选项duplicates='raise'|'drop'来控制是否在重复的边缘上进行提升或者删除它们,这将导致比指定的更少的bin,以及一些更大(具有更多的元素)比其他更多.
2 - 使用pandas.cut选择根据值本身均匀分布的bin,而pandas.qcut选择bin,以便每个bin中有相同数量的记录
3 - 减少的数目位数.分数越少意味着每个分位数的元素越多
4 -指定自定义分位数范围,例如[0,.50,.75,1.],以获得每个分位数的不等数量的项目
5 -使用DataFrame.rank(method ='first')对数据进行排名.排名为数据框中的每个元素(排名)分配唯一值,同时保持元素的顺序(相同的值除外,它们将按它们出现在数组中的顺序排列,请参见method ='first').这解决了问题,但您可能会将相同(排名前)的值分配到不同的分位数,这可能是正确的,也可能不是,具体取决于您的意图.
例:
pd.qcut(df, nbins) <-- this generates "ValueError: Bin edges must be unique"
Run Code Online (Sandbox Code Playgroud)
然后使用它代替:
pd.qcut(df.rank(method='first'), nbins)
Run Code Online (Sandbox Code Playgroud)
mgo*_*ser 19
另一种方法是引入最小量的噪声,这将人为地创建独特的箱边.这是一个例子:
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)
Run Code Online (Sandbox Code Playgroud)
我们可以使用以下内容重新创建原始错误:
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. ])
Run Code Online (Sandbox Code Playgroud)
OYR*_*YRM 17
您询问有关非唯一bin边缘的binning,我有一个相当简单的答案.在您的示例的情况下,您的意图和qcut的行为分散在pandas.tools.tile.qcut定义bin 的函数中:
bins = algos.quantile(x, quantiles)
其中,因为您的数据是50%0s,导致在任何大于2的分位数值的情况下返回具有值0的多个bin边缘的bin.我看到两种可能的分辨率.在第一个中,分割的空间被均匀地分割,在第一个箱子中分箱所有0,但不仅仅是0.在第二个中,对于大于0的值,可以均匀地划分分段空间,在第一个bin中对所有0和仅0进行分箱.
import numpy as np
import pandas as pd
import pandas.core.algorithms as algos
from pandas import Series
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,我将创建一些随机样本数据,使您的描述符合50%零,其余值介于1和100之间
zs = np.zeros(300)
rs = np.random.randint(1, 100, size=300)
arr=np.concatenate((zs, rs))
ser = Series(arr)
Run Code Online (Sandbox Code Playgroud)
解决方案1:bin 1包含0和低值
bins = algos.quantile(np.unique(ser), np.linspace(0, 1, 11))
result = pd.tools.tile._bins_to_cuts(ser, bins, include_lowest=True)
Run Code Online (Sandbox Code Playgroud)
结果是
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
Run Code Online (Sandbox Code Playgroud)
解决方案2:bin1仅包含0
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)
Run Code Online (Sandbox Code Playgroud)
结果是:
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
Run Code Online (Sandbox Code Playgroud)
我认为可以对解决方案2进行一些工作,使其更漂亮,但您可以看到屏蔽数组是实现目标的有用工具.
小智 11
如果要强制执行相等大小的 bin,即使存在重复值,也可以使用以下 2 步过程:
df['rank'] = df['value'].rank(method='first')
df['decile'] = pd.qcut(df['rank'].values, 10).codes
我在 qcut 方面也遇到了很多问题,因此我使用了 Series.rank 函数,并结合使用这些结果创建了我自己的 bin。我的代码在Github上:
https://gist.github.com/ashishsingal1/e1828ffd1a449513b8f8