比较同一行中的多个列并突出显示 pandas 中的差异

Compare multiple columns within same row and highlight differences in pandas

我的数据框类似于:

NAME DB1 DB2 DB3 DB4
WORKFLOW_1 workflow1-1.jar workflow1-2.jar workflow1-1.jar workflow1-3.jar
WORKFLOW_2 workflow2-1.jar workflow2-1.jar workflow2-1.jar workflow2-1.jar
WORKFLOW_3 workflow3-2.jar workflow3-1.jar workflow3-1.jar workflow3-1.jar
WORKFLOW_4 workflow4-1.jar

其中 NAME 是整个 n 数据库中此 table 的键.我正在从特定列收集数据并将其并排合并以供进一步分析。

我的问题是我需要突出显示列之间包含不同文件名的行 DBn.

我试过以下解决方案:

        def highlight(row):

        for key1, column1 in row.items():
            if key1 != 'NAME':
                for key2, column2 in row.items():
                    if key2 != 'NAME':
                        if column1 != column2:
                            return ['background-color: red']
        return ['background-color: green']

        pd = pd.style.apply(highlight)

当至少有一个文件名与其他文件名不同时,我尝试对整行设置样式,但没有成功,当我导出到 excel 时,只有第一行是红色的,这不是均匀的应该发生的情况之一。

最简单(和幼稚)的方法是使用 Series.eq 针对第一个值测试每一行。设置合适的 subset 在这里非常重要,因为我们只想与其他相似值进行比较。

def highlight_row(s: pd.Series) -> List[str]:
    bg_color = 'red'
    if s.eq(s[0]).all():
        bg_color = 'green'
    return [f'background-color:{bg_color}'] * len(s)


df.style.apply(
    func=highlight_row,
    subset=['DB1', 'DB2', 'DB3', 'DB4'],
    axis=1
)


在仅与过滤后的数组进行相等性比较之前,我们可以通过使用布尔索引从每一行中排除空字符串和空值(以及任何其他无效值)来减少一些天真:

def highlight_row(s: pd.Series) -> List[str]:
    filtered_s = s[s.notnull() & ~s.eq('')]
    # Check for completely empty row (prevents index error from filtered_s[0])
    if filtered_s.empty:
        # No valid values in row
        css_str = ''
    elif filtered_s.eq(filtered_s[0]).all():
        # All values are the same
        css_str = 'background-color: green'
    else:
        # Row Values Differ
        css_str = 'background-color: red'
    return [css_str] * len(s)

我们还可以利用 IndexSlice 更动态地 select subset 的列,而不是手动传递列名列表:

df.style.apply(
    func=highlight_row,
    subset=pd.IndexSlice[:, 'DB1':],
    axis=1
)


最后,如果想要突出显示整行,可以将 idx/cols 传递给样式函数而不是子集化:

def highlight_row(s: pd.Series, idx: pd.IndexSlice) -> List[str]:
    css_str = 'background-color: red'
    # Filter Columns
    filtered_s = s[idx]
    # Filter Values
    filtered_s = filtered_s[filtered_s.notnull() & ~filtered_s.eq('')]
    # Check for completely empty row
    if filtered_s.empty:
        css_str = ''  # Empty row Styles
    elif filtered_s.eq(filtered_s[0]).all():
        css_str = 'background-color: green'
    return [css_str] * len(s)


df.style.apply(
    func=highlight_row,
    idx=pd.IndexSlice['DB1':],  # 1D IndexSlice!
    axis=1
)


设置和导入:

from typing import List

import pandas as pd  # version 1.4.2

df = pd.DataFrame({
    'NAME': ['WORKFLOW_1', 'WORKFLOW_2', 'WORKFLOW_3', 'WORKFLOW_4'],
    'DB1': ['workflow1-1.jar', 'workflow2-1.jar', 'workflow3-2.jar', ''],
    'DB2': ['workflow1-2.jar', 'workflow2-1.jar', 'workflow3-1.jar',
            'workflow4-1.jar'],
    'DB3': ['workflow1-1.jar', 'workflow2-1.jar', 'workflow3-1.jar', ''],
    'DB4': ['workflow1-3.jar', 'workflow2-1.jar', 'workflow3-1.jar', '']
})