python - Inconsistent results with pandas styler applymap - Stack Overflow

So I am trying to style a pandas dataframe on a streamlit app. I was able to get it to style appropriat

So I am trying to style a pandas dataframe on a streamlit app. I was able to get it to style appropriately with the following code:

df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9], 'X': [10, 11, 12], 'Y': [13, 14, 15], 'Z': [16, 17, 18]})

default_colors = {
    "A": "brown",
    "B": "blue",
    "C": "green",
    "X": "orange",
    "Y": "red",
    "Z": "purple"
}


def highlight_columns(val, color):
    return f'color: {color}; font-weight: bold;' if val else ''

if 'A' in df.columns:
    styled_df.applymap(lambda val: highlight_columns(val, default_colors['A']), subset=['A'])

if 'B' in df.columns:
    styled_df.applymap(lambda val: highlight_columns(val, default_colors['B']), subset=['B'])

if 'C' in df.columns:
    styled_df.applymap(lambda val: highlight_columns(val, default_colors['C']), subset=['C'])

if 'X' in df.columns:
    styled_df.applymap(lambda val: highlight_columns(val, default_colors['X']), subset=['X'])

if 'Y' in df.columns:
    styled_df.applymap(lambda val: highlight_columns(val, default_colors['Y']), subset=['Y'])

if 'Z' in df.columns:
    styled_df.applymap(lambda val: highlight_columns(val, default_colors['Z']), subset=['Z'])

But upon refactoring with:

for column in 'ABCXYZ':
    if column in df.columns:
        styled_df.applymap(lambda val: highlight_columns(val, default_colors[column]), subset=[column])

All columns iterated over are updated to the the final columns color. In this case column 'Z'. What is going on here? Why do the 6 if statements work fine but the loop doesn't?

So I am trying to style a pandas dataframe on a streamlit app. I was able to get it to style appropriately with the following code:

df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9], 'X': [10, 11, 12], 'Y': [13, 14, 15], 'Z': [16, 17, 18]})

default_colors = {
    "A": "brown",
    "B": "blue",
    "C": "green",
    "X": "orange",
    "Y": "red",
    "Z": "purple"
}


def highlight_columns(val, color):
    return f'color: {color}; font-weight: bold;' if val else ''

if 'A' in df.columns:
    styled_df.applymap(lambda val: highlight_columns(val, default_colors['A']), subset=['A'])

if 'B' in df.columns:
    styled_df.applymap(lambda val: highlight_columns(val, default_colors['B']), subset=['B'])

if 'C' in df.columns:
    styled_df.applymap(lambda val: highlight_columns(val, default_colors['C']), subset=['C'])

if 'X' in df.columns:
    styled_df.applymap(lambda val: highlight_columns(val, default_colors['X']), subset=['X'])

if 'Y' in df.columns:
    styled_df.applymap(lambda val: highlight_columns(val, default_colors['Y']), subset=['Y'])

if 'Z' in df.columns:
    styled_df.applymap(lambda val: highlight_columns(val, default_colors['Z']), subset=['Z'])

But upon refactoring with:

for column in 'ABCXYZ':
    if column in df.columns:
        styled_df.applymap(lambda val: highlight_columns(val, default_colors[column]), subset=[column])

All columns iterated over are updated to the the final columns color. In this case column 'Z'. What is going on here? Why do the 6 if statements work fine but the loop doesn't?

Share edited Mar 3 at 20:41 Patrick Garrett asked Mar 3 at 20:41 Patrick GarrettPatrick Garrett 32 bronze badges 2
  • try styled_df = styled_df.applymap(...) in your loop – iBeMeltin Commented Mar 3 at 21:19
  • I tried this, but it didn’t change anything. I also tried using map instead of applymap, since the latter seems to be deprecated. – Patrick Garrett Commented Mar 4 at 3:18
Add a comment  | 

2 Answers 2

Reset to default 2

The issue with your refactored version is due to "late binding", as explained in the answer by @furas. See also: Why do lambdas defined in a loop with different values all return the same result?.

For your use case, consider using Styler.apply:

import pandas as pd

default_colors = {
    # "A": "brown",
    "B": "blue",
    "C": "green",
    "X": "orange",
    "Y": "red",
    # "Z": "purple"
}

def highlight_columns(s, default_colors):
    if s.name in default_colors:
        val = f'color: {default_colors[s.name]}; font-weight: bold;'
        return [val]*len(s)
    return ['']*len(s)
    
styled_df = df.style.apply(highlight_columns, axis=0, 
                           default_colors=default_colors)

Output:

Explanation

  • Apply highlight_columns to each column (axis=0).
  • Check if the column name (s.name) exists in default_colors.
  • Return a list (list-like object) of same length with the appropriate CSS or ''.

I can't test it but it can be common problem with lambda in for-loops.

And maybe some functions in pandas can be "lazy" and pandas may execute them later (when data will be really needed) so it may run lambda after executing all code in for-loop.

lambda doesn't copy value from varaible column to code in lambda but it keeps only reference to this variable and it gets value from variable later - when variable will already have last value Z.

It needs to assign value to parameter in lambda and this will copy value.

lambda val, col=column: highlight_columns(val, default_colors[col]), subset=[col]

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745073179a4609677.html

相关推荐

  • python - Inconsistent results with pandas styler applymap - Stack Overflow

    So I am trying to style a pandas dataframe on a streamlit app. I was able to get it to style appropriat

    15小时前
    40

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信