假设一个多索引数据框如下
(虚拟)数据
import pandas as pd
df={('AB30566', 'ACTIVE1', 'A1'): {('2021-01-01', 'PHOTO'): 2,
('2021-01-01', 'QUE'): 8,
('2021-01-01', 'TXR'): 4,
('2022-02-01', 'PHOTO'): 4,
('2022-02-01', 'QUE'): 0,
('2022-02-01', 'TXR'): 1,
('2022-03-01', 'PHOTO'): 9,
('2022-03-01', 'QUE'): 7,
('2022-03-01', 'TXR'): 7},
('CD55DF55', 'ACTIVE2', 'A2'): {('2021-01-01', 'PHOTO'): 1,
('2021-01-01', 'QUE'): 7,
('2021-01-01', 'TXR'): 0,
('2022-02-01', 'PHOTO'): 8,
('2022-02-01', 'QUE'): 8,
('2022-02-01', 'TXR'): 3,
('2022-03-01', 'PHOTO'): 6,
('2022-03-01', 'QUE'): 0,
('2022-03-01', 'TXR'): 7},
('ZT52556', 'UNACTIVE1', 'A3'): {('2021-01-01', 'PHOTO'): 8,
('2021-01-01', 'QUE'): 9,
('2021-01-01', 'TXR'): 3,
('2022-02-01', 'PHOTO'): 5,
('2022-02-01', 'QUE'): 3,
('2022-02-01', 'TXR'): 0,
('2022-03-01', 'PHOTO'): 7,
('2022-03-01', 'QUE'): 0,
('2022-03-01', 'TXR'): 9},
('MIKE90', 'PENSIONER1', 'A4'): {('2021-01-01', 'PHOTO'): 3,
('2021-01-01', 'QUE'): 9,
('2021-01-01', 'TXR'): 8,
('2022-02-01', 'PHOTO'): 3,
('2022-02-01', 'QUE'): 2,
('2022-02-01', 'TXR'): 1,
('2022-03-01', 'PHOTO'): 9,
('2022-03-01', 'QUE'): 0,
('2022-03-01', 'TXR'): 4},
('ZZ00001', 'ACTIVE3', 'A5'): {('2021-01-01', 'PHOTO'): 0,
('2021-01-01', 'QUE'): 2,
('2021-01-01', 'TXR'): 1,
('2022-02-01', 'PHOTO'): 2,
('2022-02-01', 'QUE'): 0,
('2022-02-01', 'TXR'): 8,
('2022-03-01', 'PHOTO'): 5,
('2022-03-01', 'QUE'): 6,
('2022-03-01', 'TXR'): 0}}
(当然真实案例要大得多)
我需要根据函数更改名为 userid 的 0 级名称的值。
我按照以下方式进行操作,结果发生了这种奇怪的情况:
代码和错误的解决方案
d=pd.DataFrame(f)
d.columns.names =["USERID", "STATUS","LEVEL"]
def simple_mask_user_id(userids):
exam_dict = {userid:("EX"+str(i).zfill(5) if re.match(r"[A-Z][A-Z][0-9][0-9][0-9][0-9][0-9]",userid) else userid) for i,userid in enumerate(userids) }
return exam_dict
current_userids = d.columns.get_level_values('USERID').tolist()
dict_mask = simple_mask_user_id(current_userids)
display(d)
new_names = d.columns.get_level_values("USERID").map(dict_mask).tolist()
print(new_names)
d.columns.set_levels(new_names, level=0, inplace=True)
display(d)
dataframe 的级别 USERID 应该根据字典进行更改:
{'AB30566': 'EX00000', 'CD55DF55': 'CD55DF55', 'ZT52556': 'EX00002', 'MIKE90': 'MIKE90', 'ZZ00001': 'EX00004'}
错误的结果
我显示 df 来比较前后的结果。 指数好坏参半。
MIKE90和EX00002互换。
也就是说MIKE90不在对应的PENSIONER1,A4之上,也就是对应的其他levels(MIKE90没有变) 您还可以看到列表新名称的顺序是正确的。
问题
为什么? 如何在不更改数据的情况下更改多索引的一级?
我会使用
MultiIndex.map
和给定的映射字典 (d
) 来替换 level=0 值
df.columns = df.columns.map(lambda c: (d[c[0]], *c[1:]))
结果
EX00000 CD55DF55 EX00002 MIKE90 EX00004
ACTIVE1 ACTIVE2 UNACTIVE1 PENSIONER1 ACTIVE3
A1 A2 A3 A4 A5
2021-01-01 PHOTO 2 1 8 3 0
QUE 8 7 9 9 2
TXR 4 0 3 8 1
2022-02-01 PHOTO 4 8 5 3 2
QUE 0 8 3 2 0
TXR 1 3 0 1 8
2022-03-01 PHOTO 9 6 7 9 5
QUE 7 0 0 0 6
TXR 7 7 9 4 0
通过
rename
的第一级使用MultiIndex
和dict.get
- 如果没有匹配返回原始值(第二个参数x
):
#same key values should be omitted
d = {'AB30566': 'EX00000', 'ZT52556': 'EX00002', 'ZZ00001': 'EX00004'}
df = df.rename(columns=lambda x: d.get(x,x), level=0)
print (df)
EX00000 CD55DF55 EX00002 MIKE90 EX00004
ACTIVE1 ACTIVE2 UNACTIVE1 PENSIONER1 ACTIVE3
A1 A2 A3 A4 A5
2021-01-01 PHOTO 2 1 8 3 0
QUE 8 7 9 9 2
TXR 4 0 3 8 1
2022-02-01 PHOTO 4 8 5 3 2
QUE 0 8 3 2 0
TXR 1 3 0 1 8
2022-03-01 PHOTO 9 6 7 9 5
QUE 7 0 0 0 6
TXR 7 7 9 4 0
如果字典的所有列都有键:
d = {'AB30566': 'EX00000', 'CD55DF55': 'CD55DF55',
'ZT52556': 'EX00002', 'MIKE90': 'MIKE90', 'ZZ00001': 'EX00004'}
df = df.rename(columns=lambda x: d[x], level=0)
print (df)
EX00000 CD55DF55 EX00002 MIKE90 EX00004
ACTIVE1 ACTIVE2 UNACTIVE1 PENSIONER1 ACTIVE3
A1 A2 A3 A4 A5
2021-01-01 PHOTO 2 1 8 3 0
QUE 8 7 9 9 2
TXR 4 0 3 8 1
2022-02-01 PHOTO 4 8 5 3 2
QUE 0 8 3 2 0
TXR 1 3 0 1 8
2022-03-01 PHOTO 9 6 7 9 5
QUE 7 0 0 0 6
TXR 7 7 9 4 0