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)