【问题标题】:Pandas: New column value based on the matching multi-level column's conditionsPandas:基于匹配多级列条件的新列值
【发布时间】:2021-10-05 22:27:52
【问题描述】:

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

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' 下只有 1 个值 & 对于 'C' 列,它也只是 '20' 下的一个值 & 所以结果是A20&B10&C20

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

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

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

【问题讨论】:

  • 但你不包括零值
  • @Dani Mesejo 是的,零最初是 NaN 值
  • 您真的想要用“+”分隔的列名还是总和?
  • @A.B 用“+”或“&”分隔,而不是总和。抱歉,第一天不清楚
  • @Nagib - am still interested to know why it was is not working without the transpose though? 我认为因为取决于熊猫版本,在某些版本中解决方案应该是错误的。

标签: python pandas pivot-table


【解决方案1】:

想法是将0 替换为NaN,因此如果使用DataFrame.stack,所有带有NaNs 的行都会被删除。然后通过DataFrameGroupBy.idxmax 获取索引,通过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

【讨论】:

    【解决方案2】:

    试试:

    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
    

    【讨论】:

    • 很遗憾这不是我想要的,我需要返回与最大值匹配的两个级别列。
    • @Nagib 查看我的编辑 (level=0)
    • 显然我不清楚,我需要分隔列名(即 A20&B10&C20)而不是最大值的总和
    【解决方案3】:
    • 按级别 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
    

    【讨论】:

    • 感谢您的回复。但它不工作。我收到错误“ValueError: No axis named 1 for object type Series”
    • 您能否分享出现错误的确切行
    • 它出现在最后一行 (df['results'] = df.replace(0, np.nan)\ .groupby(level=0, axis=1)\ .idxmax(skipna = True)\ .apply(map_res,axis=1)) "KeyError: 1 在处理上述异常的过程中,又发生了一个异常:ValueError: No axis named 1 for object type Series"
    • 您确定将其应用于 Dataframe 并且 df 是有效的数据帧吗?
    • 这是一个工作示例colab.research.google.com/drive/…
    猜你喜欢
    • 1970-01-01
    • 2021-11-16
    • 1970-01-01
    • 2020-11-30
    • 2019-11-29
    • 2018-06-10
    • 2019-09-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多