4

I have 2 data frames with identical indices/columns:

df = pd.DataFrame({'A':[5.5, 3, 0, 3, 1],
                     'B':[2, 1, 0.2, 4, 5],
                     'C':[3, 1, 3.5, 6, 0]})

df_bool = pd.DataFrame({'A':[0, 1, 0, 0, 1],
                          'B':[0, 0, 1, 0, 0],
                          'C':[1, 1, 1, 0, 0]})

I want to apply a style function to df element-wise using df_bool as a mask.

This is the expected result:

expected results

Current failed function

def color_boolean(val):
  color =''
  if df_bool == 1:
    color = 'red'
  elif df_bool == 0:
    color = 'black'
  return f'color: {color}'

df.head().style.apply(color_boolean, axis=None)
ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
Henry Ecker
  • 34,399
  • 18
  • 41
  • 57
Scott O
  • 65
  • 3

2 Answers2

2

You can use a function that ignores the input and simply uses the other DataFrame:

def color_boolean(val):
    return f'color: {"red" if val else "black"}'

df.style.apply(lambda _: df_bool.applymap(color_boolean), axis=None)

or:

df.style.apply(lambda c: df_bool[c.name].apply(color_boolean))

output:

dataframe style

mozway
  • 194,879
  • 13
  • 39
  • 75
  • Thanks, appreciate it! I'm trying to also add on an additional condition (mode of column). ``` def highlightMode(s): # Get mode of columns mode_ = s.mode().values # Apply style if the current value is mode return ['background-color: yellow' if v in mode_ else '' for v in s] ``` I'm a little confused on how to chain them together when one is using a separate df as a mask. Appreciate any advice, thanks! – Scott O May 14 '22 at 23:14
  • There are several options. A simple one might be to generate a DataFrame of the styles and then use this single one to apply them. Or use `numpy.select` with the many conditions. – mozway May 15 '22 at 04:59
1

You can also use np.where to convert df_bool into a DataFrame of styles based on the locations of 1 values (df_bool.eq(1)).

By setting axis=None to Styler.apply we can effectively apply styles to the entire DataFrame.

true_css = 'color:red'
false_css = '' # No Styles (Change if needed)

df.style.apply(
    lambda _: np.where(df_bool.eq(1), true_css, false_css),
    axis=None
)

Styled DataFrame with red colored text where df == 1 is True


Optional format to round and match shown output:

true_css = 'color:red'
false_css = ''

df.style.apply(
    lambda _: np.where(df_bool.eq(1), true_css, false_css),
    axis=None
).format(formatter='{:1g}')

Styled DataFrame with red text including formatted rounding


Imports (and versions):

import numpy as np  # 1.22.3
import pandas as pd  # 1.4.2
Henry Ecker
  • 34,399
  • 18
  • 41
  • 57