Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2@file
3@brief Timeseries plots.
4"""
5import calendar
6import datetime
7import matplotlib.pyplot as plt
8import matplotlib.patches as patches # pylint: disable=R0402
11def plot_week_timeseries(time, value, normalise=True,
12 label=None, h=0.85, value2=None,
13 label2=None, daynames=None,
14 xfmt="%1.0f", ax=None):
15 """
16 Shows a timeseries dispatched by days as bars.
18 @param time dates
19 @param value values to display as bars.
20 @param normalise normalise data before showing it
21 @param label label of the series
22 @param values2 second series to show as a line
23 @param label2 label of the second series
24 @param daynames names to use for week day names (default is English)
25 @param xfmt format number of the X axis
26 @param ax existing axis
27 @return axis
29 .. plot::
31 import datetime
32 import matplotlib.pyplot as plt
33 from mlinsights.timeseries.datasets import artificial_data
34 from mlinsights.timeseries.agg import aggregate_timeseries
35 from mlinsights.timeseries.plotting import plot_week_timeseries
37 dt1 = datetime.datetime(2019, 8, 1)
38 dt2 = datetime.datetime(2019, 9, 1)
39 data = artificial_data(dt1, dt2, minutes=15)
40 print(data.head())
42 agg = aggregate_timeseries(data, per='week')
43 plot_week_timeseries(
44 agg['weektime'], agg['y'], label="y",
45 value2=agg['y']/2, label2="y/2", normalise=False)
46 plt.show()
47 """
48 if time.shape[0] != value.shape[0]:
49 raise AssertionError("Dimension mismatch") # pragma: no cover
51 def coor(ti):
52 days = ti.days
53 x = days
54 y = ti.seconds
55 return x, y
57 max_value = value.max()
58 if value2 is not None:
59 max_value = max(max_value, value2.max())
60 value2 = value2 / max_value
61 value = value / max_value
62 input_maxy = 1.
64 if ax is None:
65 ax = plt.gca()
67 # bars
68 delta = None
69 maxx, maxy = None, None
70 first = True
71 for i in range(time.shape[0]):
72 ti = time[i]
73 if i < time.shape[0] - 1:
74 ti1 = time[i + 1]
75 delta = (ti1 - ti) if delta is None else min(delta, ti1 - ti)
76 if delta == 0:
77 raise RuntimeError( # pragma: no cover
78 "The timeseries contains duplicated time values.")
79 else:
80 ti1 = ti + delta
81 x1, y1 = coor(ti)
82 x2, y2 = coor(ti1)
83 if y2 < y1:
84 x2, y2 = coor(ti + delta)
85 y2 = y1 + (y2 - y1) * h
86 if first and label:
87 ax.plot([x1, x1 + value[i] * 0.8], [y1, y1],
88 'b', alpha=0.5, label=label)
89 first = False
90 if maxx is None:
91 maxx = (x1, x1 + input_maxy)
92 maxy = (y1, y2)
93 else:
94 maxx = (min(x1, maxx[0]), # pylint: disable=E1136
95 max(x1 + input_maxy, maxx[1])) # pylint: disable=E1136
96 maxy = (min(y1, maxy[0]), # pylint: disable=E1136
97 max(y2, maxy[1])) # pylint: disable=E1136
99 rect = patches.Rectangle((x1, y1), value[i] * h, y2 - y1,
100 linewidth=1, edgecolor=None,
101 facecolor='b', fill=True,
102 alpha=0.5)
104 ax.add_patch(rect)
106 # days border
107 xticks = []
108 if daynames is None:
109 daynames = list(calendar.day_name)
111 maxx = [(maxx[0] // 7) * 7, maxx[1]]
112 new_ymin = maxy[0] - (maxy[1] * 0.025 + maxy[0] * 0.975 - maxy[0])
113 for i in range(int(maxx[0]), int(maxx[1] + 0.1)):
114 x1i = maxx[0] + input_maxy * i
115 x2i = x1i + input_maxy
116 xticks.append(x1i)
117 ax.plot([x1i, x1i + input_maxy], [new_ymin, new_ymin], 'k', alpha=0.5)
118 ax.plot([x1i, x1i + input_maxy], [maxy[1], maxy[1]], 'k', alpha=0.5)
119 ax.plot([x1i, x1i], [maxy[0], maxy[1]], 'k', alpha=0.5)
120 ax.plot([x2i, x2i], [maxy[0], maxy[1]], 'k', alpha=0.5)
121 ax.text(x1i, new_ymin, daynames[i])
123 # invert y axis
124 ax.invert_yaxis()
126 # change y labels
127 nby = len(ax.get_yticklabels())
128 ys = ax.get_yticks()
129 ylabels = []
130 for i in range(nby):
131 dh = ys[i]
132 dt = datetime.timedelta(seconds=dh)
133 tx = "%dh%02d" % (dt.seconds // 3600,
134 60 * (dt.seconds / 3600 - dt.seconds // 3600))
135 ylabels.append(tx)
136 ax.set_yticklabels(ylabels)
138 # change x labels
139 xs = ax.get_xticks()
140 xticks = []
141 xlabels = []
142 for i in range(0, len(xs) - 1):
143 if xs[i] < 0:
144 continue
145 dx = xs[i] - int(xs[i] / input_maxy) * input_maxy
146 xlabels.append(dx if normalise else (dx * max_value))
147 xticks.append(xs[i])
148 dx = (xs[i] + xs[i + 1]) / 2
149 dx = dx - int(dx / input_maxy) * input_maxy
150 xlabels.append(dx if normalise else (dx * max_value))
151 xticks.append((xs[i] + xs[i + 1]) / 2)
152 if len(xticks) < len(xlabels):
153 xticks.append(xs[-1])
154 ax.set_xticks(xticks)
155 ax.set_xticklabels(
156 [xfmt % x for x in xlabels] if xfmt else xlabels)
158 ax.tick_params(axis='x', rotation=30)
160 # value2
161 if value2 is not None:
162 value = value2.copy()
163 if normalise:
164 value = value / max_value
166 first = True
167 xs = []
168 ys = []
169 for i in range(time.shape[0]):
170 ti = time[i]
171 if i < time.shape[0] - 1:
172 ti1 = time[i + 1]
173 else:
174 ti1 = ti + delta
175 x1, y1 = coor(ti)
176 x2, y2 = coor(ti1)
177 if y2 < y1:
178 x2, y2 = coor(ti + delta)
179 y2 = y1 + (y2 - y1) * h
181 x2 = x1 + value[i] * h
183 if len(ys) > 0 and y2 < ys[-1]:
184 if first and label2 is not None:
185 ax.plot(xs, ys, color='orange', linewidth=2, label=label2)
186 first = False
187 else:
188 ax.plot(xs, ys, color='orange', linewidth=2)
189 xs, ys = [], []
191 xs.append(x2)
192 ys.append((y1 + y2) / 2)
194 if len(xs) > 0:
195 ax.plot(xs, ys, color='orange', linewidth=2)
197 return ax