Pandas Dataframe Examples: Styling Cells and Conditional Formatting

Last updated:
Table of Contents

All code available on this jupyter notebook

Highlight cell if condition

Here we apply elementwise formatting, because the logic only depends on the single value itself.

Use df.applymap(styler_function) where styler_function takes a cell value and returns a CSS style

Example: Change background color for even numbers

import pandas as pd

df = pd.DataFrame({
    "name":         ["alan","beth","charlie","david", "edward"],
    "age" :         [34,    12,     43,      32,      77],
    "num_children": [1,     0,      2,       1,       6],
    "num_pets":     [1,     0,      1,       2,       0],
    "bank_balance": [100.0, 10.0,   -10.0,   30.0,    30.0]})

def even_number_background(cell_value):

    highlight = 'background-color: darkorange;'
    default = ''

    if type(cell_value) in [float, int]:
        if cell_value % 2 == 0:
            return highlight
    return default

df.style.applymap(is_even_background)

base-dataframe Base dataframe
  
pandas-dataframe-with-elementwise-styling All even numbers had their
background changed

Highlight cell row by row

In other words, we will apply a row-wise styling.

Use df.style.apply(func, axis=1). Use subset=[cols] to limit application to some columns only.

Example: Highlight whether each person has more children or more pets

import pandas as pd

df = pd.DataFrame({
    "name":         ["alan","beth","charlie","david", "edward"],
    "age" :         [34,    12,     43,      32,      77],
    "num_children": [1,     0,      2,       1,       6],
    "num_pets":     [1,     0,      1,       2,       0],
    "bank_balance": [100.0, 10.0,   -10.0,   30.0,    30.0]})

def more_children_or_more_pets_background(row):    

    highlight = 'background-color: lightcoral;'
    default = ''

    # must return one string per cell in this row
    if row['num_children'] > row['num_pets']:
        return [highlight, default]
    elif row['num_pets'] > row['num_children']:
        return [default, highlight]
    else:
        return [default, default]

df.style.apply(more_children_or_more_pets_background, subset=['num_children', 'num_pets'], axis=1)

base-dataframe-unstyled Base dataframe, unstyled.
  
styled-dataframe-with-rowwise-styling Charlie and Edward have more children
than pets, while David has more
pets than children.

Highlight cell if largest in column

That is, column-wise styling.

Use df.style.apply(func, axis=0)

Example: Change background colour in the maximum values for num_children and num_pets:

import pandas as pd

df = pd.DataFrame({
    "name":         ["alan","beth","charlie","david", "edward"],
    "age" :         [34,    12,     43,      32,      77],
    "num_children": [1,     0,      2,       1,       6],
    "num_pets":     [1,     0,      1,       2,       0],
    "bank_balance": [100.0, 10.0,   -10.0,   30.0,    30.0]})

def maximum_value_in_column(column):    

    highlight = 'background-color: palegreen;'
    default = ''

    maximum_in_column = column.max()

    # must return one string per cell in this column
    return [highlight if v == maximum_in_column else default for v in column]

df.style.apply(maximum_value_in_column, subset=['num_children', 'num_pets'], axis=0)

base-unstyled-dataframe Base dataframe
  
styled-pandas-dataframe The maximum values for num_children and
and num_pets are 6 and 2,
respectively.

Apply style to column only

In order to restrict application of some style to some columns, use subset=[cols]:

  • df.style.apply(func, subset=['col1'], axis=1) to apply func rowwise to column 'col1' only

  • df.style.apply(func, subset=['col2'], axis=0) to apply func columnwise to column 'col2' only

Apply multiple styles

In this example:

  • use red font for negative values in bank_balance
  • write the highest age in bold
  • use light red background for zero values in num_children and num_pets
import pandas as pd

df = pd.DataFrame({
    "name":         ["alan","beth","charlie","david", "edward"],
    "age" :         [34,    12,     43,      32,      77],
    "num_children": [1,     0,      2,       1,       6],
    "num_pets":     [1,     0,      1,       2,       0],
    "bank_balance": [100.0, 10.0,   -10.0,   30.0,    30.0]})

def red_font_negatives(series):
    highlight = 'color: red;'
    default = ''
    return [highlight if e < 0 else default for e in series]  

def bold_max_value_in_series(series):
    highlight = 'font-weight: bold;'
    default = ''

    return [highlight if e == series.max() else default for e in series]  

def red_background_zero_values(cell_value):
    highlight = 'background-color: tomato;'
    default = ''
    if cell_value == 0:
        return highlight
    else:
        return default     

(df
 .style
 .apply(red_font_negatives, axis=0, subset=['bank_balance'])
 .apply(bold_max_value_in_series, axis=0, subset=['age'])
 .applymap(red_background_zero_values))

base-unstyled Base dataset, unstyled
  
dataframe-with-multiple-chained-applications Multiple stylings applied together:
largest age in bold, zero values in
pets or children and negative
bank balances.

Dialogue & Discussion