pandas 使用函数更改和设置多索引 Dataframe 的一个级别

brccelvz  于 2023-03-06  发布在  其他
关注(0)|答案(3)|浏览(109)

假设多索引 Dataframe 如下

(虚拟)数据

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}}

(The当然真实的情况要大得多)
我需要根据函数更改级别0中名为userid的名称的值。
我用下面的方法来计算,结果很奇怪:

代码和错误的解决方案

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的顶部,PENSIONER1,A4是与它对应的其他级别(MIKE90没有得到更改)您还可以看到列表新名称的顺序具有正确的顺序。

问题

为什么?你怎样改变多重索引的一个级别而不改变数据?

ax6ht2ek

ax6ht2ek1#

我将使用MultiIndex.map和给定的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
h7appiyu

h7appiyu2#

MultiIndex的第一级使用renamedict.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
lvjbypge

lvjbypge3#

定时代码

df_anonym = df.copy(deep=True)

%%timeit
df_anonym.columns.map(lambda c: (dict_mask.get(c[0],"UNKONOWN"), *c[1:]))

df_anonym2 = df.copy(deep=True)

%%timeit
df_anonym2.columns = [(dict_mask.get(x,"UNKNOWN"), *y) for x, *y in df_anonym2.columns]

df_anonym3 = df.copy(deep=True)

%%timeit
df_anonym3.rename(columns=lambda x: dict_mask.get(x,"UNKNOWN"), level=0)

结果列出理解:赢家Map:第二次重命名:瓦阿阿伊减速器

相关问题