【问题标题】:Method chaining solution to drop column level in pandas DataFrame在 pandas DataFrame 中删除列级别的方法链接解决方案
【发布时间】:2016-11-17 13:12:14
【问题描述】:

在重塑和查询我在 pandas DataFrames 中的数据时,我使用了很多方法链接。有时会为 in 索引(行)和列创建额外的和不必要的级别。如果是这样,例如在索引(行轴)上,这很容易通过使用DataFrame.reset_index() 解决:

df.query('some query')
   .apply(cool_func)
   .reset_index('unwanted_index_level',drop=True) # <====
   .apply(another_cool_func)

reset_index 函数允许一个人继续链式方法并继续使用DataFrame

尽管如此,我从未找到 column_axis 的等效解决方案。有吗?

【问题讨论】:

  • 您是否在寻找.drop 删除列?
  • 嗨 - 不,我想从轴 DataFrame.columns 中的 MultiIndex 中降低一个级别
  • 如果删除列索引级别,您希望如何处理出现的列名重复?

标签: python pandas multi-index method-chaining


【解决方案1】:

您可以只 stack 列(将其移动到索引)并使用 drop=True 调用 reset_index,或者您可以使用 reset_index() 作为起点编写 reset_columns() 方法(参见框架.py#L2940)

df.query('some query')
   .apply(cool_func)
   .stack(level='unwanted_col_level_name')
   .reset_index('unwanted_col_level_name',drop=True)
   .apply(another_cool_func)

替代方案:猴子补丁解决方案

def drop_column_levels(self, level=None, inplace=False):
        """
        For DataFrame with multi-level columns, drops one or more levels.
        For a standard index, or if dropping all levels of the MultiIndex, will revert
        back to using a classic RangeIndexer for column names.

        Parameters
        ----------
        level : int, str, tuple, or list, default None
            Only remove the given levels from the index. Removes all levels by
            default
        inplace : boolean, default False
            Modify the DataFrame in place (do not create a new object)

        Returns
        -------
        resetted : DataFrame
        """
        if inplace:
            new_obj = self
        else:
            new_obj = self.copy()

        new_columns = pd.core.common._default_index(len(new_obj.columns))
        if isinstance(self.index, pd.MultiIndex):
            if level is not None:
                if not isinstance(level, (tuple, list)):
                    level = [level]
                level = [self.index._get_level_number(lev) for lev in level]
                if len(level) < len(self.columns.levels):
                    new_columns = self.columns.droplevel(level)

        new_obj.columns = new_columns
        if not inplace:
            return new_obj

# Monkey patch the DataFrame class
pd.DataFrame.drop_column_levels = drop_column_levels

【讨论】:

  • 太棒了!我不知道stack 函数!它也可能对其他事情派上用场!效果很好
【解决方案2】:

允许继续点链接的一个选项是为pd.DataFrame 类定义一种新方法,以降低列索引级别。这称为猴子补丁,它会降低代码的可移植性。

def reset_column_index(self, inplace=False):
    if inplace:
        self.columns = ['_'.join(tup) for tup in self.columns]
    else:
        c = self.copy()
        c.columns = ['_'.join(tup) for tup in c.columns]
        return c

pd.DataFrame.reset_column_index = reset_column_index

df.query('some query')
   .apply(cool_func)
   .reset_column_index()
   .apply(another_cool_func)

使用此方法会将多索引列展平为单个索引,将名称与下划线合并。

#     foo          bar
#       A     B      A     B
# 0    17     2      0     3
# 1     4    12     40    11

变成

#   foo_A   foo_B   bar_A   bar_B
# 0    17       2       0       3
# 1     4      12      40      11

【讨论】:

  • 感谢您的建议。我认为这是有效的 - 但我倾向于选择已经“打包”的选项以实现兼容性,而不是总是必须定义相同的功能
  • 我完全同意。 @Julien 的回答似乎效果很好。
【解决方案3】:

我自己刚刚找到了另一个解决方案,它使用DataFrame.T 字段,相当于DataFrame.transpose()

df.query('some query')
   .apply(cool_func)
   .T.reset_index('unwanted_col_level_name',drop=True).T
   .apply(another_cool_func)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-06
    • 1970-01-01
    • 2022-07-02
    • 1970-01-01
    相关资源
    最近更新 更多