我想从具有多个独立索引器的 MultiIndex 中选择多个列。例如
df = pd.DataFrame(
np.zeros((2,4)),
columns=pd.MultiIndex.from_product([('a','b'),(1,2)])
)
来自此数据框
a b
1 2 1 2
0 0 0 0 0
1 0 0 0 0
我想选择
'a'
加 ('b', 1)
下的所有列,如 df[[('a', 1), ('a', 2), ('b', 1)]]
,但我不想显式指定 'a'
下面的列的所有级别。
什么不起作用:
df[['a', ('b', 1)]]
:KeyError: "[('b', 1)] not in index"
df.loc[:, ['a', ('b', 1)]]
:KeyError: "[('b', 1)] not in index"
df[[('a', slice(None)), ('b', 1)]]
:TypeError: unhashable type: 'slice'
df.loc[:, [pd.IndexSlice['a', :], ('b', 1)]]
:TypeError: unhashable type: 'slice'
我想做的另一件类似的事情是:
('a', 1)
加上pd.IndexSlice[:, 2]
您可以单独选择列并将它们连接起来。
out = pd.concat([df.xs('a', axis=1, drop_level=False),
df.xs(('b', 1), axis=1, drop_level=False)],
axis=1)
print(out)
a b
1 2 1
0 0.0 0.0 0.0
1 0.0 0.0 0.0
另一种可能的解决方案:
df.loc[:,('a', slice(None))].join(df.loc[:,('b', 1)])
或者:
df[[(x,y) for x, y in df.columns if (x == 'a') | ((x == 'b') & (y == 1))]]
输出:
a b
1 2 1
0 0.0 0.0 0.0
1 0.0 0.0 0.0
这是使用两个布尔掩码的选项
m1 = df.columns.get_level_values(0).isin(['a'])
m2 = df.columns.get_level_values(1).isin([1])
df.loc[:,m1|m2]
输出:
a b
1 2 1
0 0.0 0.0 0.0
1 0.0 0.0 0.0
对于共享的示例,切片效果很好:
df.loc(axis=1)[('a',1):('b',1)]
a b
1 2 1
0 0.0 0.0 0.0
1 0.0 0.0 0.0
根据您的反馈,您想要更灵活的东西 - 标准切片语法不适合这里。一种选择是使用 select_columns :
# pip install pyjanitor
import janitor
df.select_columns({0:'a'}, ('b',2))
a b
1 2 2
0 0.0 0.0 0.0
1 0.0 0.0 0.0
该语法允许使用字典(键是级别,值是标签)或通过元组在某个级别上的 MultiIndex 上进行选择。
我最终编写了一个辅助函数来执行此操作。它需要一组切片器,并独立使用每个切片器,然后收集所有选定的列。
def mimsc(col_specs):
# usage: df.loc[msms(['A', ('B', 'X')])]
def slicer(df):
cols = []
dfc = df.columns.to_frame()
for cs in col_specs:
cols.append(dfc.loc[cs])
all_cols = pd.concat(cols, ignore_index=True)
return pd.MultiIndex.from_frame(all_cols)
return slicer
用法
df.loc[:, mimsc(['a', ('b', 1)])]
df.loc[:, mimsc([('b', 1), pd.IndexSlice[:, 2]])]
这是一个更通用的版本,也适用于索引
def mims(col_specs, axis=1):
def slicer(df):
cols = []
if axis==1:
dfc = df.columns.to_frame()
elif axis==0:
dfc = df.index.to_frame()
for cs in col_specs:
col = dfc.loc[cs, :]
if isinstance(col, pd.Series):
col = dfc.loc[[cs], :]
cols.append(col)
all_cols = pd.concat(cols, ignore_index=True)
return pd.MultiIndex.from_frame(all_cols)
return slicer
示例
df.T.loc[mims(['a', ('b', 1)], axis=0), :]
df.T.loc[mims([('b', 1), pd.IndexSlice[:, 2]], axis=0), :]