Skip to content

Commit efa37d2

Browse files
committed
update examples + provide solutions
1 parent 49134ce commit efa37d2

File tree

4 files changed

+167
-8
lines changed

4 files changed

+167
-8
lines changed

01-callable_answer.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from collections import deque
2+
import matplotlib as mpl
3+
import matplotlib.pyplot as plt
4+
import numpy as np
5+
plt.ion()
6+
7+
# disable the built in key bindings
8+
for k in ['keymap.all_axes',
9+
'keymap.back',
10+
'keymap.forward',
11+
'keymap.fullscreen',
12+
'keymap.grid',
13+
'keymap.home',
14+
'keymap.pan',
15+
'keymap.save',
16+
'keymap.xscale',
17+
'keymap.yscale',
18+
'keymap.zoom']:
19+
mpl.rcParams[k] = []
20+
21+
22+
fig, ax = plt.subplots()
23+
24+
th = np.linspace(0, 2*np.pi, 64)
25+
ln, = ax.plot(th, np.sin(th), 'o-', picker=5)
26+
27+
28+
class FormatterCollector:
29+
def __init__(self, maxlen=12):
30+
self.event_deque = deque([], maxlen=maxlen)
31+
32+
def __call__(self, event):
33+
print('called {} at ({}, {})'.format(event.name,
34+
event.xdata,
35+
event.ydata))
36+
self.event_deque.append(event)
37+
38+
def collect_string(self):
39+
return ''.join([ev.key for ev in self.event_deque
40+
if ev.name == 'key_press_event'])
41+
42+
fc = FormatterCollector()
43+
cid = fig.canvas.mpl_connect('key_press_event', fc)
44+
cid2 = fig.canvas.mpl_connect('key_release_event', fc)
45+
cid2 = fig.canvas.mpl_connect('button_press_event', fc)

02-event_filter_answer.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import matplotlib.pyplot as plt
2+
from itertools import cycle
3+
import numpy as np
4+
plt.ion()
5+
6+
7+
class LineMaker:
8+
def __init__(self, ln):
9+
# stash the current data
10+
self.xdata = list(ln.get_xdata())
11+
self.ydata = list(ln.get_ydata())
12+
# stash the Line2D artist
13+
self.ln = ln
14+
self.color_cyle = cycle(['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728',
15+
'#9467bd', '#8c564b', '#e377c2', '#7f7f7f',
16+
'#bcbd22', '#17becf'])
17+
self.button_cid = ln.figure.canvas.mpl_connect('button_press_event',
18+
self.on_button)
19+
self.key_cid = ln.figure.canvas.mpl_connect('key_press_event',
20+
self.on_key)
21+
22+
def on_button(self, event):
23+
# only consider events from the lines Axes
24+
if event.inaxes is not self.ln.axes:
25+
return
26+
27+
# if not the left mouse button or a modifier key
28+
# is held down, bail
29+
if event.button != 1 or event.key not in (None, 'shift'):
30+
print('key+button: {!r}+{!r}'.format(event.key, event.button))
31+
return
32+
33+
if event.key == 'shift':
34+
# compute the distance to each point *in data space*
35+
d = np.hypot(np.asarray(self.xdata) - event.xdata,
36+
np.asarray(self.ydata) - event.ydata)
37+
# find the closest point
38+
ix = np.argmin(d)
39+
# remove that data point
40+
del self.xdata[ix]
41+
del self.ydata[ix]
42+
else:
43+
# get the event location in data-space
44+
# and add to internal data list
45+
self.xdata.append(event.xdata)
46+
self.ydata.append(event.ydata)
47+
# update the line
48+
self._update_line()
49+
50+
def _update_line(self):
51+
# update the artist data
52+
self.ln.set_data(self.xdata, self.ydata)
53+
# ask the GUI to re-draw the next time it can
54+
self.ln.figure.canvas.draw_idle()
55+
56+
def on_key(self, event):
57+
# This is _super_ useful for debugging!
58+
# print(event.key)
59+
60+
# if the escape key is hit, clear the data
61+
if event.key == 'escape':
62+
# clear the internal data structures
63+
self.xdata.clear()
64+
self.ydata.clear()
65+
# update the internal line
66+
self._update_line()
67+
68+
# if the key is c (any case)
69+
if event.key.lower() == 'c':
70+
# change the color
71+
self.ln.set_color(next(self.color_cyle))
72+
73+
# ask the GUI to re-draw the next time it can
74+
self.ln.figure.canvas.draw_idle()
75+
76+
fig, ax = plt.subplots()
77+
ln, = ax.plot([], [], '-o')
78+
line_maker = LineMaker(ln)

04-custom_plotting.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
from pddc_helpers import load_bwi_data, aggregate_by_month
33
plt.ion()
44

5+
bwi = load_bwi_data()
6+
bwi_monthly = aggregate_by_month(bwi)
7+
8+
fig, ax = plt.subplots()
9+
510

611
def plot_aggregated_errorbar(ax, gb, label, picker=None, **kwargs):
712
kwargs.setdefault('capsize', 3)
@@ -19,12 +24,34 @@ def plot_aggregated_errorbar(ax, gb, label, picker=None, **kwargs):
1924
ax.figure.canvas.draw_idle()
2025
return eb, fill
2126

22-
bwi = load_bwi_data()
23-
bwi_monthly = aggregate_by_month(bwi)
27+
arts = plot_aggregated_errorbar(ax, bwi_monthly, 'bwi')
2428

25-
fig, ax = plt.subplots()
26-
ax.set_xlabel('Date [UTC]')
27-
ax.set_ylabel('Air Temperature [℃]')
28-
ax.set_title('BWI')
29+
# EXERCISE
30+
# - make the shaded area configurable
31+
# - make center line configurable
2932

30-
arts = plot_aggregated_errorbar(ax, bwi_monthly, 'bwi')
33+
34+
def plot_aggregated_errorbar(ax, gb, label, picker=None, *,
35+
bands=None,
36+
center_line='mean',
37+
**kwargs):
38+
if bands is None:
39+
bands = ['min', 'max']
40+
kwargs.setdefault('capsize', 3)
41+
kwargs.setdefault('markersize', 5)
42+
kwargs.setdefault('marker', 'o')
43+
eb = ax.errorbar(gb.index, center_line,
44+
yerr='std',
45+
data=gb,
46+
label=label,
47+
picker=picker,
48+
**kwargs)
49+
fill = ax.fill_between(gb.index, *bands, alpha=.5,
50+
data=gb, color=eb[0].get_color())
51+
ax.legend()
52+
ax.figure.canvas.draw_idle()
53+
return eb, fill
54+
55+
arts = plot_aggregated_errorbar(ax, bwi_monthly, 'bwi',
56+
bands=['25%', '75%'],
57+
center_line='50%')

05-interactive_temperature.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import matplotlib.pyplot as plt
2-
import matplotlib as mpl
32
from cycler import cycler
43
from pddc_helpers import (load_bwi_data, aggregate_by_month, aggregate_by_day,
4+
plot_aggregated_errorbar,
55
extract_day_of_hourly, extract_month_of_daily)
66

77

@@ -179,20 +179,29 @@ def _monthly_on_pick(self, event):
179179
self._plot_T_by_hour(sel_date.year, sel_date.month, sel_date.day)
180180

181181
def _plot_T_by_hour(self, year, month, day):
182+
# get the hourly data for a single day
182183
df = extract_day_of_hourly(self.data_by_hour, year, month, day)
184+
# format the label
183185
label = '{:s}: {:04d}-{:02d}-{:02d}'.format(
184186
self.label, year, month, day)
187+
# A 'simple' plot
185188
self.daily_ax.plot('T', '-', picker=10, label=label, data=df,
186189
**next(self.style_cycle))
190+
# update the legend
187191
self.daily_ax.legend()
192+
# ask the GUI to redraw the next time it can
188193
self.daily_ax.figure.canvas.draw_idle()
189194

190195
def _daily_on_pick(self, event):
191196
if event.mouseevent.inaxes is not self.daily_ax:
192197
return
198+
# grab the canvas
193199
canvas = event.artist.figure.canvas
200+
# remove the artist
194201
event.artist.remove()
202+
# update the legend
195203
self.daily_ax.legend()
204+
# redraw the canvas next time it is convenient
196205
canvas.draw_idle()
197206

198207
def remove(self):

0 commit comments

Comments
 (0)