Pandas:基于匹配的多级列的条件的新列值

2024-09-29 01:34:18 发布

您现在位置:Python中文网/ 问答频道 /正文

我有以下带有多级列的dataframe

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: 由于某种原因,下面@A.B提供的解决方案对我不起作用。虽然它是在他的工作和谷歌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)

我仍然有兴趣知道为什么没有转置它就不能工作


Tags: 数据truedffordatahereoutresults
3条回答

尝试:

df["results"] = df.groupby(level=0, axis=1).max().sum(1)
print(df)

印刷品:

   A        B        C       results
  10 20 30 10 20 30 10 20 30        
0  1  3  0  3  0  0  0  1  0       7
1  3  2  0  0  5  0  0  0  0       8
2  0  0  3  0  0  1  0  0  0       4
3  1  0  0  0  0  0  2  0  0       3

想法是用NaN替换0,所以如果使用^{},所有带有NaN的行都将被删除。然后通过^{}获取索引,通过map映射第二和第三个元组值,并将join聚合到每个索引的新列-第一级:

df['results'] = (df.replace(0, np.nan)
                   .stack([0,1])
                   .groupby(level=[0,1])
                   .idxmax()
                   .map(lambda x: f'{x[1]}{x[2]}')
                   .groupby(level=0)
                   .agg('&'.join))
print (df)
   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
  • 按级别0和轴=1分组

  • 您可以使用idxmax以元组的形式获取最大子级别索引(同时跳过NAN)

  • 将函数应用于concat名称的行(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

相关问题 更多 >