在 Pandas 中如何添加多级索引?

3

我有一个多级索引的数据框,我提供了最小可复现样例。

import pandas as pd
import numpy as np
import string

index = pd.MultiIndex.from_product(
    ([2020], [1, 2, 3, 4]), names=['year', 'q']
)

columns = pd.MultiIndex.from_product(
    (['Items1', 'Items2', 'Items3'], ['new', 'old']),
    names=['Items', 'type']
)

data = np.random.seed(123)
data = list(np.random.choice(list(string.ascii_lowercase), (4,6)))

Ldata = pd.DataFrame(data, index=index, columns=columns)
Ldata

我想在数据框中标记特定的值,我是这样做的。但是我不知道在哪里放置多级索引level=1。我尝试了一下,但出现了错误,请问我应该在哪里添加level?

def highlight_cells(x):
    c = 'background-color: white'
    c1 = 'background-color: red'
    c2 = 'background-color: blue'
    c3 = 'background-color: green'
                
    k1 = Ldata['new'].str.contains("a", na=False) 
    k2 = Ldata['new'].str.contains("b", na=False)
    k3 = Ldata['new'].str.contains("c", na=False) 
    
    colordata = pd.DataFrame(c, index=x.index, columns=x.columns)
    colordata.loc[k1, 'new'] = c1
    colordata.loc[k2, 'new'] = c2
    colordata.loc[k3, 'new'] = c3

    return colordata

end = Ldata.style.apply(highlight_cells,axis=None)

end

提前致谢!

3个回答

2
也许最简单的方法是在应用函数之前将DataFrame堆叠起来,像这样:
Ldata = Ldata.stack('Items')

然后你可以将代码的其余部分保持不变。

1
最简单的修改方式是在Styler.apply中使用subset参数,并与pd.IndexSlice结合使用。然后,将应用的DataFrame样式减少到每个匹配子集的MultiIndex中的每列的Series级别应用样式:
def highlight_cells(x):
    c = 'background-color: white'
    c1 = 'background-color: red'
    c2 = 'background-color: blue'
    c3 = 'background-color: green'

    k1 = x.str.contains("a", na=False)
    k2 = x.str.contains("b", na=False)
    k3 = x.str.contains("c", na=False)
    
    # Build Series for _column_ level Styles
    color_data = pd.Series(c, index=x.index)
    color_data[k1] = c1
    color_data[k2] = c2
    color_data[k3] = c3
    return color_data


idx = pd.IndexSlice
end = Ldata.style.apply(highlight_cells, subset=idx[:, idx[:, 'new']])

end

自然地,也可以不使用单独的变量来完成这个操作:
def highlight_cells(x):
    color_data = pd.Series('background-color: white', index=x.index)
    color_data[x.str.contains("a", na=False)] = 'background-color: red'
    color_data[x.str.contains("b", na=False)] = 'background-color: blue'
    color_data[x.str.contains("c", na=False)] = 'background-color: green'
    return color_data


idx = pd.IndexSlice
end = Ldata.style.apply(highlight_cells, subset=idx[:, idx[:, 'new']])

end

或者不使用带有 np.select 的索引结构:

def highlight_cells(x):
    return np.select(
        [x.str.contains("a", na=False),
         x.str.contains("b", na=False),
         x.str.contains("c", na=False)],
        ['background-color: red',
         'background-color: blue',
         'background-color: green'],
        default='background-color: white'
    )


idx = pd.IndexSlice
end = Ldata.style.apply(highlight_cells, subset=idx[:, idx[:, 'new']])

end

所有选项均产生以下结果:

styled table


0

看起来你正在尝试修改每个"new"列,跨越"Items"列的级别。

您可以使用IndexSlice访问MultiIndex的内部级别:

def highlight_cells(x):
    c = 'background-color: white'
    c1 = 'background-color: red'
    c2 = 'background-color: blue'
    c3 = 'background-color: green'

    def ldata_contains(value):
        return (
            Ldata
            .loc[:, pd.IndexSlice[:, 'new']]
            .apply(lambda y: y.str.contains(value, na=False))
        )

    k1 = ldata_contains("a")
    k2 = ldata_contains("b")
    k3 = ldata_contains("c")

    colordata = pd.DataFrame(c, index=x.index, columns=x.columns)
    colordata.loc[k1, pd.IndexSlice[:, 'new']] = c1
    colordata.loc[k2, pd.IndexSlice[:, 'new']] = c2
    colordata.loc[k3, pd.IndexSlice[:, 'new']] = c3

    return colordata

这样做不起作用,因为 Ldata.loc[:, pd.IndexSlice[:, 'new']] 将返回一个 DataFrame。而且没有 DataFrame.str.contains 方法。 - Henry Ecker
正确,你需要使用 .apply 来执行它。 - shadowtalker
我修改了它,使用.apply和一个小的辅助函数。当然,你不需要这个辅助函数,你可以直接复制粘贴。 - shadowtalker
现在 k1k2k3 是数据框,但你正在将它们传递给 loc 的索引切片。 - Henry Ecker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接