Coloured text in a table - conditional formatting

Good day guys
I was experimenting with from termcolor import colored. The whole objective was to return a value in a certain color and be able to print it in a table. But then the unfortunate thing is I was returning the code of the color. I had created a table from a dataset from a csv file, the whole intention is for the table to highlight the changes in experimental data, with colour changes tracking the evolution of the values. I had set up a if function to iterate throught he dataset and colouring the output then appending it to a row, and this output would be in colour. For example
def colour(num):
`if float(num)> 0:’
‘return colored( num ,“red”)’
‘if float(num)<0:’
'return colored(num,“green”)'
ut what I returned was ‘[31m18.0[0m’ for the red value. Is there a way to add colour to a table entry. Had taken a look at https://stackoverflow.com/questions/38511373/change-the-color-of-text-within-a-pandas-dataframe-html-table-python-using-style/38511805 with the hope of using it to create a dataframe that can be printed as a table but then it returned an error. Is there a way to do such a task

1 Like

If you want to change the color of elements within the interface of your Dash app, then you need to use CSS styles, either inline, on the element in particular, or by attaching a style sheet to your app and giving your element you want targeted a className that you have a rule for in the style sheet.

The inline option is much simpler. You can do something like:

html.Div("Some text", style={'color': 'red'})

There are plenty of tutorials on how to use CSS styles to color various types of your HTML.

ok, interesting. The disadvantage is I am more of learning python as I go. Also CSS am like a new to it. So am thinking now where specifically I will put the style since the data is coming from a dataframe and befoe being output on the table it needs colour and will need to add a function to detect the ones that take different colours.

If you’re constructing an updated layout fragment based on new data in a callback, then just add styles to the new layout being returned in the callback.

What I did was that the table is generated from the dataframe, what updates is the dataframe, from there the table then plots the dataframe i.e consider the default table layout we have under Dash Tutorial - Part 1: App Layout, all i did was create a function that updates the dataframe, then the rest will update automatically via a callback.

Is this the Dash DataTable component you’re using for a table or are you making your own out of html.Tr and html Td etc?

1 Like

Thats the component am using

@Dee - termcolor probably isn’t going to work here - the format of terminal colors (e.g. 31m18.0[0m) is different than for those of HTML color.

This is the best way to do it. I recommend using backgroundColor too. Here is an example that is adapted from the HTML table explained in plot.ly/dash/getting-started

import dash
import dash_core_components as dcc
import dash_html_components as html

import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.randn(50, 4), columns=list('ABCD'))

COLORS = [
    {
        'background': '#fef0d9',
        'text': 'rgb(30, 30, 30)'
    },
    {
        'background': '#fdcc8a',
        'text': 'rgb(30, 30, 30)'
    },
    {
        'background': '#fc8d59',
        'text': 'rgb(30, 30, 30)'
    },
    {
        'background': '#d7301f',
        'text': 'white'
    },
]


def is_numeric(value):
    try:
        float(value)
        return True
    except ValueError:
        return False


def cell_style(value, min_value, max_value):
    style = {}
    if is_numeric(value):
        relative_value = (value - min_value) / (max_value - min_value)
        if relative_value <= 0.25:
            style = {
                'backgroundColor': COLORS[0]['background'],
                'color': COLORS[0]['text']
            }
        elif relative_value <= 0.5:
            style = {
                'backgroundColor': COLORS[1]['background'],
                'color': COLORS[1]['text']
            }
        elif relative_value <= 0.75:
            style = {
                'backgroundColor': COLORS[2]['background'],
                'color': COLORS[2]['text']
            }
        elif relative_value <= 1:
            style = {
                'backgroundColor': COLORS[3]['background'],
                'color': COLORS[3]['text']
            }
    return style


def generate_table(dataframe, max_rows=100):
    max_value = df.max(numeric_only=True).max()
    min_value = df.min(numeric_only=True).max()
    rows = []
    for i in range(min(len(dataframe), max_rows)):
        row = []
        for col in dataframe.columns:
            value = dataframe.iloc[i][col]
            style = cell_style(value, min_value, max_value)
            row.append(html.Td(value, style=style))
        rows.append(html.Tr(row))

    return html.Table(
        # Header
        [html.Tr([html.Th(col) for col in dataframe.columns])] +

        # Body
        rows)


app = dash.Dash()
app.css.append_css({
    "external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"
})

app.layout = html.Div(children=[
    generate_table(df)
])

if __name__ == '__main__':
    app.run_server(debug=True)

2 Likes

awesome stuff. Thanks.

Note though that this approach works for html components but not necessarily for other Dash components. So you won’t be able to style your DataTable in this way. It looks like this component doesn’t support any kind of style customisation at the moment. However the final table includes a number of descriptive classes such as react-grid-Row and react-grid-Cell, which you could target within your CSS stylesheet.

The solution I found here was to identify the names of the columns I want to have colored texts, I then embedded an if loop into the generate table function specified by @chriddyp above, it was something like
def generate_table(dataframe, max_rows=100):
max_value = df.max(numeric_only=True).max()
min_value = df.min(numeric_only=True).max()
rows = []
for i in range(min(len(dataframe), max_rows)):
row = []
for col in dataframe.columns:
value = dataframe.iloc[i][col]
if col ==A:
style = cell_style(value, min_value, max_value)
row.append(html.Td(value, style=style))
else:
style = cell_style(value, min_value, max_value)
row.append(html.Td(value, style=style))
rows.append(html.Tr(row))

    return html.Table(
        # Header
        [html.Tr([html.Th(col) for col in dataframe.columns])] +

        # Body
        rows)

One will need to develop a custom function to generate the style they want for each cell, whether they are targeting the text or the backgound its all up to them.
There was also an issue on up arrows and s=down arrows in a dataframe that I had opened but cant locate it, its solved using the same manner where one can store a black up or down Unicode arrow as an element of the dataframe and using the function there can target them using the colours they want to appear

Awesome!As a data analyst i am a fan of dash It’s so friendly to person who not a coder. Haha

1 Like

@nedned @chriddyp

This might be little off topic, I have a requirement, where in a text document, I need to highlight few key texts. These texts are selected from dropdowns. I am not able to comprehend how I will build such a html layout.

Any idea how should I go about it?

I really liked this approach using html.table to color each cell. However, I also like the easy sort/filter function in dt.DataTable. Is there an easy way to add sort and filter in html.table?

This info about tables in this thread is a bit dated. The DataTable supports conditional formatting as well as sorting / filtering.

Thanks for the information. I like the flexibility in html.table that enables you to assign color to each cell, which I don’t think the current DataTable supports. I am looking into package visdcc, which is a cool way to combine both coloring each cell and sorting/filtering together.