|
28 | 28 | data = json.loads(urllib.request.urlopen(url, timeout=1).read().decode())
|
29 | 29 |
|
30 | 30 | dates = []
|
31 |
| - names = [] |
| 31 | + releases = [] |
32 | 32 | for item in data:
|
33 | 33 | if 'rc' not in item['tag_name'] and 'b' not in item['tag_name']:
|
34 | 34 | dates.append(item['published_at'].split("T")[0])
|
35 |
| - names.append(item['tag_name']) |
36 |
| - # Convert date strings (e.g. 2014-10-18) to datetime |
37 |
| - dates = [datetime.strptime(d, "%Y-%m-%d") for d in dates] |
| 35 | + releases.append(item['tag_name'].lstrip("v")) |
38 | 36 |
|
39 | 37 | except Exception:
|
40 | 38 | # In case the above fails, e.g. because of missing internet connection
|
41 | 39 | # use the following lists as fallback.
|
42 |
| - names = ['v2.2.4', 'v3.0.3', 'v3.0.2', 'v3.0.1', 'v3.0.0', 'v2.2.3', |
43 |
| - 'v2.2.2', 'v2.2.1', 'v2.2.0', 'v2.1.2', 'v2.1.1', 'v2.1.0', |
44 |
| - 'v2.0.2', 'v2.0.1', 'v2.0.0', 'v1.5.3', 'v1.5.2', 'v1.5.1', |
45 |
| - 'v1.5.0', 'v1.4.3', 'v1.4.2', 'v1.4.1', 'v1.4.0'] |
46 |
| - |
| 40 | + releases = ['2.2.4', '3.0.3', '3.0.2', '3.0.1', '3.0.0', '2.2.3', |
| 41 | + '2.2.2', '2.2.1', '2.2.0', '2.1.2', '2.1.1', '2.1.0', |
| 42 | + '2.0.2', '2.0.1', '2.0.0', '1.5.3', '1.5.2', '1.5.1', |
| 43 | + '1.5.0', '1.4.3', '1.4.2', '1.4.1', '1.4.0'] |
47 | 44 | dates = ['2019-02-26', '2019-02-26', '2018-11-10', '2018-11-10',
|
48 | 45 | '2018-09-18', '2018-08-10', '2018-03-17', '2018-03-16',
|
49 | 46 | '2018-03-06', '2018-01-18', '2017-12-10', '2017-10-07',
|
50 | 47 | '2017-05-10', '2017-05-02', '2017-01-17', '2016-09-09',
|
51 | 48 | '2016-07-03', '2016-01-10', '2015-10-29', '2015-02-16',
|
52 | 49 | '2014-10-26', '2014-10-18', '2014-08-26']
|
53 | 50 |
|
54 |
| - # Convert date strings (e.g. 2014-10-18) to datetime |
55 |
| - dates = [datetime.strptime(d, "%Y-%m-%d") for d in dates] |
| 51 | +dates = [datetime.strptime(d, "%Y-%m-%d") for d in dates] # Convert strs to dates. |
| 52 | +dates, releases = zip(*sorted(zip(dates, releases))) # Sort by increasing date. |
56 | 53 |
|
57 | 54 | # %%
|
58 | 55 | # Next, we'll create a stem plot with some variation in levels as to
|
|
64 | 61 | #
|
65 | 62 | # Note that Matplotlib will automatically plot datetime inputs.
|
66 | 63 |
|
67 |
| - |
68 |
| -# Choose some nice levels |
69 |
| -levels = np.tile([-5, 5, -3, 3, -1, 1], |
70 |
| - int(np.ceil(len(dates)/6)))[:len(dates)] |
71 |
| - |
72 |
| -# Create figure and plot a stem plot with the date |
| 64 | +# Choose some nice levels: alternate minor releases between top and bottom, and |
| 65 | +# progressievly shorten the stems for bugfix releases. |
| 66 | +levels = [] |
| 67 | +major_minor_releases = sorted({release[:3] for release in releases}) |
| 68 | +for release in releases: |
| 69 | + major_minor = release[:3] |
| 70 | + bugfix = int(release[4]) |
| 71 | + h = 1 + 0.8 * (5 - bugfix) |
| 72 | + level = h if major_minor_releases.index(major_minor) % 2 == 0 else -h |
| 73 | + levels.append(level) |
| 74 | + |
| 75 | +# The figure and the axes. |
73 | 76 | fig, ax = plt.subplots(figsize=(8.8, 4), layout="constrained")
|
74 | 77 | ax.set(title="Matplotlib release dates")
|
75 | 78 |
|
76 |
| -ax.vlines(dates, 0, levels, color="tab:red") # The vertical stems. |
77 |
| -ax.plot(dates, np.zeros_like(dates), "-o", |
78 |
| - color="k", markerfacecolor="w") # Baseline and markers on it. |
79 |
| - |
80 |
| -# annotate lines |
81 |
| -for d, l, r in zip(dates, levels, names): |
82 |
| - ax.annotate(r, xy=(d, l), |
83 |
| - xytext=(-3, np.sign(l)*3), textcoords="offset points", |
84 |
| - horizontalalignment="right", |
85 |
| - verticalalignment="bottom" if l > 0 else "top") |
86 |
| - |
87 |
| -# format x-axis with 4-month intervals |
88 |
| -ax.xaxis.set_major_locator(mdates.MonthLocator(interval=4)) |
89 |
| -ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %Y")) |
90 |
| -plt.setp(ax.get_xticklabels(), rotation=30, ha="right") |
91 |
| - |
92 |
| -# remove y-axis and spines |
| 79 | +# The vertical stems. |
| 80 | +ax.vlines(dates, 0, levels, |
| 81 | + color=[("tab:red", 1 if release.endswith(".0") else .5) |
| 82 | + for release in releases]) |
| 83 | +# The baseline. |
| 84 | +ax.axhline(0, c="black") |
| 85 | +# The markers on the baseline. |
| 86 | +minor_dates = [date for date, release in zip(dates, releases) if release[-1] == '0'] |
| 87 | +bugfix_dates = [date for date, release in zip(dates, releases) if release[-1] != '0'] |
| 88 | +ax.plot(bugfix_dates, np.zeros_like(bugfix_dates), "ko", mfc="white") |
| 89 | +ax.plot(minor_dates, np.zeros_like(minor_dates), "ko", mfc="tab:red") |
| 90 | + |
| 91 | +# Annotate the lines. |
| 92 | +for date, level, release in zip(dates, levels, releases): |
| 93 | + ax.annotate(release, xy=(date, level), |
| 94 | + xytext=(-3, np.sign(level)*3), textcoords="offset points", |
| 95 | + verticalalignment="bottom" if level > 0 else "top", |
| 96 | + weight="bold" if release.endswith(".0") else "normal", |
| 97 | + bbox=dict(boxstyle='square', pad=0, lw=0, fc=(1, 1, 1, 0.7))) |
| 98 | + |
| 99 | +ax.yaxis.set(major_locator=mdates.YearLocator(), |
| 100 | + major_formatter=mdates.DateFormatter("%Y")) |
| 101 | + |
| 102 | +# Remove the y-axis and some spines. |
93 | 103 | ax.yaxis.set_visible(False)
|
94 | 104 | ax.spines[["left", "top", "right"]].set_visible(False)
|
95 | 105 |
|
|
0 commit comments