如何基于多索引中级别的子集对数据帧进行切片

2024-09-26 04:49:45 发布

您现在位置:Python中文网/ 问答频道 /正文

我通常处理具有多个级别索引的大型数据帧,我希望根据级别的子集对其进行切片。我不认为有一个简单的方法可以做到这一点。特别是,pandas.IndexSlice没有提供所需的结果,我将在下面解释。你知道吗

假设我们有这样一个数据帧:

                      col0  col1
level0 level1 level2            
0      0      0          0     0
              1          1     1
       1      0          2     2
              1          3     3
1      0      0          4     4
              1          5     5
       1      0          6     6
              1          7     7

我希望我能像这样切开它:

# This doesn't work!
df.loc[[
    (0, 1), 
    (1, 0),
    ]]
# ValueError: operands could not be broadcast together with shapes (2,2) (3,) (2,2)

预期结果如下:

                      col0  col1
level0 level1 level2            
0      1      0          2     2
              1          3     3
1      0      0          4     4
              1          5     5

IndexSlice做了一些不同的事情,而不是这里想要的:

df.loc[pandas.IndexSlice[[0, 1], [1, 0], :]]

它给出了所需级别的所有组合,而不仅仅是所需级别。你知道吗

我将发表我自己的答案与一些解决办法,我已经想出,但没有一个是完美的,所以请张贴任何其他想法。你知道吗

以下是生成数据的代码:

import pandas
import numpy as np

# Size of the problem
n_levels = 3
n_values_per_level = 2

# Build an example MultiIndex
midx = pandas.MultiIndex.from_product(
    [range(n_values_per_level)] * n_levels,
    names=['level{}'.format(level) for level in range(n_levels)]
)

# Generate data of the appropriate number of rows
df = pandas.DataFrame(
    np.transpose([np.arange(len(midx))] * 2), 
    columns=['col0', 'col1'],
    index=midx)

Tags: of数据pandasdfnp级别levelcol1
1条回答
网友
1楼 · 发布于 2024-09-26 04:49:45

以下是我发现的一些变通方法,没有一个是完美的:

拆垛

slicing_midx = pandas.MultiIndex.from_tuples([(0, 1), (1, 0)], 
    names=['level0', 'level1'])
res = df.unstack('level3').loc[slicing_midx].stack('level3')

这很管用。缺点是它创建了一个中间数据结构,这个结构可能非常大。在最坏的情况下(当level3不包含重复值时),中间结构是原始结构大小的平方。你知道吗

重置索引

这个解决方案是由@anky\u 91提出的。将索引重置为数据列,然后在切片后再次追加到索引。你知道吗

# The levels to slice on, in sorted order
slicing_levels = list(slicing_midx.names)

# The levels not to slice on
non_slicing_levels = [level for level in df.index.names 
    if level not in slicing_levels]

# Reset the unneeded index
res = df.reset_index(non_slicing_levels).loc[
    slicing_midx].set_index(non_slicing_levels, append=True)

这是相当有效的。我能想到的唯一缺点是,如果列上有一个多索引(需要检查这个),它可能会弄乱列上的多索引。你知道吗

单独索引和concat

slicing_midx = pandas.MultiIndex.from_tuples([(0, 1), (1, 0)], 
    names=['level0', 'level1'])
res = pandas.concat([df.loc[idx] for idx in slicing_midx],
    keys=slicing_midx, names=slicing_midx.names)

这很管用。但是对于大型数据帧来说,它可能非常慢,因为每个元素都必须单独索引。出于某种原因,它还会删除级别名称。你知道吗

如果len(slicing_midx) << len(df),这可能是最快的

合并/比较多索引

这将索引与熊猫.合并、遮罩和切片。我相信这是最有效的,但也很麻烦。你知道吗

slicing_midx = pandas.MultiIndex.from_tuples([(0, 1), (1, 0)], 
    names=['level0', 'level1'])
df1 = slicing_midx.to_frame().reset_index(drop=True)
df2 = df.index.to_frame().reset_index(drop=True)
df1['key'] = 1
mask = ~pandas.merge(
    df2, df1, on=['level0', 'level1'], how='left')[
    'key'].isnull()
res = df.loc[mask.values]

相关问题 更多 >