I want to color a column by value with color scale according to value high to small like this
currently, I create the dash table in a function and send it by loop for each column;
def make_table_in_div(df, column_name):
pv = pd.pivot_table(df, index=[column_name], values=['val1'], aggfunc=['mean', 'count']).reset_index()
pv.columns = [column_name, 'val1', 'count']
print(column_name)
div = html.Div([html.H1(column_name), dash_table.DataTable(
columns=[{"name": i, "id": i} for i in pv.columns],
data=pv.to_dict('records'),
)], style={'height': 30, 'margin-right': 'auto', 'margin-left': 'auto', 'width': '800px'}) # 'width': '50%',
return div
div = [make_table_in_div(df, column_name) for column_name in ['column_name']]
return div
the dash table looks as the flowing picture, and I want to colorize the value column
Thanks to The answer of Kristian Haga. - It's working great.
I'd like to summarize the options for future users with the same question and me. When we want to run it on multiple columns, there are two options:
The original function will colorize all the columns with same scale (min and max) so if I run with multiple columns (from the example: values and count) its returns the table style colorized based on the range of min and max out of all columns (from the example: 0.193,109).discrete_background_color_bins(df, columns=['value','count'])
def discrete_background_color_bins(df, n_bins=7, columns='all'):
bounds = [i * (1.0 / n_bins) for i in range(n_bins+1)]
if columns == 'all':
if 'id' in df:
df_numeric_columns = df.select_dtypes('number').drop(['id'], axis=1)
else:
df_numeric_columns = df.select_dtypes('number')
else:
df_numeric_columns = df[columns]
df_max = df_numeric_columns.max().max()
df_min = df_numeric_columns.min().min()
ranges = [
((df_max - df_min) * i) + df_min
for i in bounds
]
styles = []
legend = []
for i in range(1, len(bounds)):
min_bound = ranges[i - 1]
max_bound = ranges[i]
backgroundColor = colorlover.scales[str(n_bins+4)]['div']['RdYlGn'][2:-2][i - 1]
color = 'black'
for column in df_numeric_columns:
styles.append({
'if': {
'filter_query': (
'{{{column}}} >= {min_bound}' +
(' && {{{column}}} < {max_bound}' if (i < len(bounds) - 1) else '')
).format(column=column, min_bound=min_bound, max_bound=max_bound),
'column_id': column
},
'backgroundColor': backgroundColor,
'color': color
})
legend.append(
html.Div(style={'display': 'inline-block', 'width': '60px'}, children=[
html.Div(
style={
'backgroundColor': backgroundColor,
'borderLeft': '1px rgb(50, 50, 50) solid',
'height': '10px'
}
),
html.Small(round(min_bound, 2), style={'paddingLeft': '2px'})
])
)
return (styles, html.Div(legend, style={'padding': '5px 0 5px 0'}))
If we want to colorize each column separately based on its min and max,
We will use the function below:
(very similar but running on the columns first)
def discrete_background_color_bins(df, n_bins=7, columns='all'):
bounds = [i * (1.0 / n_bins) for i in range(n_bins+1)]
if columns == 'all':
if 'id' in df:
df_numeric_columns = df.select_dtypes('number').drop(['id'], axis=1)
else:
df_numeric_columns = df.select_dtypes('number')
else:
df_numeric_columns = df[columns]
df_max = df_numeric_columns.max().max()
df_min = df_numeric_columns.min().min()
ranges = [
((df_max - df_min) * i) + df_min
for i in bounds
]
styles = []
legend = []
for i in range(1, len(bounds)):
min_bound = ranges[i - 1]
max_bound = ranges[i]
backgroundColor = colorlover.scales[str(n_bins+4)]['div']['RdYlGn'][2:-2][i - 1]
color = 'black'
for column in df_numeric_columns:
styles.append({
'if': {
'filter_query': (
'{{{column}}} >= {min_bound}' +
(' && {{{column}}} < {max_bound}' if (i < len(bounds) - 1) else '')
).format(column=column, min_bound=min_bound, max_bound=max_bound),
'column_id': column
},
'backgroundColor': backgroundColor,
'color': color
})
legend.append(
html.Div(style={'display': 'inline-block', 'width': '60px'}, children=[
html.Div(
style={
'backgroundColor': backgroundColor,
'borderLeft': '1px rgb(50, 50, 50) solid',
'height': '10px'
}
),
html.Small(round(min_bound, 2), style={'paddingLeft': '2px'})
])
)
return (styles, html.Div(legend, style={'padding': '5px 0 5px 0'}))
This is possible. You should check out this link: https://dash.plotly.com/datatable/conditional-formatting specifically the part 'Highlighting with a colorscale on a single column'
I have written a quick example for you:
import dash
import dash_table
import pandas as pd
import dash_html_components as html
import colorlover
from jupyter_dash import JupyterDash
# Dash Application
df = pd.DataFrame(list(zip(
[5,6,7,8,9,10,11,12,13,14],
[0.328, 0.323, 0.193, 0.231, 0.216, 0.284, 0.250, 0.258, 0.394, 0.455],
[67, 99, 109, 104, 88, 74, 32, 31, 33, 22]
)), columns=['column_name', 'value', 'count'])
app = JupyterDash(__name__)
# Function for styling table, defined below
cols = ['value']
(styles, legend) = discrete_background_color_bins(df, columns = cols)
app.layout = html.Div([
legend,
dash_table.DataTable(
id = 'table',
columns = [{"name": i, "id": i} for i in df.columns],
data = df.to_dict('records'),
style_data_conditional = styles
)
])
app.run_server(mode='inline')
This function returns the a list of styles for each row for the specified columns using the given colorscale.
To get the colorscale you need to install colorlover with pip install colorlover
Additional colorscales can be found here: https://github.com/plotly/colorlover
# Function for styling the table
def discrete_background_color_bins(df, n_bins=7, columns='all'):
bounds = [i * (1.0 / n_bins) for i in range(n_bins+1)]
if columns == 'all':
if 'id' in df:
df_numeric_columns = df.select_dtypes('number').drop(['id'], axis=1)
else:
df_numeric_columns = df.select_dtypes('number')
else:
df_numeric_columns = df[columns]
df_max = df_numeric_columns.max().max()
df_min = df_numeric_columns.min().min()
ranges = [
((df_max - df_min) * i) + df_min
for i in bounds
]
styles = []
legend = []
for i in range(1, len(bounds)):
min_bound = ranges[i - 1]
max_bound = ranges[i]
backgroundColor = colorlover.scales[str(n_bins+4)]['div']['RdYlGn'][2:-2][i - 1]
color = 'black'
for column in df_numeric_columns:
styles.append({
'if': {
'filter_query': (
'{{{column}}} >= {min_bound}' +
(' && {{{column}}} < {max_bound}' if (i < len(bounds) - 1) else '')
).format(column=column, min_bound=min_bound, max_bound=max_bound),
'column_id': column
},
'backgroundColor': backgroundColor,
'color': color
})
legend.append(
html.Div(style={'display': 'inline-block', 'width': '60px'}, children=[
html.Div(
style={
'backgroundColor': backgroundColor,
'borderLeft': '1px rgb(50, 50, 50) solid',
'height': '10px'
}
),
html.Small(round(min_bound, 2), style={'paddingLeft': '2px'})
])
)
return (styles, html.Div(legend, style={'padding': '5px 0 5px 0'}))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With