首页 > 解决方案 > Pandas:基于匹配多级列条件的新列值

问题描述

我有以下带有多级列的数据框

In [1]: data = {('A', '10'):[1,3,0,1],
                ('A', '20'):[3,2,0,0],
                ('A', '30'):[0,0,3,0],
                ('B', '10'):[3,0,0,0],
                ('B', '20'):[0,5,0,0],
                ('B', '30'):[0,0,1,0],
                ('C', '10'):[0,0,0,2],
                ('C', '20'):[1,0,0,0],
                ('C', '30'):[0,0,0,0]
                }
        df = pd.DataFrame(data)
        df
Out[1]:
   A        B        C
  10 20 30 10 20 30 10 20 30
0  1  3  0  3  0  0  0  1  0
1  3  2  0  0  5  0  0  0  0
2  0  0  3  0  0  1  0  0  0
3  1  0  0  0  0  0  2  0  0

在一个新列results中,我想返回包含每个子集的最大值的组合列名(即二级列)

我想要的输出应该如下所示

Out[2]:
   A        B        C
  10 20 30 10 20 30 10 20 30      results
0  1  3  0  3  0  0  0  1  0  A20&B10&C20
1  3  2  0  0  5  0  0  0  0      A10&B20
2  0  0  3  0  0  1  0  0  0      A30&B30
3  1  0  0  0  0  0  2  0  0      A10&C10

例如第一行:

对于“A”列,最大值在“20”列下,对于“B”列, “10”下只有一个值, “C”列也只有一个值,“20”下只有一个值,所以结果将是A20&B10&C20

编辑:将列中的“+”替换为“&” results,显然我被误解了,你们认为我需要求和,而我需要用分隔符分隔列名

Edit2: 由于某种原因,下面@AB 提供的解决方案对我不起作用。虽然它在他身边工作,并为 google colab 上的示例数据工作。

不知何故.idxmax(skipna = True)导致了ValueError: No axis named 1 for object type Series

我找到了一种解决方法,方法是在此步骤之前转置数据,然后再将其转回。

map_res = lambda x:  ",".join(list(filter(None,['' if isinstance(x[a], float) else (x[a][0]+x[a][1]) for a in x.keys()])))

df['results'] = df.replace(0, np.nan)\
                  .T\  # Transpose here
                  .groupby(level=0)\  # Remove (axis=1) from here
                  .idxmax(skipna = True)\
                  .T\  # Transpose back here
                  .apply(map_res,axis=1)

我仍然很想知道为什么没有转置它就不能工作?

标签: pythonpandaspivot-table

解决方案


  • 按级别 0 和轴 = 1 分组

  • 您使用 idxmax 将最大子级索引作为元组获取(同时跳过 NaN)。

  • 将函数应用于行 (axix-1) 以连接名称

  • 在函数(应用于行)中,迭代键/列并连接列级别。用空字符串替换 Nan(类型为 'float')并稍后过滤它们。

如果您最初有 NaN 并让它们保留,则不需要 df.replace(0, np.nan) 。

map_res = lambda x:  ",".join(list(filter(None,['' if isinstance(x[a], float) else (x[a][0]+x[a][1]) for a in x.keys()])))

df['results'] = df.replace(0, np.nan)\
                  .groupby(level=0, axis=1)\
                  .idxmax(skipna = True)\
                  .apply(map_res,axis=1)

这是输出

    A       B           C               results
10  20  30  10  20  30  10  20  30  
0   1   3   0   3   0   0   0   1   0   A20,B10,C20
1   3   2   0   0   5   0   0   0   0   A10,B20
2   0   0   3   0   0   1   0   0   0   A30,B30
3   1   0   0   0   0   0   2   0   0   A10,C10

推荐阅读