Deshwal
Deshwal

Reputation: 4162

change Pandas Row background colour and Font Colors based on 2 different conditions

I have a code to change the cell background colour if the Rising is False. I want to change the Cell Colour to Green if the value in the Index is Nifty 50.

So If any rows is from Nifty 50 but Rising is False, then the Nifty 50 cell should be in Green background of that Only cell and all of the other cells should be in Red.

This is what I want the Dataframe to look like: Whole Row is Red if Rising is False or 0. Color of Index is changed based on whether it is from Nifty-50/100/200

enter image description here

And the code for changing color is as follows:

def highlight_falling(s, column:str):
        '''
        Highlight The rows where average is falling
        args:
            s: Series
            column: Column name(s)
        '''
        is_max = pd.Series(data=False, index=s.index)
        is_max[column] = s.loc[column] == True
        return ['' if is_max.any() else 'background-color: #f7a8a8' for v in is_max]

picked.style.apply(highlight_falling, column=['Rising'], axis=1) # picked is the DF

Here I want to give the Index of 50,100,200,500 cell as [Green,Blue,Magenta. White] (just tan example)

Upvotes: 0

Views: 1531

Answers (1)

Henry Ecker
Henry Ecker

Reputation: 35686

We can use use Series.map to associate the values in the column with the new color styles. We will then apply the Index column styles after the row styles so as to override the previously placed red colour:

def highlight_falling(f: pd.DataFrame, column: str):
    # Create an Empty DataFrame
    f_styles = pd.DataFrame('', index=f.index, columns=f.columns)
    # Apply Styles based on column. (Set red where not Truthy)
    f_styles.loc[~(f[column].astype(bool)), :] = 'background-color: #f7a8a8'
    return f_styles


def highlight_nifty(s: pd.Series):
    return 'background-color: ' + s.map({
        'Nifty 50': 'green',
        'Nifty 100': 'blue',
        'Nifty 200': '#888888'
    })  # Map to colour codes


# Save Styler To Re-use (can also Chain)
styler = picked.style
# Apply Row Colour (Do not pass column as List[str] use str!!)
styler.apply(highlight_falling, column='Rising', axis=None)
# Apply Index Column Colours
styler.apply(highlight_nifty, subset='Index')

styled table


The mapping dictionary could also be created with dict and zip if wanted to specify a list of colors can use unique to get all unique values from the Index column, then natsorted can be used to order them (safe alphanumeric ordering):

from natsort import natsorted
from typing import List


def highlight_falling(f: pd.DataFrame, column: str):
    # Create an Empty DataFrame
    f_styles = pd.DataFrame('', index=f.index, columns=f.columns)
    # Apply Styles based on column. (Set red where not Truthy)
    f_styles.loc[~(f[column].astype(bool)), :] = 'background-color: #f7a8a8'
    return f_styles



def highlight_nifty(s: pd.Series, colours: List[str]):
    return 'background-color: ' + s.map(
        # Build Colour Map Dynamically based on unique values from column
        dict(zip(natsorted(s.unique()), colours))
    )  # Map to colour codes


# Save Styler To Re-use (can also Chain)
styler = picked.style
# Apply Row Colour (Do not pass column as List[str] use str!!)
styler.apply(highlight_falling, column='Rising', axis=None)
# Apply Index Column Colours
styler.apply(highlight_nifty, subset='Index',
             colours=['green', 'blue', '#888888'])

styled table

Upvotes: 1

Related Questions