【问题标题】:pandas qcut not putting equal number of observations into each binpandas qcut 没有将相同数量的观察值放入每个 bin
【发布时间】:2016-05-13 01:20:05
【问题描述】:

我有一个数据框,我可以从中选择一列(系列),如下所示:

df:

            value_rank
275488          90
275490          35
275491          60
275492          23
275493          23
275494          34
275495          75
275496          40
275497          69
275498          14
275499          83
...             ...

value_rank 是先前从较大数据集中创建的百分位排名。我想要做的是创建这个数据集的箱,例如五分之一

pd.qcut(df.value_rank, 5, labels=False)


275488    4
275490    1
275491    3
275492    1
275493    1
275494    1
275495    3
275496    2
...      ...

这看起来不错,正如预期的那样,但事实并非如此。

事实上,我有 1569 列。可被 5 个 bin 整除的最接近的数字是 1565,这应该在每个 bin 中给出 1565 / 5 = 313 个观察值。有 4 条额外记录,所以我希望有 4 个 bin 有 314 个观察值,一个有 313 个观察值。相反,我得到了这个:

obs =  pd.qcut(df.value_rank, 5, labels=False)
obs.value_counts()

0    329
3    314
1    313
4    311
2    302

我在 df 中没有 nans,并且想不出发生这种情况的任何原因。从字面上看,我的头发开始撕裂了!

这是一个小例子:

df:

            value_rank
286742               11
286835               53
286865               40
286930               31
286936               45
286955               27
287031               30
287111               36
287269               30
287310               18

pd.qcut 给出了这个:

pd.qcut(df.value_rank, 5, labels = False).value_counts()
bin  count
1    3
4    2
3    2
0    2
2    1

每个 bin 中应该有 2 个观察值,而不是 bin 1 中的 3 个和 bin 2 中的 1 个!

【问题讨论】:

    标签: python pandas binning


    【解决方案1】:

    qcut 试图补偿重复值。如果您将 bin 限制与 qcut 结果一起返回,这是更早的可视化:

    In [42]: test_list = [ 11, 18, 27, 30, 30, 31, 36, 40, 45, 53 ]
    In [43]: test_series = pd.Series(test_list, name='value_rank')
    
    In [49]: pd.qcut(test_series, 5, retbins=True, labels=False)
    Out[49]:
    (array([0, 0, 1, 1, 1, 2, 3, 3, 4, 4]),
     array([ 11. ,  25.2,  30. ,  33. ,  41. ,  53. ]))
    

    您可以看到除了将 bin 限制设置为 30 之外别无选择,因此 qcut 必须从第三个 bin 中的预期值中“窃取”一个并将它们放在第二个 bin 中。我认为这只是在您的百分位数上以更大的比例发生,因为您基本上将他们的等级压缩为 1 到 100 的比例。有什么理由不直接在数据上运行 qcut 而不是百分位数或返回精度更高的百分位数?

    【讨论】:

    • 谢谢,非常有帮助!就我而言,我认为返回原始数据会很困难,因为排名是作为单独筛选过程的一部分创建的。排名也是一个综合排名,由其他几个因素构成,这些因素首先是十分位排名,然后在加权总和中相加。对于每个单独的组件排名,我们决定如何分别处理极端值/负数,因此通过对原始数字进行加权求和然后在此处排名来构建原始排名可能会导致一个子排名中的极端值占主导地位。
    • 其实我还有一个问题:为什么 qcut 没有抛出 non-unique bin edges 错误?这肯定是您强调的问题,即 qcut 由于要排序的值重叠导致的模糊性而无法排序到等宽的 bin 中?
    • @Carl:当计算的 bin 不唯一时会引发该错误。在上面的示例中,您可以看到 11.、25.2、30.、33.、41.、53. 都是不同的。一个极端的例子是在上面的脚本中使用 [1, 1, 1, 1, 1] 的列表。
    【解决方案2】:

    试试下面的代码:

    pd.qcut(df.rank(method='first'),nbins)
    

    【讨论】:

      【解决方案3】:

      如果您必须获得相等(或几乎相等)的 bin,那么这里有一个技巧可以用于 qcut。使用与接受的答案相同的数据,我们可以通过向原始 test_list 添加一些随机噪声并根据这些值进行分箱来强制将它们放入相等的箱中。

      test_list = [ 11, 18, 27, 30, 30, 31, 36, 40, 45, 53 ]
      
      np.random.seed(42) #set this for reproducible results
      test_list_rnd = np.array(test_list) + np.random.random(len(test_list)) #add noise to data
      
      test_series = pd.Series(test_list_rnd, name='value_rank')
      pd.qcut(test_series, 5, retbins=True, labels=False)
      

      输出:

      (0    0
       1    0
       2    1
       3    2
       4    1
       5    2
       6    3
       7    3
       8    4
       9    4
       Name: value_rank, dtype: int64,
       array([ 11.37454012,  25.97573801,  30.42160255,  33.11683016,
               41.81316392,  53.70807258]))
      

      所以,现在我们有两个 0、两个 1、两个 2 和两个 4!

      免责声明

      显然,请自行决定是否使用,因为结果可能会因您的数据而异;例如,您的数据集有多大和/或间距。上面的“技巧”对整数很有效,因为即使我们对 test_list 进行“加盐”,它仍然会排序,因为第 0 组中的值不会大于第 1 组中的值(可能等于,但不是更大)。但是,如果您有浮动,这可能会很棘手,您可能必须相应地减小噪声的大小。例如,如果你有 2.1、5.3、5.3、5.4 等浮点数,你应该通过除以 10 来减少噪音:np.random.random(len(test_list)) / 10。如果你有任意长的浮点数,但是,考虑到“真实”数据中已经存在噪声,您可能一开始就不会遇到这个问题。

      【讨论】:

        【解决方案4】:

        这个问题是由重复值引起的。强制相等大小的 bin 的一种可能解决方案是在对数据帧进行排序后使用索引作为 pd.qcut 的输入:

        import random
        
        df = pd.DataFrame({'A': [random.randint(3, 9) for x in range(20)]}).sort_values('A').reset_index()
        del df['index']
        df = df.reset_index()
        df['A'].plot.hist(bins=30);
        

        图片:https://i.stack.imgur.com/ztjzn.png

        df.head()
        
        df['qcut_v1'] = pd.qcut(df['A'], q=4)
        df['qcut_v2'] = pd.qcut(df['index'], q=4)
        
        df
        

        图片:https://i.stack.imgur.com/RB4TN.png

        df.groupby('qcut_v1').count().reset_index()
        

        图片:https://i.stack.imgur.com/IKtsW.png

        df.groupby('qcut_v2').count().reset_index()
        

        图片:https://i.stack.imgur.com/4jrkU.png

        抱歉,我无法发布图片,因为我在 stackoverflow 上的声望至少没有 10 -.-

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-09-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多