5

我需要用 matplotlib 画一张穿孔卡片,它似乎没有这样的功能。所以我编写了以下代码:

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()

但是,它并不完全按预期工作。如果根据以下示例对其进行评估:

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()

我们得到以下结果:生成的拳头

您会注意到圆圈之间有很多空白的垂直空间,而我希望两个最大尺寸的圆圈接触在一起(水平方向是这种情况,但不是垂直方向)。

那么,如何去除这个空间,同时保持圆比呢?

非常感谢你的帮助。

4

1 回答 1

8

我根据数据的形状比(r = float(data.shape[1])/data.shape[0])更改了圆圈的垂直间距。此外,画布大小会更改以适应结果,这样您就不会在默认画布大小中留下大的白色区域。

结果:

具有适当圆形比例的圆形图

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)
于 2013-02-13T10:08:51.027 回答