Applying style to a pandas DataFrame row-wise
Asked Answered
C

3

8

I'm experimenting/learning Python with a data set containing customers information.

The DataFrame structure is the following (these are made up records):

import pandas as pd

df1 = pd.DataFrame({'left_name' : ['James', 'Mary', 'John', 'Patricia'],
                    'left_age' : [30, 37, 30, 35], 
                    'right_name' : ['Robert', 'Jennifer', 'Michael', 'Linda'], 
                    'right_age' : [30, 31, 38, 35]})
print(df1)

  left_name  left_age right_name  right_age
0     James        30     Robert         30
1      Mary        37   Jennifer         31
2      John        30    Michael         38
3  Patricia        35      Linda         35

Applying the transpose method to df1, we get the following view:

df2 = df1.T
print(df2)

                 0         1        2         3
left_name    James      Mary     John  Patricia
left_age        30        37       30        35
right_name  Robert  Jennifer  Michael     Linda
right_age       30        31       38        35

My goal is to apply some styling to df2. Specifically,

  • The left_name and right_name rows should be highlighted in yellow;
  • The left_age and right_age rows should be highlighted in blue.

I did some research before posting here and I managed to highlight one subset the following way:

df2.style.set_properties(subset = pd.IndexSlice[['left_name', 'right_name'], :], **{'background-color' : 'yellow'})

enter image description here

The problem is that I'm unable to combine multiple styles together. If I add an additional blue color for left_age and right_age using the same method as above, I "lose" the previous style.

Ideally, I would like to have a function that takes df2 as input and returns the styled DataFrame.

Celery answered 20/8, 2019 at 13:7 Comment(0)
K
6

You can create DataFrame of styles with Styler.apply and set rows by index value with loc:

def highlight(x):
    c1 = 'background-color: yellow'
    c2 = 'background-color: blue'

    df1 = pd.DataFrame('', index=x.index, columns=x.columns)
    df1.loc[['left_name','right_name'], :] = c1
    df1.loc[['left_age','right_age'], :] = c2
    return df1

df1.T.style.apply(highlight, axis=None)
Kiwi answered 20/8, 2019 at 13:14 Comment(3)
I knew there must've been a nicer way. You're always teaching me something!Jade
If I do above I receive the error : AttributeError: 'Styler' object has no attribute 'T'Laverty
@Laverty change it to df1.T.style.apply(highlight, axis=None)Dietetics
J
8

You were so close! You can actually "chain" set_properties on the same dataframe:

df2.style.set_properties(subset = pd.IndexSlice[['left_name','right_name'], :], **{'background-color' : 'yellow'})\
.set_properties(subset = pd.IndexSlice[['left_age','right_age'], :], **{'background-color' : 'blue'})

enter image description here

I'm sure there is a more elegant solution - but this works!

Jade answered 20/8, 2019 at 13:15 Comment(2)
Indeed I was close! I have a slight preference for jezrael solution but I also upvoted yours.Celery
@VonKar, for what it's worth... I also have a preference for his solution hahaJade
K
6

You can create DataFrame of styles with Styler.apply and set rows by index value with loc:

def highlight(x):
    c1 = 'background-color: yellow'
    c2 = 'background-color: blue'

    df1 = pd.DataFrame('', index=x.index, columns=x.columns)
    df1.loc[['left_name','right_name'], :] = c1
    df1.loc[['left_age','right_age'], :] = c2
    return df1

df1.T.style.apply(highlight, axis=None)
Kiwi answered 20/8, 2019 at 13:14 Comment(3)
I knew there must've been a nicer way. You're always teaching me something!Jade
If I do above I receive the error : AttributeError: 'Styler' object has no attribute 'T'Laverty
@Laverty change it to df1.T.style.apply(highlight, axis=None)Dietetics
T
1

Following up on MattR's answer: instead of chaining, you can also assign the result to a styler for the first call to set_properties, and call set_properties again - plus slightly different way of selecting the rows:

s1 = df1.T.style.set_properties(**{'background-color': 'yellow'}, subset=(['left_name', 'right_name'], slice(None)))
s1 = s1.set_properties(**{'background-color': 'blue'}, subset=(['left_age', 'right_age'], slice(None)))
s1
Textuary answered 17/9, 2021 at 7:33 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.