【问题标题】:How to extract group categories when the category names are mixed with the data当类别名称与数据混合时如何提取组类别
【发布时间】:2022-01-11 23:40:16
【问题描述】:

我有以下数据结构。其中类别名称与产品名称混合在一起

df = pd.DataFrame(data={'name':['Category A', 'Subcategory A.A', 'Product A', 'Product B', 'Category B', 'Product C'],'values':["", "", 1,2,"", 3]})

name                values
Category A  
Subcategory A.A 
Product A           1
Product B           2
Category B  
Product C           3

name 列中没有值的每个条目都是类别名称。

有没有办法将pandas DataFrame 转换成如下结构?

name        values      category
Product A   1           Category A, Subcategory A.A
Product B   2           Category A, Subcategory A.A
Product C   3           Category B

感谢任何帮助。

【问题讨论】:

    标签: python pandas dataframe group-by pandas-groupby


    【解决方案1】:

    我不确定是否有一种非常熊猫式的方式来处理这个问题,所以我想出了一个简单的 Python 解决方案:

    new_data = {'name':[], 'values': [], 'category': []}
    lasts = {}
    for idx, row in df.iterrows():
        tp, val = row['name'].split(' ')
        if row['values'] == '':
            lasts[tp] = val
            # Reset the subcategory if a new category is encountered
            if tp == 'Category' and 'Subcategory' in lasts:
                del lasts['Subcategory']
        else:
            new_data['category'].append(', '.join(f'{k} {v}' for k, v in zip(lasts.keys(), lasts.values())))
            for k in row.index:
                new_data[k].append(row[k])
    df = pd.DataFrame(new_data)
    

    输出:

    >>> df
            name  values                     category
    0  Product A       1  Category A, Subcategory A.A
    1  Product B       2  Category A, Subcategory A.A
    2  Product C       3                   Category B
    

    【讨论】:

      【解决方案2】:

      使用cumsum为类别块创建自定义分组并使用groupby.apply返回非类别行+新类别列:

      # create custom grouping per category block
      newgroup = df['values'].eq('') & df['values'].shift().ne('')
      groups = newgroup.cumsum()
      
      # given group g, return subframe of non-category rows + category name
      def categorize(g):
          is_category = g['values'].eq('')
          category = ', '.join(g.loc[is_category, 'name']) # join category names by comma
          return g.loc[~is_category].assign(category=category) # return non-category rows with new category column
      
      # apply custom function to each group
      df.groupby(groups).apply(categorize).droplevel(0)
      

      输出:

              name values                     category
      2  Product A      1  Category A, Subcategory A.A
      3  Product B      2  Category A, Subcategory A.A
      5  Product C      3                   Category B
      

      详情

      1. 当当前values 为空而前一个values 不为空时,每个类别块开始,因此我们可以generate pseudo-groups using cumsum。这里的组显示为一列,仅供视觉参考:

        newgroup = df['values'].eq('') & df['values'].shift().ne('')
        groups = newgroup.cumsum()
        
        #               name  values  groups
        # 0       Category A               1
        # 1  Subcategory A.A               1
        # 2        Product A       1       1
        # 3        Product B       2       1
        # 4       Category B               2
        # 5        Product C       3       2
        
      2. 在每个组中,通过连接所有类别行中的name 来获取类别字符串。那么我们assign新的分类名称后就可以返回非分类的行了:

        def categorize(g):
            is_category = g['values'].eq('')
            category = ', '.join(g.loc[is_category, 'name']) # join category rows by comma
            return g.loc[~is_category].assign(category=category) # return non-category rows with new category column
        
      3. 将此函数传递给groupby.apply:

        df.groupby(groups).apply(categorize).droplevel(0)
        
        #         name values                     category
        # 2  Product A      1  Category A, Subcategory A.A
        # 3  Product B      2  Category A, Subcategory A.A
        # 5  Product C      3                   Category B
        

      【讨论】:

      • 太棒了,非常感谢您的详细解释
      猜你喜欢
      • 1970-01-01
      • 2014-01-10
      • 1970-01-01
      • 2017-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多