Pandas: метод df.set_value() стирает/сбрасывает имена столбцов MultiIndex

108
12

Я пишу приложение, которое использует pandas (версия 0.10.1) для хранения базовой модели данных в виде (3-уровневого) MultiIndex'ed DataFrame. Модель представляет собой линейный спектр, а верхний уровень индекса - это атомный переход.

Простой фреймворк данных может выглядеть так:

                               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-бета, используя H-alpha в качестве шаблона. В идеале я бы это сделал как-то вроде df.ix['H-beta-wavelength'] = df.ix['H-alpha-6697.6'], но это невозможно сделать. Поэтому вместо этого я попытался выполнить следующий пример: Подготовьте уровень к pandas MultiIndex

Однако в приведенном выше примере требуются .names уровней multiindex, чтобы их переупорядочить. Атрибут 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()?

EDIT: более простой, воспроизводимый пример.

Вот сеанс iPython, воссоздающий проблему index.names с несколько более простым примером. Он также показывает, что это, возможно, ошибка, выходящая за пределы index.names, поскольку она, как кажется, меняет 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

спросил(а) 2021-01-25T19:19:33+03:00 4 месяца, 2 недели назад
1
Решение
63

Ваш индекс нужно сортировать! См. Документы здесь: 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

Heres сортированная рамка

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


И если я сортируюсь на 2-м уровне (поэтому его несортированный)

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

ответил(а) 2021-01-25T19:19:33+03:00 4 месяца, 2 недели назад
45

Итак, по словам Энди Хейдена, это ошибка 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)

... Это или убедитесь, что атрибут names воссоздан после каждого запуска set_values(), а затем просто set_values() к примеру, связанному с вопросом.

ответил(а) 2021-01-25T19:19:33+03:00 4 месяца, 2 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема