【问题标题】:How can I style one row with multiple conditions in Pandas?如何在 Pandas 中为具有多个条件的一行设置样式?
【发布时间】:2020-08-15 19:10:41
【问题描述】:

我有一个数据框,我想为其设置一些行的样式,这些行的索引出现在字典中。 在用 apply、applymap、subset with row/column/IndexSlice 尝试了一堆东西之后,我仍然没有找到解决方案

  1. 根据多个条件使用不同的颜色为特定行设置不同的单元格样式。 (由于需要以相同的方式将行和列写入 excel,因此转置不起作用(?!))
  2. 将特定行/“猫”的样式条件和颜色分组到 1 个函数中。
  3. 根据另一行中单元格的值有条件地设置行中所有单元格的样式,但同一列(参考所需结果)。

注意:有些行会被“样式化”,有些则不会。

第一个问题:cat1 出现在字典中,所以我需要使用条件对其进行样式设置。我需要通过条件传递行中的每个单元格以应用某种颜色。 经过大量尝试后,下面的代码“最接近”潜在的解决方案,但在保存到 Excel 时出现错误。 (当我只使用“绿色”的代码时,它确实可以正常工作)。

"DataFrame 的真值不明确。使用 a.empty、a.bool()、a.item()、a.any() 或 a.all()。"

d_functions_colors={'cat1': catone_color,'cat5': catfive_color, 'cat40':catforty_color}

        2020-12   2019-12   2018-12   2017-12    2016-12      ...
idx  
cat1        100       NaN        50        35          5      ...
cat2          5       NaN         7         3          2      ...
cat3       6.25       NaN      6.25      4.93       5.21      ...
avg_cat3      5         4         3         2          1      ...
max_cat3     10        20        10        30          1      ...  
cat4                                                          ...
cat5          5        20         8         9.5       25      ...
avg_cat5     10        15         8         9.5       20      ...
...

(the results for avg, max are calculated and added for some 'cats' beforehand based on multiple (other) data sources)
    def highlight_row_green(x):
        c0 = ''
        c1 = 'background-color: green'
        c2 = 'background-color: yellow'
        c3 = 'background-color: red'
         
        df1 = pd.DataFrame('', index=x.index, columns=x.columns)
    
        m1 = x.index.astype(str).str.contains('^cat1')
    
        mask = (x[m1] >= 50).reindex(x.index, fill_value=False)
    
        df1 = df1.mask(mask, c1)
        return df1
    
    def highlight_row_yellow(x):
        c0 = ''
        c1 = 'background-color: green'
        c2 = 'background-color: yellow'
        c3 = 'background-color: red'
         
        df1 = pd.DataFrame('', index=x.index, columns=x.columns)
    
        m1 = x.index.astype(str).str.contains('^cat1$')
    
        mask = (20 <= x[m1] < 50)
    
        df1 = df1.mask(mask, c2)
        return df1
    
    
    def highlight_row_red(x):
        c0 = ''
        c1 = 'background-color: green'
        c2 = 'background-color: yellow'
        c3 = 'background-color: red'
         
        df1 = pd.DataFrame('', index=x.index, columns=x.columns)
    
        m1 = x.index.astype(str).str.contains('^cat1$')
    
        mask = ( x[m1] < 20)
    
        df1 = df1.mask(mask, c3)
        return df1

    def pandastoExcel(path,filename,sheetname,my_dataframe):
    outputfilepath=(path+'\\'+filename)
    if len(sheetname)>=30:
        sheetname='Default'
    else:
        pass
    writer = pd.ExcelWriter(outputfilepath , engine='xlsxwriter')
    try:
        my_dataframe.to_excel(writer, sheet_name=sheetname,index=True)
    except Exception as e:
        print(str(e))
    writer.save()


df_output=df.style.\
    apply(highlight_row_green, axis=None).\
    apply(highlight_row_yellow, axis=None).\
    apply(highlight_row_red, axis=None)



第二个问题:我尝试将它们分组,但得到相同的错误 "DataFrame 的真值不明确。使用 a.empty、a.bool()、a.item()、a.any() 或 a.all()。"

def catone_color(x):
     c1 = 'background-color: green'
     c2 = 'background-color: yellow'
     c3 = 'background-color: red'
     c0 = '' 

     df1 = pd.DataFrame('', index=x.index, columns=x.columns)
     m1 = x.index.astype(str).str.contains('^cat1$')

     mask = (x[m1] >= 50).reindex(x.index, fill_value=False)
     masky = (20 <= x[m1] < 50)
     maskr = (x[m1] < 20)

     df1 = df1.mask(mask, c1)
     df1 = df1.mask(masky, c2)
     df1 = df1.mask(maskr, c3)
     return df1

df_output=df.style.apply(catone_color, axis=None)

第三期:(例如 cat5),样式基于 cat5_avg 中的值。所以 2020 cat5_value 的颜色是基于 2020 cat5_avg 的,2019 cat5_value 的颜色是基于 2019 cat5_value 等等。

尝试通过将数据框中的 2 行(例如 row_values、row_avg)和其他一些东西结合起来,但远未取得任何成果。

            2020-12   2019-12   2018-12   2017-12    2016-12      ...
    idx  
    cat1        100       NaN        50        35          5      ...
    cat2          5       NaN         7         3          2      ...
    cat3       6.25       NaN      6.25      4.93       5.21      ...
    avg_cat3      5         4         3         2          1      ...
    max_cat3     10        20        10        30          1      ...  
    cat4                                                          ...
    cat5          5        20         8         9.5       25      ...
    avg_cat5     10        15         8         9.5       20      ...
    ...

#Idea of what I'm trying to accomplish:
for cat5_value in row_cat_5:
    If cat5_value > avg_cat5_same column:
          color = 'green'
    elif cat5_value == avg_cat5_same_column:
          color = 'yellow'
    elif cat5_value < avg_cat5_same_column:
          color = 'red'
    else:
          color=''
    return 'background-color: %s' % color

#(Background color row cat_5 would be red,green,yellow,yellow,green).  

#What I've got left from trying:
    g= 'green'
    y = 'yellow'
    r = 'red'

    m1 = val.iloc[0, :] < val.iloc[1, :]
    m2 = val.iloc[0, :] == val.iloc[1, :]
    m3 = val.iloc[0, :] > val.iloc[1, :]

    df1 = pd.DataFrame('background-color: ', index=val.index, columns=val.columns)

    df1.iloc[:,0] = np.where(m1, 'background-color: {}'.format(g), df1.iloc[0, :])
    df1.iloc[:,0] = np.where(m2, 'background-color: {}'.format(y), df1.iloc[0, :])
    df1.iloc[:,0] = np.where(m3, 'background-color: {}'.format(r), df1.iloc[0, :])
    return df1

Desired result

提前谢谢你,

PS:早期感谢所有贡献者,他们的答案作为上述结果代码的基础。

【问题讨论】:

    标签: python-3.x pandas dataframe pandas-styles


    【解决方案1】:

    用openpyxl解决

     from openpyxl import Workbook,load_workbook
     from openpyxl.styles import Color, PatternFill, Font, Border
     from openpyxl.styles import colors
     from openpyxl.cell import Cell
    
     d_functions_colors={'cat1': catone_color,
                          'cat5': catfive_color,'cat40':catforty_color}
    
     wb = load_workbook(outputfilepath)
     ws = wb.active
    
     for row in ws.iter_rows(min_row=ws.min_row, max_row=ws.max_row):
        if row[0] in d_functions_colors:
           row=d_functions_colors[row[0].value](row)
    
     def catone_color(row):
        for cell in row:            
            try:
              fl_cell=float(cell.value)
              if fl_cell>=50:
               #Green
               cell.fill = 
    PatternFill(start_color='0000FF00',end_color='0000FF00',fill_type='solid')
              elif 50>fl_cell>=20:
               #yellow
               cell.fill = 
    PatternFill(start_color='00FFFF00',end_color='00FFFF00',fill_type='solid')             
              elif 20 > fl_cell:
               #red
               cell.fill = 
    PatternFill(start_color='FFFF0000',end_color='FFFF0000',fill_type='solid')
              else:
               pass
            except Exception as e:
               print(str(e))
        return row
    

    【讨论】:

      猜你喜欢
      • 2021-09-05
      • 2020-06-22
      • 2021-01-01
      • 2014-06-09
      • 1970-01-01
      • 1970-01-01
      • 2018-06-12
      • 2021-05-16
      • 2022-01-23
      相关资源
      最近更新 更多