Pandas: df.set_value() 方法重置 MultiIndex 的列名
我正在写一个应用程序,使用 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 个回答
你的索引需要排序!可以查看这里的文档了解更多信息: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
根据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
属性,然后按照问题中链接的示例进行操作。