当索引级别具有相同的值时,替换 pandas 数据帧多索引的第二级别上的奇异值

Replace singular value on second level of pandas dataframe multiindex when that index level has identical values

我有一个具有两个级别的多索引的数据框。给出第二级的以下示例:

d = {
"col1": [1, 2, 3, 4],
"col2": [1, 2, 3, 4],
"col3": [1, 2, 3, 4],
"col4": [1, 2, 3, 4],
"col5": [1, 2, 3, 4],
}
df = pd.DataFrame(data=d)
df.columns = pd.MultiIndex.from_product([df.columns, ["identical"]])

如何更改奇异值以使索引的第二级看起来像这样?

['example', 'identical', 'identical', 'identical', 'identical']

我试过这样做:

        updated_columns = list(df.columns.get_level_values(1))

        updated_columns[0] = 'example'

        df.columns.set_levels(
            updated_columns, level=1, inplace=True, verify_integrity=False
        )

在这种情况下,我的更改将被忽略。

我也试过这个话题的答案:pandas MultiIndex with duplicate values in one level

        df.columns = pd.MultiIndex.from_tuples(
            df.columns.set_levels(updated_columns, 1, verify_integrity=False).values
        )

也被忽略了。

我也考虑过使用 rename() 方法。不幸的是,它仅在提供重命名列的值时才有效。鉴于存在相同的值,这将不起作用。

对于非多索引有这个方法:

df.columns.values[0] = 'example'

但据我所知,它不适用于多索引。

我添加了 verify_integrity=False,因为该方法不允许我设置相同的值。

如有任何帮助,我们将不胜感激。

一种方法是获取构成 MultiIndex 的元组并直接修改它们:

    tuples = df.columns.tolist()
    tuples[0] = (tuples[0][0], 'example')

    df.columns = pd.MultiIndex.from_tuples(tuples)

输出:

>>> df
     col1      col2      col3      col4      col5
  example identical identical identical identical
0       1         1         1         1         1
1       2         2         2         2         2
2       3         3         3         3         3
3       4         4         4         4         4

MultiIndexes 有点奇怪。它们存储为级别列表(其中包含 unique 标签值)和代码(标签的索引)。例如,您当前的 MultiIndex 的级别和代码如下所示:

>>> df.columns.levels
FrozenList([['col1', 'col2', 'col3', 'col4', 'col5'], ['identical']])

>>> df.columns.codes
FrozenList([[0, 1, 2, 3, 4], [0, 0, 0, 0, 0]])

如您所见,那里只有一个 'indentical' 字符串。它的重复由代码定义。

所以如果你想通过操作级别和代码将第二级别的第一个标签设置为example,你可以这样做:

vals = df.columns.levels[1].tolist()
vals.append('example')

df.columns = df.columns.set_levels(vals, level=1)
df.columns = df.columns.set_codes([1, 0, 0, 0, 0], level=1) # 1 = 'example', 2 = 'identical'

这是一个函数,您可以使用它来设置 MultiIndex 特定级别的所有标签:

def set_level_of_df(df, level_index, labels):
    new_df = df.copy()
    
    unique, inverse = np.unique(labels, return_inverse=True)
    new_df.columns = new_df.columns.set_levels(unique, level=level_index)
    new_df.columns = new_df.columns.set_codes(inverse, level=level_index)
    return new_df

用法:

>>> df
     col1      col2      col3      col4      col5
  example identical identical identical identical
0       1         1         1         1         1
1       2         2         2         2         2
2       3         3         3         3         3
3       4         4         4         4         4

>>> df = set_level_of_df(df, level_index=0, labels=['aaa', 'abc', 'xyz', '0123', '-----'])
>>> df
      aaa       abc       xyz      0123     -----
  example identical identical identical identical
0       1         1         1         1         1
1       2         2         2         2         2
2       3         3         3         3         3
3       4         4         4         4         4

这是另一个函数(使用前一个函数),您可以使用它来设置 MultiIndex 特定级别的特定标签(这是您的问题):

def set_label_of_label_of_df(df, level_index, label_index, label):
    new_df = df.copy()
    
    labels = df.columns.get_level_values(level=level_index).tolist()
    labels[label_index] = label
    return set_level_of_df(df, level_index, labels)

用法:

>>> df
     col1      col2      col3      col4      col5
  example identical identical identical identical
0       1         1         1         1         1
1       2         2         2         2         2
2       3         3         3         3         3
3       4         4         4         4         4

>>> df = set_label_of_label_of_df(df, level_index=1, label_index=3, label='THIS_IS_A_TEST_VALUE')
>>> df
     col1      col2      col3                 col4      col5
  example identical identical THIS_IS_A_TEST_VALUE identical
0       1         1         1                    1         1
1       2         2         2                    2         2
2       3         3         3                    3         3
3       4         4         4                    4         4