Pandas: df.set_value() 方法重置 MultiIndex 的列名

0 投票
2 回答
3430 浏览
提问于 2025-04-17 20:14

我正在写一个应用程序,使用 pandas(版本 0.10.1)来存储底层数据模型,这个模型是一个三层的 MultiIndex 数据框。这个模型是一个光谱,索引的最上层是原子跃迁。

一个简单的数据框可能看起来像这样:

                               Pos     Sigma       Ampl  Line center Identifier
H-alpha-6697.6 30-30 Comp2  -3.600  0.774000  33.058000       6699.5          b
                     Comp3   3.538  2.153000  28.054000       6699.5          c
                     Contin    NaN       NaN   0.000000          NaN        NaN
                     Comp4   1.384  0.921000  37.504000       6699.5          d
                     Comp1  -2.124  1.977000  69.166000       6699.5          a
               31-31 Comp2  -3.292  0.884603  49.813423       6699.5          b
                     Comp3   3.600  2.299000  19.999000       6699.5          c
                     Contin    NaN       NaN   0.000000          NaN        NaN
                     Comp4   1.692  1.009000  22.222000       6699.5          d
                     Comp1  -1.262  2.534000  68.002000       6699.5          a

在某个时候,我需要能够创建一个不同的跃迁,比如 H-beta,使用 H-alpha 作为模板。我理想的做法是像这样 df.ix['H-beta-wavelength'] = df.ix['H-alpha-6697.6'],但这样做是不可能的。因此,我尝试遵循这个例子:在 pandas MultiIndex 前面添加一个层级

然而,上面的例子需要设置多重索引层级的 .names,才能重新排序它们。而 names 属性是在初始化数据框时设置的,但在构建数据框的过程中,我大量依赖 set_values() 方法,这样做会破坏 names 属性,或者说会把它们设置为 [None, None, None]

示例:

In [68]: df
Out[68]: 
                                  Pos  Sigma     Ampl  Line center Identifier
Transition     Rows  Component                                               
Center: 6699.5 26-26 Comp2     -3.846  0.657  15.2740       6699.5          b
                     Comp3      2.924  1.449  31.3930       6699.5          c
                     Contin       NaN    NaN   0.0000          NaN        NaN
                     Comp4      8.030  1.009   7.0831       6699.5          d
                     Comp1     -1.816  2.153  50.2750       6699.5          a

In [69]: df.set_value(('Center: 5044.3', '26-26', 'Comp1'), 'Sigma', 2.457)
Out[69]: 
                               Pos  Sigma     Ampl  Line center Identifier
Center: 6699.5 26-26 Comp2  -3.846  0.657  15.2740       6699.5          b
                     Comp3   2.924  1.449  31.3930       6699.5          c
                     Contin    NaN    NaN   0.0000          NaN        NaN
                     Comp4   8.030  1.009   7.0831       6699.5          d
                     Comp1  -1.816  2.153  50.2750       6699.5          a
Center: 5044.3 26-26 Comp1     NaN  2.457      NaN          NaN        NaN

当然,这让使用名称来重新排序多重索引的层级变得相当困难。有没有办法避免这种情况,除了在每次运行 set_values() 后强行设置名称?

编辑:更简单、可重复的示例。

这是一个 iPython 会话,重现了 index.names 问题,示例相对简单。它还显示出这可能是一个超出 index.names 的bug,因为它似乎将 index.lexsort_depth 从 3 改变为 0。提示中缺失的数字只是数据框的不必要视图。我认为必须选择已经存在的二级和/或三级索引,就像我下面所做的那样,以便重现这个问题。

In [4]: idx = pd.MultiIndex.from_arrays(
            [['Hans']*4 + ['Grethe']*4, ['1', '1', '2', '2']*2, ['a', 'b']*4], 
            names=['Name', 'Number', 'Letter'])

In [5]: df = pd.DataFrame(
            random.random((8, 3)), 
            columns=['one', 'two','three'], 
            index=idx)


In [6]: df
Out[6]: 
                           one       two     three
Name   Number Letter                              
Hans   1      a       0.803566  0.434574  0.805976
              b       0.655322  0.208469  0.989559
       2      a       0.893952  0.380358  0.173764
              b       0.822446  0.673894  0.676573
Grethe 1      a       0.202641  0.387263  0.405296
              b       0.646733  0.086953  0.882114
       2      a       0.358458  0.147107  0.769586
              b       0.183782  0.477863  0.601098

# To rule out another possible source of problems:
In [9]: df.unstack().drop(('Grethe', '1')).stack()
Out[9]: 
                           one       two     three
Name   Number Letter                              
Grethe 2      a       0.358458  0.147107  0.769586
              b       0.183782  0.477863  0.601098
Hans   1      a       0.803566  0.434574  0.805976
              b       0.655322  0.208469  0.989559
       2      a       0.893952  0.380358  0.173764
              b       0.822446  0.673894  0.676573

In [10]: df.set_value(('Frans', '2', 'b'), 'one', 23.)
Out[10]: 
                  one       two     three
Hans   1 a   0.803566  0.434574  0.805976
         b   0.655322  0.208469  0.989559
       2 a   0.893952  0.380358  0.173764
         b   0.822446  0.673894  0.676573
Grethe 1 a   0.202641  0.387263  0.405296
         b   0.646733  0.086953  0.882114
       2 a   0.358458  0.147107  0.769586
         b   0.183782  0.477863  0.601098
Frans  2 b  23.000000       NaN       NaN

In [11]: df = df.sortlevel(level='Name')

In [13]: df.index.lexsort_depth
Out[13]: 3

In [14]: df.set_value(('Frans', '2', 'b'), 'one', 23.).index.lexsort_depth
Out[14]: 0

2 个回答

1

你的索引需要排序!可以查看这里的文档了解更多信息:http://pandas.pydata.org/pandas-docs/dev/indexing.html#the-need-for-sortedness,这些食谱可能对你有帮助:http://pandas.pydata.org/pandas-docs/dev/cookbook.html。这是版本0.10.1。

这里是一个已经排序好的数据框。

In [26]: index = pd.MultiIndex.from_arrays([['a', 'a', 'b', 'b'], [1, 2, 1, 2]],
              names=['first', 'second'])

In [27]: df = pd.DataFrame(np.random.rand(len(index)), index=index,columns=['A'])

In [7]: df.index.lexsort_depth
Out[7]: 2

In [28]: df.set_value(('a',1),'A',1)
Out[28]: 
                     A
first second          
a     1       1.000000
      2       0.136456
b     1       0.712612
      2       0.818473

如果我按第二层排序(这样就变成无序的了)。

In [29]: df2 = df.sortlevel(level='second')

# this is not sorted! (well it is, just not lexsorted)
In [10]: df2.index.lexsort_depth
Out[10]: 0

In [30]: df2.set_value(('b','1'),'A',2)
Out[30]: 
            A
a 1  1.000000
b 1  0.712612
a 2  0.136456
b 2  0.818473
  1  2.000000
0

根据Andy Hayden的说法,这是pandas中的一个names错误。希望很快能有修复。

在此之前,我认为最好的解决办法是这样做:

tmp = df.ix['ExistingTransition'].copy()
tmp['Transition'] = 'NewTransition'
tmp = tmp.set_index('Transition', append=True)
tmp.index = tmp.index.reorder_levels([2, 0, 1])
# ...Do whatever else needs to be done to this before applying as template...
df = df.append(tmp)

...或者确保在每次运行set_values()之后重新创建names属性,然后按照问题中链接的示例进行操作。

撰写回答