7

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.

Henry Ecker
  • 34,399
  • 18
  • 41
  • 57
glpsx
  • 587
  • 1
  • 7
  • 21

3 Answers3

7

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!

MattR
  • 4,887
  • 9
  • 40
  • 67
  • Indeed I was close! I have a slight preference for **jezrael** solution but I also upvoted yours. – glpsx Aug 20 '19 at 13:26
  • 2
    @VonKar, for what it's worth... I also have a preference for his solution haha – MattR Aug 20 '19 at 13:32
5

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)
jezrael
  • 822,522
  • 95
  • 1,334
  • 1,252
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
Alex Waygood
  • 6,304
  • 3
  • 24
  • 46
gira
  • 175
  • 6