2

I want to highlight cells if they are greater than a value dependent on the column header.

I want to 'read' the column header, if it is in the dictionary (CEPA_FW) then the corresponding value is returned. Then if any cells in that column are greater than this value, they are filled dark orange. My effort is below, but I am getting errors (ValueError: Length mismatch: Expected axis has 1 elements, new values have 4 elements).

df=pd.DataFrame(({'As':['0.001', 0, '0.001','0.06'], 'Zn': ['6','4','6','8'], 'Pb': ['0.006','0','0.005','0.005'], 'Yt': [1,0,0.002,6]}))
cols=df.columns

CEPA_FW=  {'Ag':0.05,'As' :0.05 ,'Ba':1.0,'B':1.0,'Cd' :0.01 ,'Cr' :0.05 ,'Co':0.001,'Cu' :1.0 ,'K':5.0,'Pb' :0.005 ,'Hg' :0.0002 ,'Mn':0.5,'Ni' :1.0 ,'Se':0.01,'Sn':0.5,'SO4':400.0,'Zn' :5.0}



def fill_exceedances(val):
    for header in cols:
        if header in CEPA_FW:
            for c in df[header]:
                fill = 'darkorange' if c> CEPA_FW[header] else ''
                return ['backgroundcolor: %s' % fill]

df.style.apply(fill_exceedances, axis = 1).to_excel('styled.xlsx', engine='openpyxl')
2
  • appropriate limit is returned what do you mean by this? Commented Dec 19, 2018 at 9:53
  • @RahulAgarwal, I have edited to be clearer. Commented Dec 19, 2018 at 10:02

1 Answer 1

1

Use custom function for create DataFrame filled by styles by condition:

def fill_exceedances(x):
    color = 'orange'
    #get columns which are in keys of dict
    c = x.columns.intersection(CEPA_FW.keys())
    #filter columns and rename by dict
    df2 = x[c].rename(columns=CEPA_FW)
    #create boolean mask only for matched columns and compare
    mask = df2.astype(float).values > df2.columns[None,:].values
    #new DataFrame filled by no color
    df1 = pd.DataFrame('', index=x.index, columns=c)
    #set color by mask and add missing non matched columns names by reindex
    df1 = (df1.where(mask, 'background-color: {}'.format(color))
              .reindex(columns=x.columns, fill_value=''))

    return df1

df.style.apply(fill_exceedances, axis=None).to_excel('styled.xlsx', engine='openpyxl')

pic

Sign up to request clarification or add additional context in comments.

13 Comments

Thanks, when I replace' >' with '<' this works perfectly. It is quite an 'involved' solution though. Is it necessary to create a separate dataframe of just the columns that are dictionary keys? Anyway, I don't want to sound ungrateful, this is great, thank you.
@flashliquid - yes, maybe there is also better solution, but here is really advantage working with DataFrame common way. It is especially good if some complicated solutions.
I stumbled on this answer you gave to a similar question a while back: stackoverflow.com/a/44942410/6108107 could this approach also work in my case? What would the function be?
@flashliquid - yes, is possible change mask = df2.astype(float).values > df2.columns[None,:].values to mask = (df2.astype(float).apply(lambda x: x > x.name, axis=0).values), but performance is worse in large DataFrame
@flashliquid - You can change mask to mask = df2.apply(pd.to_numeric, errors='coerce').fillna(np.inf).values > df2.columns[None,:].values
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.