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 |2 Answers
Reset to default 2The 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 indefault_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
styled_df = styled_df.applymap(...)
in your loop – iBeMeltin Commented Mar 3 at 21:19