采用多索引 pandas df 的子集,索引出现意外行为
Taking subset of multi-index pandas df, unexpected behavior with index
我有来自组织为多索引数据帧的特定数据流的许多重复的数据(其中每个重复都被标记为例如 ['rep1', 'rep2', .., 'repN']
)。我经常需要在这些重复的范围内获取较大数据帧的一个子集(例如 df.loc['rep5':'rep50', :]
)。
不过,如果后续子集的索引仍然保留较大数据帧(即 ['rep1', 'rep2', .., 'repN']
)中的整个索引值列表,我还无法找到一种方法来执行此操作。
因此,对于一个简化的示例,给定以下 df:
dfs = [pd.DataFrame({'vals': range(3)}) for i in range(3)]
df = pd.concat(dfs, keys=['l1', 'l2', 'l3'])
df
vals
l1 0 0
1 1
2 2
l2 0 0
1 1
2 2
l3 0 0
1 1
2 2
然后取其中的一个子集:
subset = df.loc['l2':, :]
subset
vals
l2 0 0
1 1
2 2
l3 0 0
1 1
2 2
查看子集的索引,原来的'l1'
索引仍然是:
subset.index
MultiIndex(levels=[['l1', 'l2', 'l3'], [0, 1, 2]],
labels=[[1, 1, 1, 2, 2, 2], [0, 1, 2, 0, 1, 2]
但是,如果我重置该索引级别,'l1'
似乎消失了:
subset.reset_index(level=0)
level_0 vals
0 l2 0
1 l2 1
2 l2 2
0 l3 0
1 l3 1
2 l3 2
然后我可以将 'level_0'
作为索引放回去,基本上达到我想要实现的目标
subset.reset_index(level=0).set_index('level_0', append=True).reorder_levels([1, 0]).index
MultiIndex(levels=[['l2', 'l3'], [0, 1, 2]],
labels=[[0, 0, 0, 1, 1, 1], [0, 1, 2, 0, 1, 2]],
names=['level_0', None])
但这显然是一条非常迂回的路线。我想的另一个选择是删除其他行,但我发现 df.drop
在尝试为多索引 df 执行一系列行时非常笨拙。
如果数据框不是分层的,则不会发生此行为。例如:
df = pd.DataFrame({'vals': range(5)}, index=['a', 'b', 'c', 'd', 'e'])
df.index
Index(['a', 'b', 'c', 'd', 'e'], dtype='object')
然后取一个子集
subset = df.loc[('b', 'c', 'd'),:]
subset.index
Index(['b', 'c', 'd'], dtype='object')
我不清楚为什么会这样。
我认为你需要的是pd.MultiIndex.remove_unused_levels
subset.index.remove_unused_levels()
输出:
MultiIndex(levels=[['l2', 'l3'], [0, 1, 2]],
labels=[[0, 0, 0, 1, 1, 1], [0, 1, 2, 0, 1, 2]])
我有来自组织为多索引数据帧的特定数据流的许多重复的数据(其中每个重复都被标记为例如 ['rep1', 'rep2', .., 'repN']
)。我经常需要在这些重复的范围内获取较大数据帧的一个子集(例如 df.loc['rep5':'rep50', :]
)。
不过,如果后续子集的索引仍然保留较大数据帧(即 ['rep1', 'rep2', .., 'repN']
)中的整个索引值列表,我还无法找到一种方法来执行此操作。
因此,对于一个简化的示例,给定以下 df:
dfs = [pd.DataFrame({'vals': range(3)}) for i in range(3)]
df = pd.concat(dfs, keys=['l1', 'l2', 'l3'])
df
vals
l1 0 0
1 1
2 2
l2 0 0
1 1
2 2
l3 0 0
1 1
2 2
然后取其中的一个子集:
subset = df.loc['l2':, :]
subset
vals
l2 0 0
1 1
2 2
l3 0 0
1 1
2 2
查看子集的索引,原来的'l1'
索引仍然是:
subset.index
MultiIndex(levels=[['l1', 'l2', 'l3'], [0, 1, 2]],
labels=[[1, 1, 1, 2, 2, 2], [0, 1, 2, 0, 1, 2]
但是,如果我重置该索引级别,'l1'
似乎消失了:
subset.reset_index(level=0)
level_0 vals
0 l2 0
1 l2 1
2 l2 2
0 l3 0
1 l3 1
2 l3 2
然后我可以将 'level_0'
作为索引放回去,基本上达到我想要实现的目标
subset.reset_index(level=0).set_index('level_0', append=True).reorder_levels([1, 0]).index
MultiIndex(levels=[['l2', 'l3'], [0, 1, 2]],
labels=[[0, 0, 0, 1, 1, 1], [0, 1, 2, 0, 1, 2]],
names=['level_0', None])
但这显然是一条非常迂回的路线。我想的另一个选择是删除其他行,但我发现 df.drop
在尝试为多索引 df 执行一系列行时非常笨拙。
如果数据框不是分层的,则不会发生此行为。例如:
df = pd.DataFrame({'vals': range(5)}, index=['a', 'b', 'c', 'd', 'e'])
df.index
Index(['a', 'b', 'c', 'd', 'e'], dtype='object')
然后取一个子集
subset = df.loc[('b', 'c', 'd'),:]
subset.index
Index(['b', 'c', 'd'], dtype='object')
我不清楚为什么会这样。
我认为你需要的是pd.MultiIndex.remove_unused_levels
subset.index.remove_unused_levels()
输出:
MultiIndex(levels=[['l2', 'l3'], [0, 1, 2]],
labels=[[0, 0, 0, 1, 1, 1], [0, 1, 2, 0, 1, 2]])