I need to draw a punchcard with matplotlib which seem to not have such a function.So I have coded the following one:
import matplotlib.pyplot as plt
import numpy as np
def draw_punchcard(infos,
ax1=range(7),
ax2=range(24),
ax1_ticks=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
ax2_ticks=range(24),
ax1_label='Day',
ax2_label='Hour'):
"""Construct a punchcard.
Quick'n dirty way.
Parameters
==========
- infos: Dictionary of quantities to display.
They are indexed by key of type (val1,val2) with
val1 included in ax1 and val2 included in ax2.
- ax1: list
Possible values for first axe (if different than days)
- ax2: list
Possible values for second axe (if different than hours)
- ax1_ticks: list
Value to display in ticks of first axe (if different than days)
- ax2_ticks: list
Value to display in ticks of second axe (if different than days)
- ax1_label: String
Value to give to first axis (if different than day)
- ax2_label: String
Value to give to second axis (if different than day)
"""
# build the array which contains the values
data = np.zeros((len(ax1),len(ax2)))
for key in infos:
data[key[0],key[1]] = infos[key]
data = data/float(np.max(data))
# Draw the punchcard (create one circle per element)
# Ugly normalisation allows to obtain perfect circles instead of ovals....
for y in range(data.shape[0]):
for x in range(data.shape[1]):
circle = plt.Circle((x/float(data.shape[1])*data.shape[0],y),
data[y][x]/float(data.shape[1])*data.shape[0]/2)
plt.gca().add_artist(circle)
plt.ylim(0-0.5, data.shape[0]-0.5)
plt.xlim(0, data.shape[0])
plt.yticks(np.arange(len(ax1)), ax1_ticks)
plt.xticks(np.linspace(0,len(ax1),len(ax2))+0.5/float(data.shape[1]), ax2_ticks)
plt.xlabel(ax1_label)
plt.ylabel(ax2_label)
plt.gca().invert_yaxis()
However it does not exactly work as expected. If it is evaluated on the following example:
infos = {(6, 9): 12196, (0, 20): 22490, (1, 17): 59636, (0, 7): 14915, (2, 22): 7193, (1, 6): 11694, (0, 10): 85793, (3, 7): 17507, (2, 5): 4078, (1, 11): 83424, (5, 8): 33625, (4, 0): 1915, (6, 7): 10528, (5, 5): 3525, (4, 19): 33253, (6, 10): 12186, (5, 18): 20856, (0, 17): 61370, (0, 4): 551, (1, 1): 389, (4, 10): 94684, (3, 2): 286, (2, 6): 11845, (5, 11): 46822, (4, 5): 5215, (3, 23): 1841, (6, 0): 3441, (4, 16): 94545, (6, 23): 1285, (5, 21): 11096, (2, 17): 59928, (0, 1): 279, (3, 12): 56193, (1, 12): 59846, (4, 15): 102986, (3, 1): 371, (2, 11): 78007, (5, 14): 27711, (3, 18): 41365, (6, 13): 11994, (4, 21): 14477, (6, 16): 11669, (1, 21): 13629, (2, 18): 42399, (0, 14): 66284, (3, 11): 76402, (2, 1): 358, (1, 15): 93381, (4, 12): 67279, (2, 12): 57427, (5, 1): 509, (3, 17): 58974, (6, 14): 11383, (0, 21): 12604, (1, 16): 86199, (2, 23): 1914, (1, 5): 4002, (0, 11): 79164, (3, 6): 11434, (2, 2): 304, (1, 10): 88874, (4, 1): 420, (6, 4): 750, (5, 4): 783, (6, 11): 12886, (5, 17): 21573, (0, 18): 41842, (1, 19): 33073, (0, 5): 2777, (1, 0): 1189, (0, 8): 46486, (4, 11): 89246, (3, 5): 4105, (2, 7): 18534, (5, 10): 54826, (4, 6): 14638, (3, 22): 5043, (6, 1): 894, (5, 7): 16052, (4, 17): 66899, (6, 20): 16085, (5, 20): 18041, (0, 2): 219, (3, 15): 81526, (1, 3): 251, (4, 8): 58008, (3, 0): 1581, (2, 8): 47233, (5, 13): 23896, (3, 21): 13998, (6, 2): 540, (4, 22): 5920, (6, 17): 13856, (5, 23): 2155, (1, 20): 24386, (2, 19): 33216, (0, 15): 86664, (3, 10): 81444, (1, 14): 74440, (4, 13): 62307, (2, 13): 51784, (5, 0): 1959, (3, 16): 76742, (6, 15): 11438, (0, 22): 4055, (6, 18): 17554, (1, 23): 1681, (2, 20): 26427, (1, 4): 710, (0, 12): 59008, (3, 9): 72555, (2, 3): 372, (1, 9): 79140, (4, 2): 322, (2, 14): 68869, (6, 5): 3091, (5, 3): 392, (6, 8): 11720, (5, 16): 28663, (0, 19): 30223, (1, 18): 41624, (0, 6): 8791, (1, 7): 18280, (0, 9): 75860, (3, 4): 765, (2, 4): 834, (5, 9): 52874, (4, 7): 21830, (6, 6): 7618, (5, 6): 9935, (4, 18): 43274, (6, 21): 9836, (5, 19): 20758, (0, 16): 81458, (0, 3): 245, (3, 14): 66845, (1, 2): 291, (4, 9): 86355, (3, 3): 346, (2, 9): 71401, (5, 12): 27939, (4, 4): 987, (3, 20): 24478, (6, 3): 450, (4, 23): 2236, (6, 22): 3779, (5, 22): 4950, (2, 16): 79009, (0, 0): 1655, (3, 13): 53589, (1, 13): 55308, (4, 14): 81394, (2, 10): 80932, (5, 15): 32751, (3, 19): 32193, (6, 12): 12770, (4, 20): 24379, (0, 23): 1240, (6, 19): 18908, (1, 22): 4887, (2, 21): 16508, (0, 13): 54858, (3, 8): 47367, (2, 0): 1778, (1, 8): 50393, (4, 3): 387, (2, 15): 86256, (5, 2): 385}
draw_punchcard(infos)
plt.show()
we obtain the following result:
You can notice there are a lot of empty vertical space between the circles, whereas I would expect two circles of maximum size to touch together (it would be the case horizontally, but not vertically).
So, how to remove this space, while keeping the circle ratio?
Thanks a lot for your help.
MatPlotLib with Python Load an image from a file. Convert the image from one color space to another. To remove grid lines, use ax. grid(False).
Using subplots_adjust() method to set the spacing between subplots. We can use the plt. subplots_adjust() method to change the space between Matplotlib subplots. The parameters wspace and hspace specify the space reserved between Matplotlib subplots.
I changed the vertical spacing of the circles based on the shape ratio of the data (r = float(data.shape[1])/data.shape[0]
).
Also, the canvas size is changed to fit the result, so that you're not left with a large white area in the default canvas size.
Result:
def draw_punchcard(infos,
ax1=range(7),
ax2=range(24),
ax1_ticks=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
ax2_ticks=range(24),
ax1_label='Day',
ax2_label='Hour'):
# build the array which contains the values
data = np.zeros((len(ax1),len(ax2)))
for key in infos:
data[key[0],key[1]] = infos[key]
data = data/float(np.max(data))
# shape ratio
r = float(data.shape[1])/data.shape[0]
# Draw the punchcard (create one circle per element)
# Ugly normalisation allows to obtain perfect circles instead of ovals....
for y in range(data.shape[0]):
for x in range(data.shape[1]):
circle = plt.Circle((x/float(data.shape[1]-1)*(data.shape[0]-1),y/r),
data[y][x]/float(data.shape[1])*data.shape[0]/2)
plt.gca().add_artist(circle)
plt.ylim(0-0.5, data.shape[0]-0.5)
plt.xlim(0, data.shape[0])
plt.yticks(np.arange(0,len(ax1)/r-.1,1/r), ax1_ticks)
xt = np.linspace(0, len(ax1)-1, len(ax2))
plt.xticks(xt, ax2_ticks)
plt.xlabel(ax1_label)
plt.ylabel(ax2_label)
plt.gca().invert_yaxis()
# make sure the axes are equal, and resize the canvas to fit the plot
plt.axis('equal')
plt.axis([0, 7, 6/r, -.5])
scale = 0.5
plt.gcf().set_size_inches(data.shape[1]*scale,data.shape[0]*scale, forward=True)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With