Search code examples
pythonpandaspandas-styles

How to combine different styles in one styler map?


I have a dataframe with 3 kind of data types - int64, float64 and object (string). How to apply format for two different data types in one function?

Sample:

def abc_style_format(cell_val):
    default = ""
    style_a = "background-color: green"
    style_b = "background-color: gold"
    style_c = "background-color: salmon"
    style_float = "precision=2"

    match cell_val:
        case "A":
            return style_a
        case "B":
            return style_b
        case "C":
            return style_c

    if type(cell_val) == "float64":
        return style_float

    return default

df_abc.style.map(abc_style_format)

Block match/case works properly, but part precision=2 doesn't work.

The result:

enter image description here


Solution

  • Here is one way to do it by defining as many helper functions as needed:

    import pandas as pd
    
    
    def _abc_style_format(s: pd.Series) -> list[str]:
        """Helper function to format 'abc' columns.
    
        Args:
            s: target column.
    
        Returns:
            list of styles to apply to each target column cell.
    
        """
        background_colors = []
        for v in s:
            match v:
                case "A":
                    background_colors.append("background-color: green")
                case "B":
                    background_colors.append("background-color: gold")
                case "C":
                    background_colors.append("background-color: salmon")
        return background_colors
    
    def _qty_format(s: pd.Series) -> list[str]:
        """Helper function to format 'qty' column.
    
        Args:
            s: target column.
    
        Returns:
            list of styles to apply to each target column cell.
    
        """
        font_colors = []
        for v in s:
            if v > 150:
                font_colors.append("color: red")
            else:
                font_colors.append("color: green")
        return font_colors
    

    And one main function:

    from pandas.io.formats.style import Styler
    
    
    def style(df: pd.DataFrame) -> Styler:
        """Function to style whole dataframe.
    
        Args:
            df: target dataframe.
    
        Returns:
            styled dataframe.
    
        """
        styler = df.style
        for col in df.columns:
            match df[col].dtype:
                case "object":
                    styler = styler.apply(_abc_style_format, subset=col)
                case "float64":
                    styler = styler.format(precision=2, subset=col)
                case "int64":
                    styler = styler.apply(_qty_format, subset=col)
                case _:
                    pass
        return styler
    

    Then, with the following toy dataframe:

    df = pd.DataFrame(
        {
            "description": ["product1", "product2", "product3"],
            "qty": [130, 160, 110],
            "gm": [43.20000, 40.70000, 35.20000],
            "abc_gm_category": ["A", "B", "C"],
            "abc_amount_category": ["C", "A", "B"],
        }
    )
    

    You can simply do:

    styled_df = style(df)
    
    styled_df
    

    Which gets you the expected output:

    enter image description here