MultiIndex 上的 Pandas set_levels:级别值必须是唯一的

问题描述 投票:0回答:4

给定一个数据框

df

                    Value
Category Pool Class      
A        1.0  1.0       1
              9.0       2
B        1.0  1.0       3
C        1.0  1.0       4
              5.0       5

我想将级别

Pool
Class
转换为没有
reset_index
的整数(见下文)。

我尝试使用

get_level_values
set_levels
的组合,就像这样

for c in ['Pool', 'Class']:
    df.index.set_levels(df.index.get_level_values(c).astype(int), level=c, inplace=True)

然而,这提高了

ValueError: Level values must be unique: [1, 1, 1, 1, 1] on level 1

为了了解会发生什么,我还尝试使用

verify_integrity=False
。然后

df.index.set_levels(df.index.get_level_values('Class').astype(int),
                    level='Class', verify_integrity=False, inplace=True)

产生

                    Value
Category Pool Class      
A        1.0  1         1
              1         2
B        1.0  1         3
C        1.0  1         4
              9         5

而我的目标是获得

                    Value
Category Pool Class      
A        1.0  1         1
              9         2
B        1.0  1         3
C        1.0  1         4
              5         5

如何正确实现这一目标?链接

get_level_values
set_levels
是正确的方法吗?为什么
pandas
astype
变换后无法正确设置级别?

我想你可以使用

reset_index
set_index
但是使用这些方法
set_levels
有什么好处?

d = {'Category': str, 'Pool': int, 'Class': int}
df.reset_index(drop=False, inplace=True)
for k, v in d.items():
    df[k] = df[k].astype(v)

df.set_index(list(d.keys()), inplace=True)
python pandas dataframe indexing multi-index
4个回答
14
投票

您可以直接通过

pd.MultiIndex.levels
访问索引级别并提供给
pd.MultiIndex.set_levels
:

df.index = df.index.set_levels(df.index.levels[2].astype(int), level=2)

print(df)

                     Value
Category Pool Class       
A        1.0  1          1
              9          2
B        1.0  1          3
C        1.0  1          4
              5          5

5
投票

以下功能可以作为

get_level_values
的补充:

def set_level_values(midx, level, values):
    full_levels = list(zip(*midx.values))
    names = midx.names
    if isinstance(level, str):
        if level not in names:
            raise ValueError(f'No level {level} in MultiIndex')
        level = names.index(level)
    if len(full_levels[level]) != len(values):
        raise ValueError('Values must be of the same size as original level')
    full_levels[level] = values
    return pd.MultiIndex.from_arrays(full_levels, names=names)

使用此函数,原问题的解决方案将是:

for c in ['Pool', 'Class']:
    df.index = set_level_values(df.index, level=c, values=df.index.get_level_values(c).astype(int))

0
投票

要获取与变量

k
中存储的级别名称相对应的整数位置,您可以使用:

df.index.names.index(k)

因此,如果像 OP 一样,您有

dict
的关卡名称和类型,只需执行以下操作:

levels = [df.index.levels[df.index.names.index(k)].astype(v)
          for k, v in d.items()]
df.index = df.index.set_levels(levels=levels, level=d.keys())

或者,方法链中的相同内容:

df.set_index(
    df.index.set_levels(
        [df.index.levels[df.index.names.index(k)].astype(v)
         for k, v in d.items()],
        level=d.keys())
)...

OP 的

DataFrame
dict
设置:

import pandas as pd

df = pd.DataFrame(
    range(1, 6),
    index=pd.MultiIndex.from_tuples(
        [
            ('A', 1., 1.),
            ('A', 1., 9.),
            ('B', 1., 1.),
            ('C', 1., 1.),
            ('C', 1., 5.)
        ],
        names=['Category', 'Pool', 'Class']
    ),
    columns=['Value']
)
d = {'Category': str, 'Pool': int, 'Class': int}

0
投票

我发现所有的答案要么不起作用,要么对我来说太混乱而无法实施。我确实找到了一个解决方案是绕过 set_index。而是完全形成 df.columns 外部的元组,将其称为 x。然后 df.columns=x 将替换与元组中任何不唯一元素的唯一性无关的列。我还没有测试如果两个元组相同会发生什么。代码片段如下所示: 代码片段未显示,因为我不知道如何格式化它。我认为这个想法是,虽然级别可能具有非唯一元素,但元组是唯一的 - 只是在这里猜测;但它确实有效。

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