Search code examples
pythonpandasdataframepandas-styles

Pass Attributes (not functions) to python `pandas.DataFrame.style`


I have a pandas.DataFrame with values in it, say:

df = pd.DataFrame(np.random.randn(5, 3), columns=['a', 'b', 'c'])

In [160]: df
Out[160]:
          a         b         c
0 -0.316527 -0.721590  1.812285
1 -1.704653 -0.415888 -0.294740
2 -1.126637  0.032084 -1.344484
3  0.081789 -1.311954  1.941496
4  0.617405  0.114212 -0.763610

Now I've written my own color gradient functions so that I get a pd.DataFrame of the same size and shape, but with color hex codes for each cell, say:

df_clrs = pd.DataFrame([
    ['#bc4700', '#dea380', '#bc4700'], 
    ['#384f69', '#dea380', '#bc4700'], 
    ['#dea380', '#bc4700', '#384f69'], 
    ['#384f69', '#384f69', '#dea380'],
    ['#dea380', '#bc4700', '#384f69']], 
    columns=['a', 'b', 'c']
)

In [164]: df_clrs
Out[164]:
         a        b        c
0  #bc4700  #dea380  #bc4700
1  #384f69  #dea380  #bc4700
2  #dea380  #bc4700  #384f69
3  #384f69  #384f69  #dea380
4  #dea380  #bc4700  #384f69

I let's say I've done this as well with text colors, so:

 df_fnts = pd.DataFrame([
    ['#f1f1f1','#f1f1f1','#000000'],
    ['#000000','#f1f1f1','#f1f1f1'],
    ['#000000','#f1f1f1','#000000'],
    ['#f1f1f1','#000000','#f1f1f1'],
    ['#000000','#000000','#f1f1f1']],
    columns=['a', 'b' ,'c']
)

In [167]: df_fnts
Out[167]:
         a        b        c
0  #f1f1f1  #f1f1f1  #000000
1  #000000  #f1f1f1  #f1f1f1
2  #000000  #f1f1f1  #000000
3  #f1f1f1  #000000  #f1f1f1
4  #000000  #000000  #f1f1f1

My goal is to now expose the DatFrame.style functionality, as demonstrated in these tutorials.

However, all of the functions demonstrated in tutorial focus on passing a function (using the pd.DataFrame.style.applymap), however, I have all of the properties already created.

Things I've Tried

Because in the documentation it looks like you need to append the value with the appropriate property, I've created a function like this:

def _apply_prop(df, prop):
   return df.applymap(lambda x: prop + ':' + x)

# apply the color mapping
df.style.applymap(
    _apply_prop(
        df_clrs, 
       'background-color'
    )
).to_excel('~/Desktop/background-colors.xlsx')

But I get a TypeError

TypeError: the first argument must be callable

Solution

  • df (5x3) and df_clrs (4x3) have different shapes. Assuming you got that corrected, try this:

    def _apply_prop(_, style, prop):
        return style.applymap(lambda x: prop + ':' + x)
    
    df.style.apply(_apply_prop, axis=None, style=df_clrs, prop='background-color')
    

    Output:

    Styled data frame

    Some notes:

    • Don't call style.applymap. It iterates over cells. Use apply(..., axis=...) to iterate over column / row / table. Whatever you iterate over, return an object with the same shape.
    • You do not execute the styling function in apply / applymap. You supply the function's name along with its parameters
    • The first parameter of the styling function is always the data frame being styled. apply / applymap implicitly passes the data frame to the styling function. You can pass additional arguments by keywords.