【问题标题】:Pandas dataframes with multi-level columns:rename a specific level of column so that it's same as another level具有多级列的 Pandas 数据框:重命名特定级别的列,使其与另一个级别相同
【发布时间】:2020-07-21 11:50:21
【问题描述】:

对于看似令人困惑的标题,我们深表歉意。我正在使用 Pandas 读取 Excel 数据。但是,原始 Excel 数据具有多行标题,并且一些单元格被合并。它看起来像这样:

它在我的 Jupyter Notebook 中显示如下

我的计划是将第 2 级作为我的列名并删除第 0 级。但是原始数据有大约 15 列显示为“未命名...”,我想知道是否可以在删除 level0 列名之前重命名这些列。

理想的输出如下所示:

我可能会重复这样做,所以我没有先将其保存为 CSV,然后在 Pandas 中读取。现在,我在修复列名上花费的时间比我愿意承认的要长。我想知道是否有一种方法可以使用函数而不是重命名每个感兴趣的列。

谢谢。

【问题讨论】:

    标签: python pandas rename


    【解决方案1】:

    我认为这里最简单的是使用列表理解 - 仅在没有 Unnamed 文本时获取 MultiIndex 的值:

    df.columns = [first if 'Unnamed' in second else second for first, second in df.columns]
    print (df)
       Purchase/sell_time  Quantity  Price Side
    0 2020-04-09 15:22:00        20     43    B
    1 2020-04-09 16:22:00        30     56    S
    

    但是,如果实际数据中可能存在更多级别,则应复制某些列,因此无法选择它们(如果按重复列选择会获取所有列,而不仅仅是一个列,例如 df['dup_column_name'])。

    你可以测试一下:

    print (df.columns[df.columns.duplicated(keep=False)])
    

    然后我建议加入所有未命名的级别以防止它:

    df.columns = ['_'.join(y for y in x if 'Unnamed' not in y) for x in df.columns]
    print (df)
       Purchase/sell_time  Purchase/sell_time_Quantity  Purchase/sell_time_Price  \
    0 2020-04-09 15:22:00                           20                        43   
    1 2020-04-09 16:22:00                           30                        56   
    
      Side  
    0    B  
    1    S  
    

    【讨论】:

    • 您好,抱歉回复晚了。非常感谢您的回复。这太详细了,超出了我的预期。感谢您加入所有未命名级别以防止重复列的额外建议。后来确实成了问题。再次感谢。
    【解决方案2】:

    您的列是多索引的,并且索引是不可变的,这意味着您不能只更改其中的一部分。这就是为什么我建议检索多索引的两个级别,然后创建一个包含所需列的数组并用它替换 DataFrame 列,如下所示:

    # First I reproduce your dataframe
    df1 = pd.DataFrame({("Purchase/sell_time","Unnamed:"):  pd.date_range("2020-04-09 15:22:00", 
                                                            freq="H", periods = 2),
                        ("Purchase/sell_time", "Quantity"): [20,30],
                        ("Purchase/sell_time", "Price"): [43, 56],
                        ("Side", "Unnamed:") : ["B", "S"]})
    df1 = df1.sort_index()
    

    看起来像这样:

     Purchase/sell_time                    Side
                 Unnamed: Quantity Price Unnamed:
    0 2020-04-09 15:22:00       20    43        B
    1 2020-04-09 16:22:00       30    56        S
    

    如您所见,该列是一个多索引:

    MultiIndex([('Purchase/sell_time', 'Unnamed:'),
                ('Purchase/sell_time', 'Quantity'),
                ('Purchase/sell_time',    'Price'),
                (              'Side', 'Unnamed:')],
               )
    
    # I retrieve the first and second level of the multiindex then create an array conditionally 
    # on the second level not starting with "Unnamed" 
    first_header = df1.columns.get_level_values(0)
    second_header = df1.columns.get_level_values(1)
    merge_header = np.where(second_header.str.startswith("Unnamed:"),
                            first_header, second_header)
    df1.columns = merge_header
    

    结果如下:

     Purchase/sell_time  Quantity  Price Side
    0 2020-04-09 15:22:00        20     43    B
    1 2020-04-09 16:22:00        30     56    S
    

    希望对你有帮助

    【讨论】:

    • 嗨 Raphaele,非常感谢您的帮助。我没有想到np.where 方法。我喜欢你使用get_level_values(0)get_level_values(1) 清楚地显示每个列级别的值的方式。非常干净和可读。感谢您不仅展示了您的方法为何有效,而且还以我易于理解的方式呈现了它。真的很感激。
    • 就您的代码添加一个简单的问题:df1 = df1.sort_index() 的目的是什么?我假设您在这里尝试对列的顺序进行排序,在这种情况下,我们不需要添加axis = 1 吗?谢谢。
    猜你喜欢
    • 1970-01-01
    • 2014-08-09
    • 2018-09-14
    • 1970-01-01
    • 2021-11-12
    • 1970-01-01
    • 2016-10-13
    • 2019-02-04
    • 2020-02-21
    相关资源
    最近更新 更多