【问题标题】:Creating bins of a column and getting the count in pandas创建列的 bin 并在 pandas 中获取计数
【发布时间】:2017-10-18 05:47:32
【问题描述】:

我有一个熊猫数据框:

item_code    price
   1           15
   1           30
   1           60
   2           50
   3           90
   4           110
   5           130
   4           150

我们可以看到最高价格是 150。我想将它分成 5 个箱子,每个箱子 30 个(分成新的列),并获取该价格箱中每个项目代码的出现次数。

最终 df=

item_code    0-30    31-60    61-90    91-120    121-150
    1         2         1       0         0          0
    2         0         1       0         0          0
    3         0         0       1         0          0
    4         0         0       0         1          1
    5         0         0       0         0          1

item_code 1 在价格范围 0-30 内下跌两次,因此在 0-30 列下看跌计数为 2。item_code 1 在价格范围 31-60 内下跌一次。因此将 count 设为 1.... 其他项目代码也是如此。

我尝试使用 pd.cut

bins = [0, 30, 60, 90, 120,150]
df2 = pd.cut(df['price'], bins)

但它不起作用。

【问题讨论】:

    标签: pandas


    【解决方案1】:

    设置

    cats = ['0-30', '31-60', '61-90', '91-120', '121-150']
    bins = [0, 30, 60, 90, 120, 150]
    

    选项 1
    使用pd.get_dummiespd.DataFrame.join

    df[['item_code']].join(pd.get_dummies(pd.cut(df.price, bins, labels=cats)))
    
       item_code  0-30  31-60  61-90  91-120  121-150
    0          1     1      0      0       0        0
    1          1     1      0      0       0        0
    2          1     0      1      0       0        0
    3          2     0      1      0       0        0
    4          3     0      0      1       0        0
    5          4     0      0      0       1        0
    6          5     0      0      0       0        1
    7          4     0      0      0       0        1
    

    选项 2
    使用 numpy 的 searchsorted 和一些字符串数组添加。

    from numpy.core.defchararray import add
    
    bins = np.arange(30, 121, 30)
    
    b = bins.astype(str)
    cats = add(add(np.append('0', b), '-'), np.append(b, '150'))
    
    df[['item_code']].join(pd.get_dummies(cats[bins.searchsorted(df.price)]))
    
       item_code  0-30  120-150  30-60  60-90  90-120
    0          1     1        0      0      0       0
    1          1     1        0      0      0       0
    2          1     0        0      1      0       0
    3          2     0        0      1      0       0
    4          3     0        0      0      1       0
    5          4     0        0      0      0       1
    6          5     0        1      0      0       0
    7          4     0        1      0      0       0
    

    如果您要对价值 item_codes 的类似值求和。你可以用groupby代替join

    from numpy.core.defchararray import add
    
    bins = np.arange(30, 121, 30)
    
    b = bins.astype(str)
    cats = add(add(np.append('0', b), '-'), np.append(b, '150'))
    
    pd.get_dummies(cats[bins.searchsorted(df.price)]).groupby(df.item_code).sum().reset_index()
    
       item_code  0-30  120-150  30-60  60-90  90-120
    0          1     2        0      1      0       0
    1          2     0        0      1      0       0
    2          3     0        0      0      1       0
    3          4     0        1      0      0       1
    4          5     0        1      0      0       0
    

    选项 3
    使用pd.factorizenp.bincount 的一种非常快速的方法

    from numpy.core.defchararray import add
    
    bins = np.arange(30, 121, 30)
    
    b = bins.astype(str)
    cats = add(add(np.append('0', b), '-'), np.append(b, '150'))
    
    j, c = pd.factorize(bins.searchsorted(df.price))
    i, r = pd.factorize(df.item_code.values)
    n, m = c.size, r.size
    
    pd.DataFrame(
        np.bincount(i * m + j, minlength=n * m).reshape(n, m),
        r, cats).rename_axis('item_code').reset_index()
    
       item_code  0-30  30-60  60-90  90-120  120-150
    0          1     2      1      0       0        0
    1          2     0      1      0       0        0
    2          3     0      0      1       0        0
    3          4     0      0      0       1        1
    4          5     0      0      0       0        1
    

    【讨论】:

      【解决方案2】:

      将参数标签添加到cut 然后groupby 并聚合size

      cats = ['0-30','31-60','61-90','91-120','121-150']
      bins = [0, 30, 60, 90, 120,150]
      df2 = (df.groupby(['item_code', pd.cut(df['price'], bins, labels=cats)])
               .size()
               .unstack(fill_value=0))
      print (df2)
      price      0-30  31-60  61-90  91-120  121-150
      item_code                                     
      1             2      1      0       0        0
      2             0      1      0       0        0
      3             0      0      1       0        0
      4             0      0      0       1        1
      5             0      0      0       0        1
      

      编辑如果您想要通用解决方案,请添加reindex

      print (df)
         item_code  price
      0          1     15
      1          1     30
      2          1     60
      3          2     50
      4          3     90
      5          4    110
      
      cats = ['0-30','31-60','61-90','91-120','121-150']
      bins = [0, 30, 60, 90, 120,150]
      df2 = (df.groupby(['item_code', pd.cut(df['price'], bins, labels=cats)])
              .size()
              .unstack(fill_value=0)
              .reindex(columns=cats, fill_value=0))
      print (df2)
      price      0-30  31-60  61-90  91-120  121-150
      item_code                                     
      1             2      1      0       0        0
      2             0      1      0       0        0
      3             0      0      1       0        0
      4             0      0      0       1        0
      

      【讨论】:

      • @jezrael 不错的解决方案,但是在您的情况下,如果对于 bin 没有可用的值,则该列不会出现在我的最终数据框中。例如:如果我用所有值 0 更新 cats = ['0-30','31-60','61-90','91-120','121-150','151-180'] bins = [0, 30, 60, 90, 120,150,180], there should be one column 151-180`,但它不是。我觉得 piRSquared 解决方案适用于所有情况`
      • 没问题,只调用reindex。我编辑答案。
      【解决方案3】:

      使用groupbyunstack

      In [3835]: bins = np.array(bins)  # for dynamic labels 
      
      In [3836]: labels = map('{0[0]}-{0[1]}'.format, zip(1+bins[:-1], bins[1:]))
      
      In [3837]: (df.groupby(['item_code', pd.cut(df['price'], bins=bins, labels=labels)])
                    .size().unstack(fill_value=0))
      Out[3837]:
      price      1-30  31-60  61-90  91-120  121-150
      item_code
      1             2      1      0       0        0
      2             0      1      0       0        0
      3             0      0      1       0        0
      4             0      0      0       1        1
      5             0      0      0       0        1
      

      【讨论】:

      • 这是正确的,因为如果您的价格 = 0,则会为给定的 bin 捕获它,而左侧是打开的。
      【解决方案4】:

      使用cut + pivot_table

      bins = [0, 30, 60, 90, 120,150]
      labels = ['0-30', '31-60', '61-90', '91-120',' 121-150']
      
      df = df.assign(bins=pd.cut(df.price, bins, labels=labels))\
             .pivot_table('price', 'item_code', 'bins', 'count').fillna(0).astype(int)
      
      print(df)
      bins       0-30  31-60  61-90  91-120   121-150
      item_code                                      
      1             2      1      0       0         0
      2             0      1      0       0         0
      3             0      0      1       0         0
      4             0      0      0       1         1
      5             0      0      0       0         1
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-09-28
        • 2013-07-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-21
        相关资源
        最近更新 更多