Sparklines as Fonts! Embedding Minimal Sparklines in Tables & Components

I’ve used these sparkline fonts in a project and think they are pretty cool. Here is a minimal example that demos adding sparklines to a DataTable and shows the different fonts available.

The function that creates the spark column also has notes on 2 ways to normalize the data between 0-100 depending on whether the data includes negative numbers.

See more info on how to serve the fonts locally here

And thanks to @Eduardo for input on this topic!

import dash
import dash_table
import pandas as pd
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from dash_table.Format import Format, Scheme, Group
from dash.exceptions import PreventUpdate

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv"
)
formatted = Format().scheme(Scheme.fixed).precision(0).group(Group.yes)
spark_options = [
    "Sparks-Bar-Narrow",
    "Sparks-Bar-Medium",
    "Sparks-Bar-Wide",
    "Sparks-Bar-Extrawide",
    "Sparks-Dot-Medium",
    "Sparks-Dot-Large",
    "Sparks-Dot-Extralarge",
    "Sparks-Dotline-Extrathin",
    "Sparks-Dotline-Thin",
    "Sparks-Dotline-Medium",
    "Sparks-Dotline-Thick",
    "Sparks-Dotline-Extrathick",
]


app.layout = html.Div(
    [
        html.H4("Sparkline Demo App"),
        html.Div("Fonts from https://github.com/aftertheflood/sparks"),
        html.Div(
            [
                dcc.Dropdown(
                    id="spark_style",
                    options=[{"label": i, "value": i} for i in spark_options],
                    value="Sparks-Bar-Extrawide",
                    placeholder="Select sparkline style",
                    style={"width": 300},
                ),
                dcc.RadioItems(
                    id="stat_radio",
                    options=[
                        {"label": i, "value": i}
                        for i in ["pop", "lifeExp", "gdpPercap"]
                    ],
                    value="gdpPercap",
                    labelStyle={"display": "inline-block"},
                ),
                dcc.RangeSlider(
                    id="year_slider",
                    marks={i: str(i) for i in df["year"].unique().tolist()},
                    min=1952,
                    max=2007,
                    allowCross=False,
                    value=[1987, 2007],
                ),
            ],
            style={"width": 600, "margin": 20},
        ),
        dash_table.DataTable(id="table", sort_action="native",),
    ]
)


def make_sparkline(df_wide):
    """

    :param df_wide: dataframe in "wide" format with the sparkline periods as columns
    :return: a series formatted for the sparkline fonts.
             Example:  '453{10,40,30,80}690'
    """

    # normalize between 0 and 100
    max = df_wide.max(axis=1)
    min = df_wide.min(axis=1)
    # Use this formula if the data has negative numbers:  (x-x.min)/ (x.max-x.min)*100
    df_spark = df_wide.sub(min, axis="index").div((max - min), axis="index").mul(100)
    #  if data is all positive numbers this may be better: (x)/ (x.max)*100
    #  df_spark = df_wide.div((max), axis="index").mul(100)

    # format the normalized numbers like: '25,20,50,80'
    df_spark["spark"] = df_spark.astype(int).astype(str).agg(",".join, axis=1)

    # get the starting and ending numbers
    df_spark["start"] = df_wide[df_wide.columns[0]].round(0).astype(int).astype(str)
    df_spark["end"] = df_wide[df_wide.columns[-1]].round(0).astype(int).astype(str)

    # put it all together
    return df_spark["start"] + "{" + df_spark["spark"] + "}" + df_spark["end"]


@app.callback(
    Output("table", "columns"),
    Output("table", "data"),
    Output("table", "style_data_conditional"),
    Input("year_slider", "value"),
    Input("stat_radio", "value"),
    Input("spark_style", "value"),
)
def update_table(year, stat, spark_style):
    if year[0] == year[1]:
        raise PreventUpdate

    dff = df[(df["year"] >= year[0]) & (df["year"] <= year[1])]
    dff = pd.pivot_table(dff, index=["country", "continent"], columns="year", values=stat)
    dff["sparkline"] = make_sparkline(dff)

    dff = dff.reset_index()
    columns = [
        {"name": str(i), "id": str(i), "format": formatted, "type": "numeric"}
        for i in dff.columns
    ]
    data = dff.to_dict("records")
    style = [{"if": {"column_id": "sparkline"}, "font-family": spark_style,}]

    return columns, data, style


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


7 Likes