如何从DataFrame中的命名列级别选择值的子集?

问题描述 投票:8回答:2

假设我们有一个包含多个列标题的DataFrame

level_0         A                   B                   C          
level_1         P                   P                   P          
level_2         x         y         x         y         x         y
0       -1.027155  0.667489  0.314387 -0.428607  1.277167 -1.328771
1        0.223407 -1.713410  0.480903 -3.517518 -1.412756  0.718804

我想从命名级别中选择列列表。

required_columns = ['A', 'B']
required_level = 'level_0'

方法1 :(不赞成使用df.loc)

print df.select(lambda x: x[0] in required_columns, axis=1)

这个问题是我必须用0指定级别。如果我使用级别的名称,它将失败。

方法2:

print df.xs('A', level=required_level, axis=1)

这个问题是我只能指定一个值。如果我使用['A','B']则失败。

方法3:

print df.ix[:, df.columns.get_level_values(required_level).isin(required_columns)]

这有效,但不像前两种方法那样简洁! :)

题:

如何使方法1或2工作?或者,有更多的pythonic方式?

MWE:

import pandas as pd
import numpy as np

header = pd.MultiIndex.from_product([['A', 'B', 'C'],
                                     ['P'],
                                     ['x', 'y']],
                                    names=['level_0',
                                           'level_1',
                                           'level_2'])
df = pd.DataFrame(
    np.random.randn(2, 6),
    columns=header
)

required_columns = ['A', 'B']
required_level = 'level_0'

print df
print df.select(lambda x: x[0] in required_columns, axis=1)
print df.xs('A', level=required_level, axis=1)
print df.ix[:, df.columns.get_level_values(required_level).isin(required_columns)]

相关问题:

  1. pandas dataframe select columns in multiindex
  2. Giving a column multiple indexes/headers
python pandas dataframe multi-index
2个回答
6
投票

你可以使用reindex

df.reindex(columns=required_columns, level=required_level)

结果输出:

level_0         A                   B          
level_1         P                   P          
level_2         x         y         x         y
0       -1.265558  0.681565 -0.553084 -1.340652
1        1.705043 -0.512333 -0.785326  0.968391 

3
投票

你考虑过使用IndexSlice吗?它通常要求首先对列进行排序(在原始数据帧中,它们已经排序)。

df.sort_index(axis=1, inplace=True)
>>> df.loc[:, pd.IndexSlice[required_columns, :, :]]
# Output:
# level_0         A                   B          
# level_1         P                   P          
# level_2         x         y         x         y
# 0        0.079368 -1.083421  0.129979 -0.558004
# 1       -0.157843 -1.176632 -0.219833  0.935364

更新

您选择的方法实际上取决于您首先选择数据的原因以及是否需要通过选择修改原始数据。

首先,为了使示例更具挑战性,让我们使用MultiIndex数据帧,该数据帧在不同级别具有相同的值并且未排序。

required_columns = ['A', 'B']  # Per original question.
required_level = 'level_0'  # Per original question.

np.random.seed(0)
idx = pd.MultiIndex.from_product([list('BAC'), list('AB')], names=['level_0', 'level_1'])
df = pd.DataFrame(np.random.randn(2, len(idx)), columns=idx)
>>> df
# Output:
# level_0         B                   A                   C          
# level_1         A         B         A         B         A         B
# 0        1.764052  0.400157  0.978738  2.240893  1.867558 -0.977278
# 1        0.950088 -0.151357 -0.103219  0.410599  0.144044  1.454274

返回数据的副本

如果您只需要直接查看数据或在管道中进行后续计算,那么@root提到的reindex方法和文档中讨论的here是一个不错的选择。

df2 = df.reindex(columns=required_columns, level=required_level)
>>> df2
# Output:
# level_0         A                   B          
# level_1         A         B         A         B
# 0        0.978738  2.240893  1.764052  0.400157
# 1       -0.103219  0.410599  0.950088 -0.151357

但是,如果您尝试修改此数据框,则更改将不会反映在原始数据框中。

df2.iloc[0, 0] = np.nan
>>> df  # Check values in original dataframe.  None are `NaN`.
# Output:
# level_0         B                   A                   C          
# level_1         A         B         A         B         A         B
# 0        1.764052  0.400157  0.978738  2.240893  1.867558 -0.977278
# 1        0.950088 -0.151357 -0.103219  0.410599  0.144044  1.454274

修改数据

另一种方法是使用loc的布尔索引。您可以使用条件列表推导来与get_level_values一起选择所需的列:

cols = [col in required_columns for col in df.columns.get_level_values(required_level)]
>>> df.loc[:, cols]
# Output:
# level_0         B                   A          
# level_1         A         B         A         B
# 0        1.764052  0.400157  0.978738  2.240893
# 1        0.950088 -0.151357 -0.103219  0.410599

如果你正在切割索引而不是列,那么显然需要在上面的代码片段中将df.columns.get_level_values更改为df.index.get_level_values

您还可以使用loc修改原始数据:

df2 = df.copy()
df2.loc[:, cols] = 1
>>> df2
# Output:
# level_0  B     A            C          
# level_1  A  B  A  B         A         B
# 0        1  1  1  1  1.867558 -0.977278
# 1        1  1  1  1  0.144044  1.454274

结论

虽然select是返回多索引数据视图的好选择,但使用loc进行布尔索引可以查看或修改数据。

我会使用上面描述的loc方法,而不是方法1或方法2。

截至pandas 0.20.0,ix方法已被弃用。我不推荐方法3。

© www.soinside.com 2019 - 2024. All rights reserved.