如何在 Pandas Multiindex 中重置单个索引?

How to reset a single index in Pandas Multiindex?

我有一个由多个数据帧构建的 MultiIndex DataFrame,我需要重置第一个索引。除了第一个索引,我不想改变任何东西;应保留其他所有内容的整个结构和顺序。

我目前的情况是这样的:

index = pd.MultiIndex.from_tuples([('0', 'Albert'),
                                   ('0', 'Isaac'),
                                   ('0', "Charles"),
                                   ('1', 'James'),
                                   ('1', 'Paul'),
                                   ('0', 'Wolfgang'),
                                   ('0', 'Enrico'),
                                   ('0', "John"),
                                   ('1', 'Marie'),
                                   ('1', 'Carol'),
                                   ('2', "Solomon"),
                                   ("2", "Joseph"),
                                   ("2", "Phil"),
                                   ('2', 'Danielle')],
                                  names=['class', 'name'])
columns = ('High', 'Average')
df = pd.DataFrame([(98.8, 97.9),
                   (100.0, 99.9),
                   (76.5, 64.2),
                   (99.3, 98.9),
                   (87.2, 83.3),
                   (98.8, 96.5),
                   (100.0, 97.7),
                   (88.6, 64.2),
                   (99.3, 78.3),
                   (87.2, 81.0),
                   (78.8, 65.9),
                   (99.0, 95.4),
                   (86.1, 74.7),
                   (97.9, 91.1)],
                  index=index,
                  columns=columns)

这给出了一个如下所示的数据框:

                 High  Average
class name                    
0     Albert     98.8     97.9
      Isaac     100.0     99.9
      Charles    76.5     64.2
1     James      99.3     98.9
      Paul       87.2     83.3
0     Wolfgang   98.8     96.5
      Enrico    100.0     97.7
      John       88.6     64.2
1     Marie      99.3     78.3
      Carol      87.2     81.0
2     Solomon    78.8     65.9
      Joseph     99.0     95.4
      Phil       86.1     74.7
      Danielle   97.9     91.1

我只想将“class”索引重置为从 0 开始并迭代到“class”中的最后一个条目。最终结果应如下所示:

                 High  Average
class name                    
0     Albert     98.8     97.9
      Isaac     100.0     99.9
      Charles    76.5     64.2
1     James      99.3     98.9
      Paul       87.2     83.3
2     Wolfgang   98.8     96.5
      Enrico    100.0     97.7
      John       88.6     64.2
3     Marie      99.3     78.3
      Carol      87.2     81.0
4     Solomon    78.8     65.9
      Joseph     99.0     95.4
      Phil       86.1     74.7
      Danielle   97.9     91.1

我不知道该怎么做;我试过使用重新索引,set_levels;其他一些事情。我觉得这应该是一件简单的事情,所以我必须在内置函数的选项中遗漏一些东西(这将是我的首选方法,但此时即使是“hacky”就足够了!)。

一个选项是

df = df1.reset_index()
df['class'] = df['class'].astype(int).diff().fillna(0).ne(0).cumsum().astype(int)
df = df.set_index(['class', 'name'])
>>> df
                index   High  Average
class name                           
0     Albert        0   98.8     97.9
      Isaac         1  100.0     99.9
      Charles       2   76.5     64.2
1     James         3   99.3     98.9
      Paul          4   87.2     83.3
2     Wolfgang      5   98.8     96.5
      Enrico        6  100.0     97.7
      John          7   88.6     64.2
3     Marie         8   99.3     78.3
      Carol         9   87.2     81.0
4     Solomon      10   78.8     65.9
      Joseph       11   99.0     95.4
      Phil         12   86.1     74.7
      Danielle     13   97.9     91.1

这也不适用于索引,因此它是在 class 列中进行一些更改之前和之后设置的。

评论

在我的第一次尝试中,我使用了这个:

df = df.reset_index()
df['class'] = df['class'].astype(int).diff().abs().cumsum().fillna(0).astype(int)
df = df.set_index(['class', 'name'])

这将为给定示例生成正确的结果,因为“class”列中的差异仅为 -11。如果差异不同于-101,这会生成错误的结果。

从第一个索引级别创建一个新索引,然后与第二个索引级别合并并将其设置为新索引:

import numpy as np

idx = df.index.get_level_values('class')
new_idx = pd.Index((idx != np.roll(idx, 1)).cumsum() -1, name='class')

df = df.set_index(pd.MultiIndex.from_arrays(
                      [new_idx, df.index.get_level_values('name')]))
>>> df
                 High  Average
class name                    
0     Albert     98.8     97.9
      Isaac     100.0     99.9
      Charles    76.5     64.2
1     James      99.3     98.9
      Paul       87.2     83.3
2     Wolfgang   98.8     96.5
      Enrico    100.0     97.7
      John       88.6     64.2
3     Marie      99.3     78.3
      Carol      87.2     81.0
4     Solomon    78.8     65.9
      Joseph     99.0     95.4
      Phil       86.1     74.7
      Danielle   97.9     91.1

@It_is_Chris

改进了解决方案

详情:

>>> idx.tolist()
['0', '0', '0', '1', '1', '0', '0', '0', '1', '1', '2', '2', '2', '2']

>>> np.roll(idx, 1).tolist()
['2', '0', '0', '0', '1', '1', '0', '0', '0', '1', '1', '2', '2', '2']

现在比较两个列表的每个元素:'0' != '2''0' != '0' 直到 '2' != '2'。你有一个布尔掩码

>>> (idx != np.roll(idx, 1)).tolist()
[True, False, False, True, False, True, False,
 False, True, False, True, False, False, False]

在此布尔掩码上应用累加和减一以获得新索引:

>>> ((idx != np.roll(idx, 1)).cumsum() - 1).tolist()
[0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 4]

现在用新索引和第二级重建索引:

>>> pd.MultiIndex.from_arrays([new_idx, df.index.get_level_values('name')])
MultiIndex([(0,   'Albert'),
            (0,    'Isaac'),
            (0,  'Charles'),
            (1,    'James'),
            (1,     'Paul'),
            (2, 'Wolfgang'),
            (2,   'Enrico'),
            (2,     'John'),
            (3,    'Marie'),
            (3,    'Carol'),
            (4,  'Solomon'),
            (4,   'Joseph'),
            (4,     'Phil'),
            (4, 'Danielle')],
           names=['class', 'name'])

一个选项是重置索引,然后从中创建组,然后重新索引回来

df2 = df.reset_index()
df2['class'] = (df2['class'] != df2['class'].shift()).cumsum() - 1
df = df2.set_index(['class', 'name'])
>>>
                 High  Average
class name
0     Albert     98.8     97.9
      Isaac     100.0     99.9
      Charles    76.5     64.2
1     James      99.3     98.9
      Paul       87.2     83.3
2     Wolfgang   98.8     96.5
      Enrico    100.0     97.7
      John       88.6     64.2
3     Marie      99.3     78.3
      Carol      87.2     81.0
4     Solomon    78.8     65.9
      Joseph     99.0     95.4
      Phil       86.1     74.7
      Danielle   97.9     91.1