【问题标题】:How to set a background color to cells in a multiindex table?如何为多索引表中的单元格设置背景颜色?
【发布时间】:2022-10-24 03:10:03
【问题描述】:

我有这个多索引列df:

None         INT        INT        INT        PP         PP         PP                       
DATE      2021-12-01 2021-12-02 2021-12-03 2021-12-04 2021-12-05 2021-12-06
0            1.0        0.0        2.0        2.0        4.0        2.0
1            NaN        NaN        NaN        NaN        NaN        NaN
2            0.0        0.0        2.0        0.0        3.0        4.0
3            0.0        2.0        2.0        2.0        3.0        2.0
4            0.0        0.0        0.0        0.0        0.0        0.0
5            0.0        0.0        0.0        0.0        0.0        0.0
6            0.0        0.0        0.0        0.0        0.0        0.0
7            2.0        1.0        0.0        1.0        2.0        0.0
8            NaN        NaN        NaN        NaN        NaN        NaN
9            0.0        0.0        0.0        0.0        0.0        0.0

我想根据它们的值(白色到值 = 0,浅灰色到值 = 1 等)只为“PP”列中的值(并导出到 excel)赋予背景颜色样式。所以我想到了这一点:

###############################################################################
n=len(df.columns)
def colors_excel(s):
    
    if s.PP == 0:
        return ['background-color: white']*n
    elif s.PP == 1:
        return ['background-color: lightgray']*n
    elif s.PP == 2:
        return ['background-color: gray']*n
    elif s.PP == 3:
        return ['background-color: yellow']*n
    elif s.PP == 4:
        return ['background-color: orange']*n
    elif s.PP == 5:
        return ['background-color: red']*n
    else:
        return ['background-color: black']*n 
###############################################################################
exceldata=df.style.apply(colors_excel, axis=0)

exceldata.to_excel('ROUTE/name_of_thefile.xlsx',
                     engine='openpyxl', index=True)

但这在多索引列中不起作用。而且我不想删除多索引列的日期。我该如何解决这个问题?

这是我期望得到的另一个例子:

我将不胜感激。

提前致谢。

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    考虑到subset,您可以使用Styler.apply(func, axis=None, subset) 获取具有有效索引和列标签的DataFrame:

    def highlight_cols(df):
        color = pd.DataFrame().reindex_like(df.droplevel(0, axis=1))
        colors = ['yellow', 'lightgray', 'gray']
    
        color = color.fillna(f'background-color: {colors[-1]}')
        for idx, col in enumerate(df.columns.get_level_values(1)):
            if idx < len(colors) - 1:
                color = color.mask(df.eq(idx).values, f'background-color: {colors[idx]}')
    
        return color.values
    
    
    idx = pd.IndexSlice
    style = df.style.apply(highlight_cols, axis=None, subset=idx[:, idx['PP', :]])
    style.to_excel('74075209.xlsx')
    

    【讨论】:

      【解决方案2】:

      固定单元格格式

      让我们准备数据:

      import pandas as pd
      from io import StringIO
      
      data = '''None         INT        INT        INT        PP         PP         PP                       
      DATE      2021-12-01 2021-12-02 2021-12-03 2021-12-04 2021-12-05 2021-12-06
      0            1.0        0.0        2.0        2.0        4.0        2.0
      1            NaN        NaN        NaN        NaN        NaN        NaN
      2            0.0        0.0        2.0        0.0        3.0        4.0
      3            0.0        2.0        2.0        2.0        3.0        2.0
      4            0.0        0.0        0.0        0.0        0.0        0.0
      5            0.0        0.0        0.0        0.0        0.0        0.0
      6            0.0        0.0        0.0        0.0        0.0        0.0
      7            2.0        1.0        0.0        1.0        2.0        0.0
      8            NaN        NaN        NaN        NaN        NaN        NaN
      9            0.0        0.0        0.0        0.0        0.0        0.0
      '''
      
      df = pd.read_csv(StringIO(data), sep='s+', header=[0,1], index_col=0)
      

      为了用指定的颜色替换值,我会使用字典和applymap 方法:

      colors = {
          0: 'white',
          1: 'lightgray',
          2: 'gray',
          3: 'yellow',
          4: 'orange',
          5: 'red'
      }
      
      default_color = 'black'
      get_color = lambda x: colors.get(x, default_color)
      color_map = lambda df: 'background-color: ' + df.applymap(get_color).values + ';'
      

      在最后一行中,我使用.values 将表单DataFrame 切换为numpy.ndarray,以避免与索引或列标签不匹配。

      接下来,在样式器中,我使用applycolor_map 作为函数,axis=None 将框架作为参数传递给color_mapsubset='PP' 将整个框架限制为具有聚丙烯在标题中:

      exceldata = df.style.apply(color_map, subset='PP', axis=None)
      exceldata.to_excel('file.xlsx', engine='openpyxl', index=True)
      

      条件格式

      就数据的条件格式而言,在 Excel 文件中使用条件格式似乎很自然。如何做到这一点取决于我们使用的引擎(openpyxl、xlsxwriter 等)。

      让我们坚持openpyxl

      file = 'test.xlsx'
      writer = pd.ExcelWriter(file, engine='openpyxl')
      df.to_excel(writer, sheet_name = 'Data')
      

      现在,在关闭 writer 之前,我们必须设置条件格式。为此,我们需要找出放置df.PP 的范围的左上角和右下角。请注意,默认情况下,将在标题和数据之间放置一条用于索引命名的行。所以数据开始的行是df.columns.nlevels + 1

      row_start = df.columns.nlevels + 1
      row_end = row_start + len(df) - 1
      

      至于列,我们可以使用df.columns.get_level_values(0) == 'PP' 来查找列聚丙烯在标题中,或者像df.columns.get_loc('PP') 这样的东西,在这种情况下将返回一个从第 3 列到第 6 列的切片。让我们用get_loc 来做吧:

      from string import ascii_uppercase as letters
      
      col_slice = df.columns.get_loc('PP')
      col_start = letters[col_slice.start]
      col_end = letters[col_slice.stop - 1]
      
      range_str = f'{col_start}{row_start}:{col_end}{row_end}'
      

      这里range_str 是工作表中df.PP 数据的地址,类似于'D3:F12'

      现在我们可以添加条件格式:

      from openpyxl.styles import PatternFill, Font
      from openpyxl.formatting.rule import CellIsRule
      
      BLACK = '010101'
      colors = {
          '""': BLACK,     # black for blank cells    
          '0': "FFFFFF",   # white
          '1': "CCCCCC",   # lightgray
          '2': "999999",   # gray
          '3': "FFFF00",   # yellow
          '4': "FFCC00",   # orange
          '5': "FF0000",   # red
      }
      
      colors = {k: PatternFill(bgColor=v, fill_type='solid') for k, v in colors.items()}
      
      for value, color in colors.items():
          sheet.conditional_formatting.add(
              range_str,
              CellIsRule('equal', formula=[value], stopIfTrue=True, fill=color),
          )
          
      sheet.conditional_formatting.add(
          range_str,
          CellIsRule('notBetween', formula=['0','5'], stopIfTrue=True, 
                     fill=PatternFill(bgColor=BLACK, fill_type='solid'), 
                     font=Font(color='FFFFFF')),
      )
      

      笔记:

      • 空白单元格在 Excel 中等于 0,因此我们必须先通过与空字符串比较来检查它们(在与 0 比较之前);
      • 黑色"0x000000"被openpyxl视为白色(不知道为什么),所以我们必须将其定义为几乎黑色的;
      • 最后,我们为区间 [0, 5] 之外的值添加了一条附加规则,以使其更具体,例如 not in the list [0,1,2,3,4,5],我们必须提出一些其他规则。

      蟒蛇:3.10.7
      熊猫:1.5.1
      开放pyxl:3.0.10

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-10-21
        • 1970-01-01
        • 2017-12-08
        • 1970-01-01
        • 2012-06-06
        • 2015-06-02
        • 2010-11-21
        • 2016-12-18
        相关资源
        最近更新 更多