Pandas Dataframe Examples: Styling Cells and Conditional Formatting

Pandas Dataframe Examples: Styling Cells and Conditional Formatting

Last updated:
Table of Contents

All code available on this jupyter notebook

Style 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

Row-wise style

In other words: for row, take all values and decide which ones to style.

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

Chain styles

In other words, apply multiple styles one after the other:

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.

Multiple styles in same function

You can use multiple else clauses in a ternary if-else condition too:

import pandas as pd

df = pd.DataFrame({
    "name":         ["alan","beth","charlie","david", "edward"],
    "bank_balance": [100.0, 10.0,   -10.0,   30.0,    30.0]})

def colour_numbers(series):
    red    = 'background-color: red;'
    orange = 'background-color: orange;'     
    default = ''

    # note multiple else ..if conditions
    return [red if e < 0 else orange if e < 15 else default for e in series]  

(df
 .style
 .apply(colour_numbers, axis=0, subset=['bank_balance']))

before-pandas-dataframe-no-styling BEFORE: no styling
  
after-applied-pandas-styling-to-individual-cells AFTER: our logic now checks
two conditions and depending
on which one matched, the cell
gets a different colour