Plot a calendar in python using matplotlib. The days are drawn on a 7*52 heatmap grid, with the months clearly delineated.
Slightly cleaned up from https://gist.github.com/rsnape/98bbf68e333b0948192e found in this Stack Overflow answer
Plot a calendar in python using matplotlib. The days are drawn on a 7*52 heatmap grid, with the months clearly delineated.
Slightly cleaned up from https://gist.github.com/rsnape/98bbf68e333b0948192e found in this Stack Overflow answer
| import numpy as np | |
| import brewer2mpl | |
| import matplotlib.colorbar as cbar | |
| from matplotlib import rcParams | |
| import palettable | |
| from dateutil import rrule | |
| import matplotlib | |
| import calendar | |
| from random import random | |
| def set_matplotlib_params(): | |
| """ | |
| Set matplotlib defaults to nicer values | |
| """ | |
| rcParams['mathtext.default'] ='regular' | |
| #rcParams['axes.labelsize'] = 11 | |
| #rcParams['xtick.labelsize'] = 11 | |
| #rcParams['ytick.labelsize'] = 11 | |
| #rcParams['legend.fontsize'] = 11 | |
| rcParams['font.family'] = 'sans-serif' | |
| rcParams['font.serif'] = ['Helvetica'] | |
| #rcParams['figure.figsize'] = 16, 40 ## depends on number of years | |
| def get_colors(): | |
| """ | |
| Get palettable colors, which are nicer | |
| """ | |
| bmap = palettable.colorbrewer.sequential.BuPu_9.mpl_colors | |
| return bmap | |
| def draw_calendar(ax, df, horizontal, column_name): | |
| ''' | |
| Draws one calendar year | |
| ''' | |
| max_val = max(df[column_name]) | |
| min_val = min(df[column_name]) | |
| color_list = get_colors() | |
| color_len = len(color_list) | |
| for i in range(len(df.index)): | |
| if(df[column_name][i] > 0): | |
| cur_wk = dt.date(df.year[i], df.month[i], df.day[i]).isocalendar()[1] | |
| cur_yr = dt.date(df.year[i], df.month[i], df.day[i]).isocalendar()[0] | |
| if((cur_yr < df.year[i]) and (df.day[i]<7)): | |
| cur_wk = 0 | |
| if(cur_yr>df.year[i]): | |
| cur_wk = 53 | |
| day_of_week = df.index[i].weekday() | |
| #normalise each data point to val - note added a very small amount | |
| #to data range, so that we never get exactly 1.0 | |
| val = float((df[column_name][i]-min_val)/float(max_val-min_val + 0.000001)) | |
| if horizontal: | |
| rect = matplotlib.patches.Rectangle((cur_wk,day_of_week), 1, 1, color = color_list[int(val*color_len)]) | |
| else: | |
| rect = matplotlib.patches.Rectangle((day_of_week,cur_wk), 1, 1, color = color_list[int(val*color_len)],label='a') | |
| ax.add_patch(rect) | |
| return | |
| def draw_day_boundary(ax, num_weeks): | |
| ''' | |
| Draws calendar grid, seperating the days and weeks in a month | |
| ''' | |
| line_color = 'white' | |
| line_width = 0.5 | |
| line_style = '-' | |
| ## lines between weeks | |
| for i in range(num_weeks): | |
| ax.plot([0, 7], [i, i], | |
| color=line_color, linestyle=line_style, lw=line_width) | |
| ## lines between days | |
| for j in range(7): | |
| ax.plot([j, j], [0, num_weeks], | |
| color=line_color, linestyle=line_style, lw=line_width) | |
| return | |
| def draw_month_boundary(ax, df, horizontal): | |
| line_color = 'black' | |
| line_width = 1.25 | |
| line_style = '-' | |
| month_seq = rrule.rrule(rrule.MONTHLY, dtstart=df.index[0], until=df.index[-1]) | |
| for mon in month_seq: | |
| num_days = calendar.monthrange(mon.year,mon.month)[1] | |
| current_week = dt.date(mon.year, mon.month, num_days).isocalendar()[1] | |
| current_year = dt.date(mon.year, mon.month, num_days).isocalendar()[0] | |
| day_of_week = dt.date(mon.year, mon.month, num_days).weekday() | |
| if current_year == mon.year and mon.month <> 12: | |
| if horizontal: | |
| ax.plot([current_week+1, current_week+1], [0, day_of_week+1], | |
| color=line_color, linestyle=line_style, lw=line_width) | |
| else: | |
| ax.plot([0, day_of_week+1], [current_week+1, current_week+1], | |
| color=line_color, linestyle=line_style, lw=line_width) | |
| if day_of_week != 6: | |
| if horizontal: | |
| ax.plot([current_week+1, current_week], [day_of_week+1, day_of_week+1], | |
| color=line_color, linestyle=line_style, lw=line_width) # Parallel to X-Axis | |
| ax.plot([current_week, current_week], [day_of_week+1, 7], | |
| color=line_color, linestyle=line_style, lw=line_width) | |
| else: | |
| ax.plot([day_of_week+1, day_of_week+1], [current_week+1, current_week], | |
| color=line_color, linestyle=line_style, lw=line_width) # Parallel to Y-axis | |
| ax.plot([day_of_week+1, 7], [current_week, current_week], | |
| color=line_color, linestyle=line_style, lw=line_width) | |
| return | |
| def draw_month_labels(horizontal): | |
| ''' | |
| ''' | |
| pos_from_edge = 3 | |
| for idx in range(1,13): | |
| month_name = dt.date(1900, idx, 1).strftime('%b') | |
| if horizontal: | |
| plt.text(pos_from_edge, 8, month_name, fontsize=14) | |
| else: | |
| plt.text(8, pos_from_edge, month_name, fontsize=14) | |
| pos_from_edge += int(random()+4.2) ## bias for 4 | |
| return | |
| def plot_calendars(data, column_name, orientation='v'): | |
| ''' | |
| data: already processed to have year, month and day columns, plus a numeric column, | |
| with data that will be shown along a colour axis | |
| ''' | |
| df = data[['year','month','day', column_name]] | |
| ## global parameters | |
| horizontal = True if orientation == 'h' else False | |
| num_yrs = len(df['year'].unique()) | |
| max_val = max(df[column_name]) | |
| min_val = min(df[column_name]) | |
| figscale = 0.4 | |
| longueur = num_yrs*52*figscale | |
| largeur = num_yrs*7*figscale | |
| figsize = (longueur, largeur) if horizontal else (largeur, longueur) | |
| # Draw blank figure | |
| fig = plt.figure(figsize=figsize) | |
| set_matplotlib_params() | |
| plt.subplots_adjust(hspace=0.3) | |
| plt.axis('off') | |
| plt.axes().set_aspect('equal') | |
| for idx, year in enumerate(df['year'].unique()): | |
| sub_df = df[df['year'] == year] | |
| if horizontal: | |
| ax = plt.subplot2grid((num_yrs, 2), (idx, 1), rowspan=1, colspan=2) | |
| else: | |
| ax = plt.subplot2grid((2, num_yrs), (1, idx), rowspan=2, colspan=1) | |
| ax.xaxis.tick_top() | |
| ax.axes.get_xaxis().set_ticks([]) | |
| ax.axes.get_yaxis().set_ticks([]) | |
| ax.axis('off') | |
| ax.set_title(str(year), fontsize=18) | |
| period = np.timedelta64(sub_df.index[-1] - sub_df.index[0]) | |
| num_weeks = int(np.ceil( period/(np.timedelta64(1,'W')) )+2) | |
| if horizontal: | |
| plt.xlim(0, num_weeks) | |
| plt.ylim(0, 7) | |
| else: | |
| plt.xlim(0, 7) | |
| plt.ylim(num_weeks, 0) | |
| draw_calendar(ax, sub_df, horizontal, column_name) | |
| draw_day_boundary(ax, num_weeks) | |
| draw_month_boundary(ax, sub_df, horizontal) | |
| if year == min(df['year'].unique()) and horizontal: | |
| draw_month_labels(horizontal) | |
| elif year == max(df['year'].unique()) and not horizontal: | |
| draw_month_labels(horizontal) | |
| def draw_legend(): | |
| # plot an overall colorbar type legend | |
| ax_colorbar = plt.subplot2grid((4, num_yrs), (3,0), rowspan=1, colspan=num_yrs) | |
| mappableObject = matplotlib.cm.ScalarMappable(cmap = palettable.colorbrewer.sequential.BuPu_9.mpl_colormap) | |
| mappableObject.set_array(np.array(df[column_name])) | |
| col_bar = fig.colorbar(mappableObject, cax=ax_colorbar, orientation='horizontal', | |
| boundaries = np.arange(min_val, max_val, (max_val-min_val)/10)) | |
| # You can change the boundaries kwarg to either make the scale look less boxy (increase 10) | |
| # or to get different values on the tick marks, or even omit it altogether to let | |
| col_bar.set_label(column_name) | |
| ax_colorbar.set_title(column_name + ' color mapping') | |
| def draw_bar_plot(): | |
| # draw the top overall graph | |
| ax0 = plt.subplot2grid((4, num_yrs), (0,0), rowspan=1, colspan=num_yrs) | |
| x_axis = np.arange(0.325, num_yrs+1,1.1) | |
| bar_val = df[column_name].groupby(df['year']).mean() | |
| err_val = df[column_name].groupby(df['year']).std() | |
| ax0.bar(x_axis, bar_val, yerr=err_val, | |
| linewidth=0, width=0.25, color='g', | |
| error_kw=dict(ecolor='gray', lw=1)) | |
| ax0.axes.get_xaxis().set_ticks([]) | |
| ax0.spines['top'].set_visible(False) | |
| ax0.spines['right'].set_visible(False) | |
| plt.ylabel(column_name) | |
| ax0.axes.get_yaxis().set_ticks([]) | |
| return | |
| plt.tight_layout() | |
| plt.show() | |
| return | |
| plot_calendars(calendar_df, 'size', 'h') | |
| def plot_calendar_year(data, column_name, orientation='h'): | |
| horizontal = True if orientation == 'h' else False | |
| figscale = 0.4 | |
| longueur = 52*figscale | |
| largeur = 7*figscale | |
| figsize = (longueur, largeur) if horizontal else (largeur, longueur) | |
| fig = plt.figure(figsize=figsize) | |
| ax = fig.add_subplot(111) | |
| plt.axis('off') | |
| if horizontal: | |
| plt.xlim(0, 52) | |
| plt.ylim(0, 7) | |
| else: | |
| plt.xlim(0, 7) | |
| plt.ylim(52, 0) | |
| draw_calendar(ax, data, horizontal, column_name) | |
| draw_day_boundary(ax, 52) | |
| draw_month_boundary(ax, data, horizontal) | |
| plt.show() | |
| return | |
| #plot_calendar_year(calendar_df[calendar_df.year == 2015], 'size', 'h') |