Pandas: измените имя определенного столбца в фрейме данных с многоуровневыми столбцами

Я хочу найти способ изменить имя определенного столбца в многоуровневом фрейме данных.

С этими данными:

data = {
    ('A', '1', 'I'): [1, 2, 3, 4, 5], 
    ('B', '2', 'II'): [1, 2, 3, 4, 5], 
    ('C', '3', 'I'): [1, 2, 3, 4, 5], 
    ('D', '4', 'II'): [1, 2, 3, 4, 5], 
    ('E', '5', 'III'): [1, 2, 3, 4, 5], 
}

dataDF = pd.DataFrame(data)

Этот код не работает:

dataDF.rename(columns = {('A', '1', 'I'):('Z', '100', 'Z')}, inplace=True)

Результат:

    A   B   C   D   E
    1   2   3   4   5
    I   II  I   II  III
0   1   1   1   1   1
1   2   2   2   2   2
2   3   3   3   3   3
3   4   4   4   4   4
4   5   5   5   5   5

А также нет:

dataDF.columns.values[0] = ('Z', '100', 'Z')

Результат:

    A   B   C   D   E
    1   2   3   4   5
    I   II  I   II  III
0   1   1   1   1   1
1   2   2   2   2   2
2   3   3   3   3   3
3   4   4   4   4   4
4   5   5   5   5   5

Но с комбинацией вышеуказанных кодов работает!!!

dataDF.columns.values[0] = ('Z', '100', 'Z')
dataDF.rename(columns = {('A', '1', 'I'):('Z', '100', 'Z')}, inplace=True)
dataDF

Результат:

    Z   B   C   D   E
    100 2   3   4   5
    Z   II  I   II  III
0   1   1   1   1   1
1   2   2   2   2   2
2   3   3   3   3   3
3   4   4   4   4   4
4   5   5   5   5   5

Это ошибка панд?


person sh.jeon    schedule 07.11.2016    source источник


Ответы (3)


это моя теория

pandas не хочет, чтобы pd.Indexs изменялось. Мы можем увидеть это, если попытаемся сами изменить первый элемент индекса.

dataDF.columns[0] = ('Z', '100', 'Z')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-32-2c0b76762235> in <module>()
----> 1 dataDF.columns[0] = ('Z', '100', 'Z')

//anaconda/envs/3.5/lib/python3.5/site-packages/pandas/indexes/base.py in __setitem__(self, key, value)
   1372 
   1373     def __setitem__(self, key, value):
-> 1374         raise TypeError("Index does not support mutable operations")
   1375 
   1376     def __getitem__(self, key):

TypeError: Index does not support mutable operations

Но панды не могут контролировать то, что вы делаете с атрибутом values.

dataDF.columns.values[0] = ('Z', '100', 'Z')

мы видим, что dataDF.columns выглядит так же, но dataDF.columns.values явно отражает изменение. К сожалению, df.columns.values - это не то, что отображается на дисплее фрейма данных.


С другой стороны, это действительно похоже на то, что это должно работать. Тот факт, что это не кажется мне неправильным.

dataDF.rename(columns={('A', '1', 'I'): ('Z', '100', 'Z')}, inplace=True)

Я считаю, что причина, по которой это работает только после изменения значений, заключается в том, что rename вызывает реконструкцию столбцов, просматривая значения. Поскольку мы меняем значения, теперь это работает. Это исключительно глупо, и я не рекомендую создавать процесс, основанный на этом.


моя рекомендация

  • определить местоположение имени столбца, которое вы хотите изменить
  • присвоить имя столбца массиву значений
  • создавать новые столбцы с нуля, явно

from_col = ('A', '1', 'I')
to_col = ('Z', '100', 'Z')
colloc = dataDF.columns.get_loc(from_col)
cvals = dataDF.columns.values
cvals[colloc] = to_col

dataDF.columns = pd.MultiIndex.from_tuples(cvals.tolist())

dataDF

[![enter code here][1]][1]
person piRSquared    schedule 07.11.2016
comment
dataDF.columns.values[0] = ('Z', '100', 'Z') у меня работает. Печать dataDF.columns после этого корректно отражает изменение. Что мне здесь не хватает? Панды исправили это в более новой версии? - person Jon; 09.06.2021

Вы можете просто изменить его, как DF.columns.levels=[[u'Z', u'B', u'C', u'D', u'E'],[u'5', u'2', u'3', u'4', u'5'],[u'IIIIII', u'II', u'III']]

person Dark Matter    schedule 07.11.2016
comment
все же я не уверен, что это ошибка, как вы говорите. - person Dark Matter; 07.11.2016

Я столкнулся с этим вопросом, когда сам пытался найти решение для переименования имен столбцов в фрейме данных с несколькими уровнями. Я попробовал решение, предоставленное @Dark Matter, поскольку оно оказалось очень простым:

dataDF.columns.levels = [[u'Z', u'B', u'C', u'D', u'E'], [u'100', u'2', u'3', u'4', u'5'], [u'Z', u'II', u'III']]

Но высветилось сообщение об ошибке:

C:\anaconda3\lib\site-packages\ipykernel_launcher.py:1: FutureWarning: setting `levels` directly is deprecated. Use set_levels instead
  """Entry point for launching an IPython kernel.

Вроде работало, но уже не работает. Поэтому я использовал:

dataDF.columns.set_levels([['Z', 'B', 'C', 'D', 'E'],
                           ['100', '2', '3', '4', '5'],
                           ['Z', 'II', 'III']],
                          [0, 1, 2], inplace=True)

Результат: dataDF

Z   B   C   D   E
100 2   3   4   5
Z   II  Z   II  III
0   1   1   1   1   1
1   2   2   2   2   2
2   3   3   3   3   3
3   4   4   4   4   4
4   5   5   5   5   5
person novastar    schedule 23.03.2018