Skip to content

Doc: Update timeline example #13719

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ per-file-ignores =
examples/lines_bars_and_markers/span_regions.py: E402
examples/lines_bars_and_markers/stem_plot.py: E402
examples/lines_bars_and_markers/step_demo.py: E402
examples/lines_bars_and_markers/timeline.py: E402
examples/misc/agg_buffer.py: E402
examples/misc/anchored_artists.py: E501
examples/misc/contour_manual.py: E501
Expand Down
138 changes: 93 additions & 45 deletions examples/lines_bars_and_markers/timeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,57 +15,105 @@
import matplotlib.dates as mdates
from datetime import datetime

# A list of Matplotlib releases and their dates
# Taken from https://api.github.com/repos/matplotlib/matplotlib/releases
names = ['v2.2.2', 'v2.2.1', 'v2.2.0', 'v2.1.2', 'v2.1.1', 'v2.1.0', 'v2.0.2',
'v2.0.1', 'v2.0.0', 'v1.5.3', 'v1.5.2', 'v1.5.1', 'v1.5.0', 'v1.4.3',
'v1.4.2', 'v1.4.1', 'v1.4.0']

dates = ['2018-03-17T03:00:07Z', '2018-03-16T22:06:39Z',
'2018-03-06T12:53:32Z', '2018-01-18T04:56:47Z',
'2017-12-10T04:47:38Z', '2017-10-07T22:35:12Z',
'2017-05-10T02:11:15Z', '2017-05-02T01:59:49Z',
'2017-01-17T02:59:36Z', '2016-09-09T03:00:52Z',
'2016-07-03T15:52:01Z', '2016-01-10T22:38:50Z',
'2015-10-29T21:40:23Z', '2015-02-16T04:22:54Z',
'2014-10-26T03:24:13Z', '2014-10-18T18:56:23Z',
'2014-08-26T21:06:04Z']
dates = [datetime.strptime(ii, "%Y-%m-%dT%H:%M:%SZ") for ii in dates]
try:
# Try to fetch a list of Matplotlib releases and their dates
# from https://api.github.com/repos/matplotlib/matplotlib/releases
import urllib.request
import json

url = 'https://api.github.com/repos/matplotlib/matplotlib/releases'
url += '?per_page=100'
data = json.loads(urllib.request.urlopen(url, timeout=.4).read().decode())

dates = []
names = []
for item in data:
if 'rc' not in item['tag_name'] and 'b' not in item['tag_name']:
dates.append(item['published_at'].split("T")[0])
names.append(item['tag_name'])
# Convert date strings (e.g. 2014-10-18) to datetime
dates = [datetime.strptime(d, "%Y-%m-%d") for d in dates]

except Exception:
# In case the above fails, e.g. because of missing internet connection
# use the following lists as fallback.
names = ['v2.2.4', 'v3.0.3', 'v3.0.2', 'v3.0.1', 'v3.0.0', 'v2.2.3',
'v2.2.2', 'v2.2.1', 'v2.2.0', 'v2.1.2', 'v2.1.1', 'v2.1.0',
'v2.0.2', 'v2.0.1', 'v2.0.0', 'v1.5.3', 'v1.5.2', 'v1.5.1',
'v1.5.0', 'v1.4.3', 'v1.4.2', 'v1.4.1', 'v1.4.0']

dates = ['2019-02-26', '2019-02-26', '2018-11-10', '2018-11-10',
'2018-09-18', '2018-08-10', '2018-03-17', '2018-03-16',
'2018-03-06', '2018-01-18', '2017-12-10', '2017-10-07',
'2017-05-10', '2017-05-02', '2017-01-17', '2016-09-09',
'2016-07-03', '2016-01-10', '2015-10-29', '2015-02-16',
'2014-10-26', '2014-10-18', '2014-08-26']

# Convert date strings (e.g. 2014-10-18) to datetime
dates = [datetime.strptime(d, "%Y-%m-%d") for d in dates]

##############################################################################
# Next, we'll iterate through each date and plot it on a horizontal line.
# We'll add some styling to the text so that overlaps aren't as strong.
# Next, we'll create a `~.Axes.stem` plot with some variation in levels as to
# distinguish even close-by events. In contrast to a usual stem plot, we will
# shift the markers to the baseline for visual emphasis on the one-dimensional
# nature of the time line.
# For each event, we add a text label via `~.Axes.annotate`, which is offset
# in units of points from the tip of the event line.
#
# Note that Matplotlib will automatically plot datetime inputs.

levels = np.array([-5, 5, -3, 3, -1, 1])
fig, ax = plt.subplots(figsize=(8, 5))

# Create the base line
start = min(dates)
stop = max(dates)
ax.plot((start, stop), (0, 0), 'k', alpha=.5)

# Iterate through releases annotating each one
for ii, (iname, idate) in enumerate(zip(names, dates)):
level = levels[ii % 6]
vert = 'top' if level < 0 else 'bottom'

ax.scatter(idate, 0, s=100, facecolor='w', edgecolor='k', zorder=9999)
# Plot a line up to the text
ax.plot((idate, idate), (0, level), c='r', alpha=.7)
# Give the text a faint background and align it properly
ax.text(idate, level, iname,
horizontalalignment='right', verticalalignment=vert, fontsize=14,
backgroundcolor=(1., 1., 1., .3))

# Choose some nice levels
levels = np.tile([-5, 5, -3, 3, -1, 1],
int(np.ceil(len(dates)/6)))[:len(dates)]

# Create figure and plot a stem plot with the date
fig, ax = plt.subplots(figsize=(8.8, 4), constrained_layout=True)
ax.set(title="Matplotlib release dates")
# Set the xticks formatting
# format xaxis with 3 month intervals
ax.get_xaxis().set_major_locator(mdates.MonthLocator(interval=3))

markerline, stemline, baseline = ax.stem(dates, levels,
linefmt="C3-", basefmt="k-",
use_line_collection=True)

plt.setp(markerline, mec="k", mfc="w", zorder=3)

# Shift the markers to the baseline by replacing the y-data by zeros.
markerline.set_ydata(np.zeros(len(dates)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a hack on the original purpose of stem. Not sure I like it, but OTOH, the loop before wasn't good either. At least, this needs explanation.


# annotate lines
vert = np.array(['top', 'bottom'])[(levels > 0).astype(int)]
for d, l, r, va in zip(dates, levels, names, vert):
ax.annotate(r, xy=(d, l), xytext=(-3, np.sign(l)*3),
textcoords="offset points", va=va, ha="right")

# format xaxis with 4 month intervals
ax.get_xaxis().set_major_locator(mdates.MonthLocator(interval=4))
ax.get_xaxis().set_major_formatter(mdates.DateFormatter("%b %Y"))
fig.autofmt_xdate()
plt.setp(ax.get_xticklabels(), rotation=30, ha="right")

# remove y axis and spines
ax.get_yaxis().set_visible(False)
for spine in ["left", "top", "right"]:
ax.spines[spine].set_visible(False)

# Remove components for a cleaner look
plt.setp((ax.get_yticklabels() + ax.get_yticklines() +
list(ax.spines.values())), visible=False)
ax.margins(y=0.1)
plt.show()


#############################################################################
#
# ------------
#
# References
# """"""""""
#
# The use of the following functions, methods and classes is shown
# in this example:

import matplotlib
matplotlib.axes.Axes.stem
matplotlib.axes.Axes.annotate
matplotlib.axis.Axis.set_major_locator
matplotlib.axis.Axis.set_major_formatter
matplotlib.dates.MonthLocator
matplotlib.dates.DateFormatter