From 9ad39b0678dee077fd694a6e4d5739e977eddc6a Mon Sep 17 00:00:00 2001 From: Chris Holdgraf Date: Fri, 8 Jun 2018 19:37:26 -0700 Subject: [PATCH 1/2] timeline example --- examples/lines_bars_and_markers/timeline.py | 68 +++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 examples/lines_bars_and_markers/timeline.py diff --git a/examples/lines_bars_and_markers/timeline.py b/examples/lines_bars_and_markers/timeline.py new file mode 100644 index 000000000000..008ef742a885 --- /dev/null +++ b/examples/lines_bars_and_markers/timeline.py @@ -0,0 +1,68 @@ +""" +=============================================== +Creating a timeline with lines, dates, and text +=============================================== + +How to create a simple timeline using Matplotlib release dates. + +Timelines can be created with a collection of dates and text. In this example, +we show how to create a simple timeline using the dates for recent releases +of Matplotlib. First, we'll pull the data from GitHub. +""" + +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +import urllib.request +import json + +# Grab a list of Matplotlib releases +url = 'https://api.github.com/repos/matplotlib/matplotlib/releases' +data = json.loads(urllib.request.urlopen(url).read().decode()) + +releases = [] +for irelease in data: + releases.append((irelease['tag_name'], irelease['published_at'])) +releases = pd.DataFrame(releases, columns=['name', 'date']) +releases['date'] = pd.to_datetime(releases['date']) +# Remove release candidates and betas +releases = releases.loc[['rc' not in nm for nm in releases['name']]] +releases = releases.loc[['b' not in nm for nm in releases['name']]] + +############################################################################## +# 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. +# +# Note that Matplotlib will automatically plot datetime inputs. + +levels = np.array([-5, 5, -3, 3, -1, 1]) +fig, ax = plt.subplots(figsize=(20, 5)) + +# Create the base line +start = releases['date'].min() +stop = releases['date'].max() +ax.plot((start, stop), (0, 0), 'k', alpha=.5) + +# Iterate through releases annotating each one +for ix, (iname, idate) in releases.iterrows(): + level = levels[ix % 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)) +ax.set(title="Matplotlib release dates") +# Set the xticks formatting +xticks = pd.date_range(start, stop, freq='3M') +ax.set_xticks(xticks) +ax.set_xticklabels(xticks.strftime("%b %Y"), + rotation=45, horizontalalignment='right', fontsize=14) +# Remove components for a cleaner look +plt.setp((ax.get_yticklabels() + ax.get_yticklines() + + list(ax.spines.values())), visible=False) +plt.show() From 4e60dfbcabdea700fc1f4c4c6884d88a53c0d8d5 Mon Sep 17 00:00:00 2001 From: Chris Holdgraf Date: Sat, 9 Jun 2018 13:39:44 -0700 Subject: [PATCH 2/2] removing pandas dependency --- examples/lines_bars_and_markers/timeline.py | 37 +++++++++++---------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/examples/lines_bars_and_markers/timeline.py b/examples/lines_bars_and_markers/timeline.py index 008ef742a885..72150a841a11 100644 --- a/examples/lines_bars_and_markers/timeline.py +++ b/examples/lines_bars_and_markers/timeline.py @@ -12,7 +12,8 @@ import matplotlib.pyplot as plt import numpy as np -import pandas as pd +import matplotlib.dates as mdates +from datetime import datetime import urllib.request import json @@ -20,14 +21,14 @@ url = 'https://api.github.com/repos/matplotlib/matplotlib/releases' data = json.loads(urllib.request.urlopen(url).read().decode()) -releases = [] +names = [] +dates = [] for irelease in data: - releases.append((irelease['tag_name'], irelease['published_at'])) -releases = pd.DataFrame(releases, columns=['name', 'date']) -releases['date'] = pd.to_datetime(releases['date']) -# Remove release candidates and betas -releases = releases.loc[['rc' not in nm for nm in releases['name']]] -releases = releases.loc[['b' not in nm for nm in releases['name']]] + if 'rc' not in irelease['tag_name'] and 'b' not in irelease['tag_name']: + names.append(irelease['tag_name']) + # Convert date strings (e.g. 2014-10-18T18:56:23Z) to datetime + dates.append(datetime.strptime(irelease['published_at'], + "%Y-%m-%dT%H:%M:%SZ")) ############################################################################## # Next, we'll iterate through each date and plot it on a horizontal line. @@ -39,29 +40,29 @@ fig, ax = plt.subplots(figsize=(20, 5)) # Create the base line -start = releases['date'].min() -stop = releases['date'].max() +start = min(dates) +stop = max(dates) ax.plot((start, stop), (0, 0), 'k', alpha=.5) # Iterate through releases annotating each one -for ix, (iname, idate) in releases.iterrows(): - level = levels[ix % 6] +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) + 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)) ax.set(title="Matplotlib release dates") # Set the xticks formatting -xticks = pd.date_range(start, stop, freq='3M') -ax.set_xticks(xticks) -ax.set_xticklabels(xticks.strftime("%b %Y"), - rotation=45, horizontalalignment='right', fontsize=14) +# format xaxis with 3 month intervals +ax.get_xaxis().set_major_locator(mdates.MonthLocator(interval=3)) +ax.get_xaxis().set_major_formatter(mdates.DateFormatter("%b %Y")) +fig.autofmt_xdate() + # Remove components for a cleaner look plt.setp((ax.get_yticklabels() + ax.get_yticklines() + list(ax.spines.values())), visible=False)