将 .loc 与 DataFrame 的级别子集一起使用

Using .loc with on subset of levels from a DataFrame with MultIndex

提问人:endive1783 提问时间:11/13/2023 更新时间:11/13/2023 访问量:49

问:

给定一个具有 3 个多索引级别的数据帧:

import pandas as pd

df = pd.concat({'a': pd.Series([1,2,3,1]),
                'b': pd.Series([5,4,3,5]),
                'c': pd.Series(range(9,13)),
                'd': pd.Series(range(13,17))}, axis=1).set_index(['a', 'b', 'c'])
>>>         d
    a b c       
    1 5 9   13
    2 6 10  14
    3 7 11  15
    4 8 12  16

我想将 loc 与前 2 个级别的索引列表一起使用:

idx = pd.MultiIndex.from_arrays([[1, 2], [5, 4]], names=('a', 'b'))
>>> MultiIndex([(1, 5),
                (2, 6)],
               names=['a', 'b'])

我尝试将 .loc 与单个索引一起使用:

df.loc[idx[0]]
>>>      d
    c     
    9   13
    12  16

df.loc[idx[1]]
>>>      d
    c     
    10  14

我希望返回与df.loc[idx]

pd.concat([df.loc[i] for i in idx])
>>>     d
    c     
    9   13
    12  16
    10  14

但我回来了df.loc[idx]

ValueError: operands could not be broadcast together with shapes (2,2) (3,) (2,2)

还有什么比获得预期结果更干净的吗?pd.concat([df.loc[i] for i in idx])

Python Pandas 多索引

评论


答:

3赞 mozway 11/13/2023 #1

具有 MultiIndex 的 loc 需要相同的级别,解决方法是将额外的级别临时设置为列:

levels = df.index.names.difference(idx.names)

out = df.reset_index(levels).loc[idx].set_index(levels, append=True)

加入

out = df.join(pd.DataFrame(index=idx), how='right')

输出:

         d
a b c     
1 5 9   13
    12  16
2 4 10  14

如果您想在此过程中删除/中:ab

levels = df.index.names.difference(idx.names)
out = df.reset_index(levels).loc[idx].set_index(levels)

艺术

out = df.join(pd.DataFrame(index=idx), how='right').droplevel(idx.names)

输出:

     d
c     
9   13
12  16
10  14

评论

0赞 endive1783 11/13/2023
我尝试了第一个技巧,发现它很丑,但我喜欢加入选项,谢谢!
1赞 sammywemmy 11/13/2023 #2

将索引视为元组列表并传递给 janitor.select

# pip install pyjanitor
import janitor

# index sorting is done here 
# to avoid performance warning
df.sort_index().select(rows=list(idx))

         d
a b c     
1 5 9   13
    12  16
2 4 10  14

另一种选择是使用字典:

# a safer option would be to use get_level_values
# instead of levels
rows=dict(zip(idx.names, idx.levels))
df.select(rows=rows)

         d
a b c     
1 5 9   13
    12  16
2 4 10  14

另一个选项是:pd.xs

df = df.sort_index()
selected = [df.xs(key=tup, drop_level=False) for tup in idx]
pd.concat(selected)

         d
a b c     
1 5 9   13
    12  16
2 4 10  14