Colored calendar heatmap in dash

Hi all,

I found a interesting github-like activity tracker chart, it is implemented using matplotlib.
https://pythonhosted.org/calmap

my question is: can i embed the calmap chart into my dash app?

Is your matplotlib chart technically just a picture? If so this might help:

1 Like

No. I would like to embed a real interactive chart. I guess we can create one using plotly heatmap but it is not trivial for a beginner like me :slightly_smiling_face:

I am currently working on using a plotly heatmap as a calendar to select/deselect days of production in some analysis I am doing for my company.
I will return when I have a rough idea about how to make a good calendar with plotly heatmap…
So far I have something like the following, but I have yet to test how well it works. :slightly_smiling_face:

year = datetime.datetime.now().year

d1 = datetime.date(year, 1, 1)
d2 = datetime.date(year, 12, 31)

delta = d2 - d1

dates_in_year = [d1 + datetime.timedelta(i) for i in range(delta.days+1)]
weekdays_in_year = [i.weekday() for i in dates_in_year]
weeknumber_of_dates = [int(i.strftime("%V")) for i in dates_in_year]
z = [0]*len(dates_in_year)
text = [str(i) for i in dates_in_year]

data = [
	go.Heatmap(
		x = weekdays_in_year,
		y = weeknumber_of_dates,
		z = z
	)
]
layout = go.Layout(
	title='test'
)

fig = go.Figure(data=data, layout=layout)

I have looked alot at the Github Commits per Day example in https://plot.ly/python/heatmaps/ to get where I am right now, if you need some inspiration.

2 Likes

Okay, so now I know I have something which works… I will add a few comments if you need 'em.
It creates a calendar with weeks along the y-axis and weekdays along the x-axis. When you hover a “cell” the hoverinfo is a string with the given date… :man_technologist::metal:
(Yes, I am too lazy to translate my danish strings into english…)

def holidays():
	year = datetime.datetime.now().year

	d1 = datetime.date(year, 1, 1)
	d2 = datetime.date(year, 12, 31)

	delta = d2 - d1

	dates_in_year = [d1 + datetime.timedelta(i) for i in range(delta.days+1)] #gives me a list with datetimes for each day a year
	weekdays_in_year = [i.weekday() for i in dates_in_year] #gives [0,1,2,3,4,5,6,0,1,2,3,4,5,6,...] (ticktext in xaxis dict translates this to weekdays
	weeknumber_of_dates = [int(i.strftime("%V")) for i in dates_in_year] #gives [1,1,1,1,1,1,1,2,2,2,2,2,2,2,...] name is self-explanatory
	z = np.random.uniform(low=0.0, high=1.0, size=(len(dates_in_year,))) #random numbers to give some mad colorz
	text = [str(i) for i in dates_in_year] #gives something like list of strings like '2018-01-25' for each date. Used in data trace to make good hovertext.

	data = [
		go.Heatmap(
			x = weekdays_in_year,
			y = weeknumber_of_dates,
			z = data_days,
			text=text,
			hoverinfo="text",
			xgap=3, # this
			ygap=3, # and this is used to make the grid-like apperance
			showscale=False
		)
	]
	layout = go.Layout(
		title='Ferielukket Kalender',
		height=1000,
		xaxis=dict(
			showline=True, #draw axis option (maybe I should remove this?)
			tickmode="array",
			ticktext=["Man", "Tirs", "Ons", "Tors", "Fre", "Lør", "Søn"],
			tickvals=[0,1,2,3,4,5,6],
			title="Ugedag"
		),
		yaxis=dict(
			showline=True,
			title="Uge Nr."
		),
		plot_bgcolor=('rgb(0,0,0)') #making grid appear black
	)

	fig = go.Figure(data=data, layout=layout)
	return fig
2 Likes

awesome! it is pretty close to what i need, i will give it a try. may be need to tune the grid a bit so that it become a square grid with white border line.

To tune the grid, I think you need to do something smart™ with ticks, I will pass on this one. :sweat_smile:
If you want a white border you just need to change the plot_bgcolor option in the go.Layout

plot_bgcolor=('rgb(0,0,0)') #making grid appear black
plot_bgcolor=('rgb(255,255,255)') #making grid appear white
1 Like

Looks good! Just to add some ideas to help refine the layout a little more. You can:

remove all plot/grid lines:

xaxis = dict(
   showline = False,
   showgrid = False,
   zeroline = False
)
yaxis = dict(
   showline = False,
   showgrid = False,
   zeroline = False
)

set the global font color:

font = dict(color = 'rgb(255, 255, 255)')

and you can use margins to make it skinny:

margin = dict(l=80,r=80,t=400,b=300)
1 Like

this is the changes i make it more github-chart -look-alike (there is still some flaw with the grid dimension which has dependency on the period of my chart.) i wish i could remove the ‘-’ from the axis label to make the chart neater, but i couldn’t find a way.

code:

import datetime
import plotly.graph_objs as go
import numpy as np
import dash_core_components as dcc
import dash_html_components as html
import dash

def holidays():
year = datetime.datetime.now().year

d1 = datetime.date(year, 8, 1)
d2 = datetime.date(year+1, 7, 15)

delta = d2 - d1

dates_in_year = [d1 + datetime.timedelta(i) for i in range(delta.days+1)] #gives me a list with datetimes for each day a year
weekdays_in_year = [i.weekday() for i in dates_in_year] #gives [0,1,2,3,4,5,6,0,1,2,3,4,5,6,…] (ticktext in xaxis dict translates this to weekdays
weeknumber_of_dates = [i.strftime("%Gww%V")[2:] for i in dates_in_year] #gives [1,1,1,1,1,1,1,2,2,2,2,2,2,2,…] name is self-explanatory
z = np.random.randint(2, size=(len(dates_in_year)))
text = [str(i) for i in dates_in_year] #gives something like list of strings like ‘2018-01-25’ for each date. Used in data trace to make good hovertext.
#4cc417 green #347c17 dark green
colorscale=[[False, ‘#eeeeee’], [True, ‘#76cf63’]]

data = [
go.Heatmap(
x = weeknumber_of_dates,
y = weekdays_in_year,
z = z,
text=text,
hoverinfo=“text”,
xgap=3, # this
ygap=3, # and this is used to make the grid-like apperance
showscale=False,
colorscale=colorscale
)
]
layout = go.Layout(
title=‘activity chart’,
height=280,
yaxis=dict(
showline = False, showgrid = False, zeroline = False,
tickmode=“array”,
ticktext=[“Mon”, “Tue”, “Wed”, “Thu”, “Fri”, “Sat”, “Sun”],
tickvals=[0,1,2,3,4,5,6],
),
xaxis=dict(
showline = False, showgrid = False, zeroline = False,
),
font={‘size’:‘10’, ‘color’:’#9e9e9e’},
plot_bgcolor=(’#fff’),
margin = dict(t=40),
)

fig = go.Figure(data=data, layout=layout)
return fig

app = dash.Dash()
app.layout = html.Div([
dcc.Graph(id=‘heatmap-test’, figure=holidays(), config={‘displayModeBar’: False})
])

outcome:

1 Like

Hey @zhsee

You can remove the ticks using ticks = ' ' in your xaxis and yaxis dict https://plot.ly/python/axes/#toggling-axes-lines-ticks-labels-and-autorange

1 Like

cool! now it become neater :slight_smile: i am run into another issue here, instead of two state my ‘z’ value now include another state (0, 1, 2) and i wish to use discrete color scale with green for 1 and dark green for 2 but it turns out giving me red and orange.

colorscale=[[0, ‘rgb(238, 238, 238)’], [1, ‘rgb(48, 137, 29)’], [2, ‘rgb(118, 207, 99)’]]

1 Like

Colorscale maps between 0 (lowest) and 1 (highest) https://plot.ly/python/reference/#heatmap-colorscale Thus, try something like https://plot.ly/python/colorscales/#custom-discretized-heatmap-colorscale

2 Likes

i love this community so much :blush:

2 Likes

Also see Auto generated Heat map Calendar