Conditional styling a row, based on column value - python

I would like to style row based on a value of a column, I currently have this code:
import dash
import dash_table
import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/solar.csv')
app = dash.Dash(__name__)
print([{"name": i, "id": i} for i in df.columns])
app.layout = dash_table.DataTable(
id='table',
columns=[{"name": i, "id": i} for i in df.columns],
data=df.to_dict("rows"),
style_data_conditional=[
{
'if': {
'column_id': 'Number of Solar Plants',
'filter': '"Number of Solar Plants" > num(100)'
},
'backgroundColor': '#3D9970',
'color': 'white',
}
],
)
if __name__ == '__main__':
app.run_server(debug=True)
Which produces the following result:
But what I really want is for the rows (tr 1 and 8 in this case) to be styled with a green background, not just the cells.
What can I do to achieve this?

to fix your issue you just have to remove the column_id parameter in your style_data_conditional. So all the row will be colored in green.
You should do this:
style_data_conditional=[
{
'if': {
'filter': '"Number of Solar Plants" > num(100)'
},
'backgroundColor': '#3D9970',
'color': 'white',
}
]

Related

Turn ON/OFF AutoUpdate base on boolean switch (Dash, Python)

How do I please enable/disable auto-update (n_intervals) based on a boolean switch?
When the switch is turned off so that auto-update is not performed. And when it is on, so that the table is updated every 6 seconds?
Below I am posting the code that works for me with autoupdate (n_intervals).
import pandas as pd
import dash
from dash import html, Input, Output, callback, dash_table, dcc
import database
database = database.database()
dash.register_page(__name__)
layout = html.Div(
[
html.Div(id="table-container", children=[]),
dcc.Interval(id='interval-component', interval=6000, n_intervals=0),
]
)
#callback(Output('table-container', 'children'), [Input('interval-component', 'n_intervals')])
def update_table(n_intervals):
sql = pd.read_sql_query("SELECT * FROM PY_LOGGING ORDER BY LOG_TIME DESC FETCH FIRST 500 ROWS ONLY", database.connection())
df = pd.DataFrame(sql)
table = dash_table.DataTable(
id='table',
data = df.to_dict('records'),
columns = [{"name": i, "id": i} for i in df.columns],
fill_width=True,
page_size=20,
filter_action='native',
sort_action='native',
sort_mode='multi',
sort_as_null=['', 'No'],
style_table = {
"overflow": "hidden",
},
style_header = {
"text-transform": "uppercase",
"backgroundColor": "#333",
"color": "#FFFFFF",
"padding": "15px",
},
style_cell_conditional=[
{
'if': {'column_id': 'text_message'},
'textAlign': 'left'
}
],
style_data={
'whiteSpace': 'normal',
'height': 'auto',
'border-top': "1px solid #333",
'border-bottom': "1px solid #333",
'border-left': "0px solid #333",
'border-right': "0px solid #333",
},
style_data_conditional = [
{"if": {"column_id": "text_message"}, "width": "50%"},
{"if": {"column_id": "log_time"}, "width": "10%"},
{"if": {"column_id": "name_process"}, "width": "10%"},
{"if": {"column_id": "name_machine"}, "width": "10%"},
],
style_cell = {
# "padding": "15px",
"font-size": "0.8em",
}
)
return table
Thank you very much for your help.
John

Filtering a column on Dash dataTable based on a list

I am very new to Dash. I have made a dataTable that includes several columns. These columns can be filtered and sorted. However, one problem with the filtering is that I cannot filter based on a list (like pandas .loc) e.g. if I want to filter the countries based on a list (say, ['India', 'United States']), the filter does not work. I have previously checked the advanced filtering here and found that I can use || operators; however,this would not be a good choice if the list is more than 4 or 5.
Here's the code:
import dash
from dash.dependencies import Input, Output
import dash_table
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import json
df = pd.read_csv(
'https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv')
app = dash.Dash(__name__)
app.layout = html.Div([
html.Div(id='heading-users', children='Users\' Country details', style={
'textAlign': 'center', 'font-family': 'Helvetica'}),
dash_table.DataTable( # users
id='datatable-users',
columns=[
{"name": i, "id": i, "deletable": True, "selectable": True} for i in df.columns
],
data=df.to_dict('records'),
editable=True,
filter_action="native",
sort_action="native",
sort_mode="multi",
column_selectable="single",
row_selectable="multi",
row_deletable=True,
selected_columns=[],
selected_rows=[],
page_action="native",
page_current=0,
page_size=20,
export_format='csv'
),
html.Div(id='datatable-users-container')
])
#app.callback(
Output('datatable-users-container', "children"),
Input('datatable-users', "derived_virtual_data"),
Input('datatable-users', "derived_virtual_selected_rows"))
def update_graphs(rows, derived_virtual_selected_rows):
if derived_virtual_selected_rows is None:
derived_virtual_selected_rows = []
dff = df if rows is None else pd.DataFrame(rows)
colors = ['#7FDBFF' if i in derived_virtual_selected_rows else '#0074D9'
for i in range(len(dff))]
return [
dcc.Graph(
id=column,
figure={
"data": [
{
"x": dff["country"],
"y": dff[column],
"type": "bar",
"marker": {"color": colors},
}
],
"layout": {
"xaxis": {"automargin": True},
"yaxis": {
"automargin": True,
"title": {"text": column}
},
"height": 250,
"margin": {"t": 10, "l": 10, "r": 10},
},
},
)
# check if column exists - user may have deleted it
# If `column.deletable=False`, then you don't
# need to do this check.
for column in ["pop", "lifeExp", "gdpPercap"] if column in dff
]
if __name__ == '__main__':
app.run_server(debug=True)
From that link - The 'native' filter function doesn't support 'OR' operations within a single column. Assign filter_action="custom" then create a callback to update the table children. See the 'Back-End Filtering section of that link.
You'll need to grab the filter query string in a callback and decompose to extract the column name and query string. With that you can query the pandas dataframe and return the results in a callback. I don't have the code for "OR" functionality but found some I used that can query pandas once you have the input values
def filter_df(df, filter_column, value_list):
conditions = []
for val in value_list:
conditions.append(f'{filter_column} == "{val}"')
query = ' or '.join(conditions)
print(f'querying with: {query}')
return df.query(query_expr)
filter_df(df, 'country', ['Albania', 'Algeria'])

How to add hyperlink in column field of dash Datatable

I wanted to add Hyperlink to the first column CR-number, so it should be clickable, on clicking it will redirect to the link.
below is the code snippet of it.
html.Div(
className = "row",
children = [
dash_table.DataTable(
id='datatable-cr-list',
columns=[
{"name": i, "id": i} for i in cr_columns_to_display
],
filter_action = "native",
sort_action = "native",
style_cell_conditional=[
{'if': {'column_id': 'Title'},
'textAlign': 'left',
'overflow': 'hidden',
'textOverflow': 'ellipsis',
'width': '60%',
'whiteSpace': 'normal'
}
],
style_data_conditional=[
{
'if': {'row_index': 'odd'},
'backgroundColor': 'rgb(230, 242, 255)'
}
],
style_header={
'backgroundColor': 'rgb(153, 204, 255)',
'fontWeight': 'bold'
},
export_format="csv",
)
]
),
here is the image of the table, where I wanted to add a hyperlink to the first column. Do we have any feature to add a hyperlink to the dash?
I fix with adding hyperlink to first column with below method and add "presentation": 'markdown' in the Layout for Table field
def f(row):
return "[{0}](<link of redirection>{0})".format(row["CR_Number"])
if open_cr_count > 0:
df_fa_direct_open["CR_Number"] = df_fa_direct_open.apply(f, axis=1)

Insert shape in Dash DataTable

I have the below datatable in dash. Code for generating this table is below:
import dash
import dash_table
import pandas as pd
data = {'value':['very low','low','medium','high','very high'],'data':[1,3,5,7,10]}
df = pd.DataFrame(data)
app = dash.Dash(__name__)
app.layout = dash_table.DataTable(
id='table',
columns=[{"name": i, "id": i} for i in df.columns],
data=df.to_dict('records'),
)
if __name__ == '__main__':
app.run_server(debug=True)
But I want to generate the below table. How can i do this using dash? The indicator column is a shape that is color coded based on a scale (For eg. 5 is medium and hence yellow/amber, above 5 the value go from green to dark green. Similarly, values below 5 go from amber to red
Thanks to Eduardo on the plotly community forum I learned that we can now use html content for Markdown cells of a DataTable (plotly forum thread, github pull request).
This allows us to do something like this:
import dash
import dash_html_components as html
import dash_table
import pandas as pd
def get_svg_arrow(val):
if val > 7:
fill = "green"
elif val > 5:
fill = "blue"
elif val == 5:
fill = "yellow"
elif val >= 3:
fill = "orange"
else:
fill = "red"
if val >= 5:
path = f'<path d="M19.5 11L26 11L13 -1.1365e-06L7.97623e-09 11L6.5 11L6.5 22L19.5 22L19.5 11Z" fill="{fill}"/>'
else:
path = (
f'<path d="M6.5 11H0L13 22L26 11L19.5 11V0L6.5 0L6.5 11Z" fill="{fill}"/>'
)
return f'<svg width="26" height="22" viewBox="0 0 26 22" fill="none" xmlns="http://www.w3.org/2000/svg">{path}</svg>'
values = [1, 3, 5, 7, 10]
data = {
"value": ["very low", "low", "medium", "high", "very high"],
"indicator": [get_svg_arrow(val) for val in values],
"data": values,
}
df = pd.DataFrame(data)
app = dash.Dash(__name__)
app.layout = html.Div(
[
dash_table.DataTable(
css=[dict(selector="p", rule="margin: 0px; text-align: center")],
data=df.to_dict("records"),
style_cell={"textAlign": "center"},
columns=[
{"name": col, "id": col, "presentation": "markdown"}
if col == "indicator"
else {"name": col, "id": col}
for col in df.columns
],
markdown_options={"html": True},
)
]
)
if __name__ == "__main__":
app.run_server()
So the idea of the code above is to dynamically create an arrow svg with a fill color based on the data.
For creating the svg arrows I've used a vector drawing program and exported to svg, but you could construct the path manually if you wanted to.
Result

Dash dataTable conditional cell formatting isn't working

I want to change a color of a cell of a Dash dataTable based on a value. I have tried a minimal example:
html.Div(
children=[
dash_table.DataTable(
id='table_1',
data=df.to_dict('records'),
columns=[{"name": i, "id": i} for i in df.columns],
#conditional cell formating
style_data_conditional=[
{
'if': {
'column_id': 'col1',
'filter': 'col1 > num(15)'
},
'backgroundColor': '#3D9970',
'color': 'white',
},
],
n_fixed_rows=2,
filtering=True,
sorting=True,
sorting_type="multi"
)],
)
After adding the style_conditional, the table is not showing at all, and there is no error message thrown. The table is embedded in html component, and after looking into forums and on github, I am stiil not sure if I have missed anything here, and if I need to write a callback for this. The example provided in the mentioned tutorial is not suggesting the need for a callback.
Update:
Tried to run minimal version with the same code and different data, with same results, that is no change in cell colors. My libraries are up to date, but something in the environment may still be causing problems.
Full code:
import dash
import dash_table
import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/solar.csv')
# Having trouble with conditional when column name has spaces
df = df.rename({'Number of Solar Plants': 'Plants'}, axis='columns')
app = dash.Dash(__name__)
app.layout = dash_table.DataTable(
id='table',
columns=[{"name": i, "id": i} for i in df.columns],
style_data_conditional=[{
"if": {'column_id': 'State',
'filter': 'State eq "Nevada"'
},
"backgroundColor": "#3D9970",
"color": "white"
}],
data=df.to_dict('records'),
)
if __name__ == '__main__':
app.run_server(debug=True)
I do not think you will need a callback for this like said in the tutorial. According to the last example of the tutorial I think you have a typo (one ' to much).
Change this line
'filter': 'col1' > num(15)'
to:
'filter': 'col1 > num(15)'
I had the same issue and i found that giving the index directly rather than a condition was a lot easier.
style_data_conditional = [{'if': {'column_id': 'col1',"row_index": x},'backgroundColor': '#3D9970','color': 'white'} for x in df[df['col1']>15].index ]
it's ugly as it has been hard coded but it did the trick for me when the direct filter did not.
I don't know how it was back in 2019, but with recent releases of Dash Datatables:
Column names in filter expressions must be wrapped in curly braces
filter has been renamed to filter_query
style_data_conditional=[{
"if": {
'column_id': 'State',
'filter_query': '{State} eq "Nevada"'
# ^ ^ <-- required braces
},
"backgroundColor": "#3D9970",
"color": "white"
}]
BTW: You can conveniently highlight the entire row by removing the column_id line.

Categories