【问题标题】:Counting all combinations of values in multiple columns计算多列中值的所有组合
【发布时间】:2022-02-21 23:40:04
【问题描述】:

以下是 1,2 或 3 星评级的项目示例。 我正在尝试计算每个月的所有项目评级(星级)组合。

在以下示例中,项目 10 在第 1 个月进行了评级,并且有两个评级等于 1,一个评级等于 2,一个评级等于 3。

inp = pd.DataFrame({'month':[1,1,1,1,1,2,2,2], 
                    'item':[10,10,10,10,20,20,20,20], 
                    'star':[1,2,1,3,3,2,2,3]}
                  )

 month item star
0   1   10  1
1   1   10  2
2   1   10  1
3   1   10  3
4   1   20  3
5   2   20  2
6   2   20  2
7   2   20  3

对于上面给定的输入帧输出应该是:

   month    item    star_1_cnt  star_2_cnt  star_3_cnt
0   1       10      2           1           1
1   1       20      0           0           1
2   2       20      0           2           1

我正在尝试从以下代码开始解决问题, 哪个结果仍然需要转换为输出帧的所需格式,并且给出了错误的答案:

1   20  3   (1, 1)
2   20  3   (1, 1)

无论如何,应该有更好的方法来创建输出表,然后敲定这个:

months = [1,2]
items = [10,20]
stars = [1,2,3]

d = {'month': [], 'item': [], 'star': [], 'star_cnts': [] }

for month in months:
    for star in stars:
        for item in items:
            star_cnts=dict(inp[(inp['item']==item) & (inp['star']==star)].value_counts()).values()
            d['month'].append(month)
            d['item'].append(item)
            d['star'].append(star)
            d['star_cnts'].append(star_cnts)
            
pd.DataFrame(d)

    month   item    star    star_cnts
0   1       10      1       (2)
1   1       20      1       ()
2   1       10      2       (1)
3   1       20      2       (2)
4   1       10      3       (1)
5   1       20      3       (1, 1)
6   2       10      1       (2)
7   2       20      1       ()
8   2       10      2       (1)
9   2       20      2       (2)
10  2       10      3       (1)
11  2       20      3       (1, 1)

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    您可以在整个 DataFrame 上使用value_counts 并取消堆栈:

    out = (
     inp.value_counts()
        .unstack('star', fill_value=0)
     )
    

    或者,您可以使用crosstab

    由于它需要一维数据作为输入,您可以使用元组:

    cols = ['month','item']
    out = pd.crosstab(inp[cols].apply(tuple, axis=1), inp['star'])
    
    out.index = pd.MultiIndex.from_tuples(out.index, names=cols)
    

    输出:

    star        1  2  3
    month item         
    1     10    2  1  1
          20    0  0  1
    2     20    0  2  1
    
    格式化输出

    @ansev 已经很好地证明了这一点

    (inp.value_counts()
        .unstack('star', fill_value=0)
        .rename(lambda c: f'star_{c}_cnt', axis=1)
        .reset_index()
        .rename_axis(columns=None)
     )
    

    输出:

       month  item  star_1_cnt  star_2_cnt  star_3_cnt
    0      1    10           2           1           1
    1      1    20           0           0           1
    2      2    20           0           2           1
    

    【讨论】:

      【解决方案2】:

      Series.value_counts + Series.unstack 转换为数据帧

      out = inp.value_counts()\
               .unstack('star', fill_value=0)\
               .rename(lambda x: f'star_{x}_cnt', axis=1)\
               .reset_index().rename_axis(columns=None)
      
      print(out)
      
         month  item  star_1_cnt  star_2_cnt  star_3_cnt
      0      1    10           2           1           1
      1      1    20           0           0           1
      2      2    20           0           2           1
      

      【讨论】:

      • 你不需要groupby,value_counts就足够了
      【解决方案3】:

      一个选项,带有 pivot_table :

      (inp
      .pivot_table(index=['month', 'item'], 
                   values = 'star', 
                   columns='star', 
                   aggfunc='size', 
                   fill_value = 0)
      .rename(columns = lambda col: f"star_{col}_cnt")
      .rename_axis(columns = None)
      .reset_index()
      )
         month  item  star_1_cnt  star_2_cnt  star_3_cnt
      0      1    10           2           1           1
      1      1    20           0           0           1
      2      2    20           0           2           1
      

      另一个选项,使用 groupby(pivot_table 是 groupby + unstack 的包装):

      (inp
      .groupby(['month', 'item', 'star'])
      .size()
      .unstack(fill_value = 0)
      .rename(columns = lambda col: f"star_{col}_cnt")
      .rename_axis(columns = None)
      .reset_index()
      )
      
         month  item  star_1_cnt  star_2_cnt  star_3_cnt
      0      1    10           2           1           1
      1      1    20           0           0           1
      2      2    20           0           2           1
      

      另一个选项,pd.get_dummies + groupby

      (pd.get_dummies(inp, columns=['star'])
      .groupby(['month', 'item'])
      .sum()
      .add_suffix('_cnt')
      .reset_index()
      )
         month  item  star_1_cnt  star_2_cnt  star_3_cnt
      0      1    10           2           1           1
      1      1    20           0           0           1
      2      2    20           0           2           1
      

      性能方面,只有测试可以判断 - 我希望 pivot_table 比 groupby 慢

      【讨论】:

        【解决方案4】:

        这对pd.get_dummies 来说似乎是个不错的问题:

        new_df = (
            pd.concat([df, pd.get_dummies(df['star'])], axis=1)
            .groupby(['month', 'item'], as_index=False)
            [df['star'].unique()]
            .sum()
        )
        

        输出:

        >>> new_df
           month  item  1  2  3
        0      1    10  2  1  1
        1      1    20  0  0  1
        2      2    20  0  2  1
        

        重命名:

        u = df['star'].unique()
        new_df = (
            pd.concat([df, pd.get_dummies(df['star'])], axis=1)
            .groupby(['month', 'item'], as_index=False)
            [u]
            .sum()
            .rename({k: f'star_{k}_cnt' for k in df['star'].unique()}, axis=1)
        )
        

        输出:

        >>> new_df
           month  item  star_1_cnt  star_2_cnt  star_3_cnt
        0      1    10           2           1           1
        1      1    20           0           0           1
        2      2    20           0           2           1
        

        强制性的一个(或两个)衬垫:

        # Renames the columns
        u = df['star'].unique()
        new_df = pd.concat([df, pd.get_dummies(df['star'])], axis=1).groupby(['month', 'item'], as_index=False)[u].sum().rename({k: f'star_{k}_cnt' for k in df['star'].unique()}, axis=1)
        

        【讨论】:

          猜你喜欢
          • 2017-12-27
          • 1970-01-01
          • 1970-01-01
          • 2016-03-22
          • 1970-01-01
          • 2022-01-15
          • 1970-01-01
          • 2023-03-17
          • 1970-01-01
          相关资源
          最近更新 更多