PyXLL The Python Excel Add-In
  • Product
    • Features
    • Get Started
    • Request Demo
    • Download
  • Pricing
  • Resources
    • Documentation
    • Blog
    • Videos
    • FAQ
    • Learn Python
    • Customer Portal
    • About Us
  • Support
    • Documentation
    • Videos
    • FAQ
    • Contact Us
  • Contact Us
Table of Contents
  • PyXLL Documentation
  • Introduction to PyXLL
  • User Guide
    • Installing PyXLL
    • Configuring PyXLL
    • Worksheet Functions
    • Macro Functions
    • Real Time Data
    • Cell Formatting
      • Formatting Worksheet Functions
      • Pandas DataFrame Formatting
      • Custom Formatters
      • Formatting in Macros Functions
    • Charts and Plotting
    • Custom Task Panes
    • ActiveX Controls
    • Using Pandas in Excel
    • Customizing the Ribbon
    • Context Menu Functions
    • Working with Tables
    • Python as a VBA Replacement
    • Menu Functions
    • Reloading and Rebinding
    • Error Handling
    • Deploying your add-in
    • Workbook Metadata
  • Video Guides and Tutorials
  • API Reference
  • What’s new in PyXLL 5
  • Changelog
Close

Pandas DataFrame Formatting¶

Array formulas can also be formatted, and PyXLL provides the DataFrameFormatter class specifically for functions that return pandas DataFrames.

from pyxll import xl_func, xl_return, Formatter, DataFrameFormatter
import pandas as pd

df_formatter = DataFrameFormatter(
    index=Formatter(bold=True, interior_color=Formatter.rgb(0xA9, 0xD0, 0x8E)),
    header=Formatter(bold=True, interior_color=Formatter.rgb(0xA9,0xD0,0x8E)),
    rows=[
        Formatter(interior_color=Formatter.rgb(0xE4, 0xF1, 0xDB)),
        Formatter(interior_color=Formatter.rgb(0xF4, 0xF9, 0xF1)),
    ],
    columns={
        "C": Formatter(number_format="0.00%")
    }
)

@xl_func(formatter=df_formatter, auto_resize=True)
@xl_return("dataframe<index=True>")
def get_dataframe():
    df = pd.DataFrame({
        "A": [1, 2, 3],
        "B": [4, 5, 6],
        "C": [0.3, 0.6, 0.9]
    })
    return df

When the function is called from Excel, any previous formatting is cleared and the formatter is applied to the range for the DataFrame.

The DataFrameFormatter class handles many common formatting requirements, but more complex formatting can be done by a custom formatter.

If the size of the DataFrame changes when inputs change, as long as the formula stays the same the previous range will be cleared before formatting the new range. This allows the returned range to contract without the formatting being left behind.

Formatting a DataFrame returned from a UDF

Conditional Formatting¶

As well as formatting specific rows and columns based on their position in the DataFrame as shown above, it is also possible to apply formatting that is conditional on the values in the DataFrame.

This is done using the ConditionalFormatter class.

The ConditionalFormatter class is constructed with an expression string and a formatter object. The expression string is passed to the DataFrame.eval method which returns a Series where that expression evaluates to True. The formatter will be applied to the rows where that expression is True. The formatting can be further restricted to only apply to specific columns.

A list of ConditionalFormatter objects can be passed as the conditional_formatters argument to DataFrameFormatter. The conditional formatters are applied in order after any other formatting has been applied.

The following example shows how to color rows green where column A is greater than 0 and red where column A is less than 0.

from pyxll import DataFrameFormatter, ConditionalFormatter, Formatter, xl_func
import pandas as pd

green_formatter = Formatter(interior_color=Formatter.rgb(0x00, 0xff, 0x00))
red_formatter = Formatter(interior_color=Formatter.rgb(0xff, 0x00, 0x00))

a_gt_zero = ConditionalFormatter("A > 0", formatter=green_formatter)
b_lt_zero = ConditionalFormatter("A < 0", formatter=red_formatter)

df_formatter = DataFrameFormatter(conditional_formatters=[
                                    a_gt_zero,
                                    b_lt_zero])

@xl_func(": dataframe<index=False>", formatter=df_formatter, auto_resize=True)
def get_dataframe():
    df = pd.DataFrame({
        "A": [-1, 0, 1],
        "B": [1, 2, 3],
        "C": [4, 5, 6]
    })
    return df
Conditional formatting of a DataFrame returned from a UDF

To restrict the formatting to certain columns the columns argument to ConditionalFormatter can be used. This can be a list of column names or a function that takes a DataFrame and returns a list of columns.

Custom Conditional Formatters¶

For more complex conditional formatting a custom conditional formatter class can be derived from ConditionalFormatterBase.

Formatting a DataFrame using a custom conditional formatter

The method ConditionalFormatterBase.get_formatters should be implemented to return a DataFrame of Formatter objects where any formatting is to be applied.

The returned DataFrame must have the same index and columns as the DataFrame being formatted.

The following example shows how a custom ConditionalFormatter can be written that changes the background color of cells in a DataFrame based on their value.

from pyxll import xl_func, DataFrameFormatter, ConditionalFormatterBase, Formatter
from matplotlib import colors, cm
import pandas as pd


class RainbowFormatter(ConditionalFormatterBase):

    def __init__(self, column, min=0, max=100, cmap="rainbow"):
        self.column = column
        self.min = min
        self.max = max
        self.cmap = cmap

    def get_formatters(self, df):
        # Create an empty DataFrame with the same index and columns as df.
        formatters = pd.DataFrame(None, index=df.index, columns=df.columns)

        # Normalize the column values into the range [0, 1]
        normalizer = colors.Normalize(self.min, self.max)
        values = normalizer(df[self.column])

        # Get a list of (r,g,b,a) colors from a colormap.
        colormap = cm.get_cmap(self.cmap)
        color_values = colormap(values)

        # Create the Formatter objects, remembering Formatter.rgb takes integers from 0 to 255.
        # This could use any Formatter class, including your own custom formatters.
        formatters[self.column] = [
            Formatter(interior_color=Formatter.rgb(int(r * 255), int(g * 255), int(b * 255)))
            for r, g, b, a in color_values
        ]

        # Return the DataFrame containing Formatter objects for the cells we want to format
        return formatters


# Construct a DataFrameFormatter using our custom RainbowFormatter class.
# Multiple formatters can be combined by adding them together.
df_formatter = DataFrameFormatter(
    header=Formatter(interior_color=Formatter.rgb(255, 255, 0)),
    conditional_formatters=[
        RainbowFormatter("A"),
        RainbowFormatter("B")
    ]
)


# This worksheet function uses our DataFrameFormatter and RainbowFormatters
@xl_func("int min, int max, int step: dataframe", formatter=df_formatter)
def custom_formatter_test(min=0, max=100, step=5):
    df = pd.DataFrame({"A": range(min, max, step), "B": range(max, min, -step)})
    return df
« Formatting Worksheet Functions
Custom Formatters »
  • Home
  • Product
  • Features
  • Documentation
  • Download
  • Pricing
  • Support
  • Documentation
  • Videos
  • FAQ
  • Learn Python
  • Contact Us
  • About
  • About Us
  • Legal
  • Blog
© Copyright PyXLL Ltd