Hide keyboard shortcuts

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 

9 

10 

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. 

17 

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 

28 

29 .. plot:: 

30 

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 

36 

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()) 

41 

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 

50 

51 def coor(ti): 

52 days = ti.days 

53 x = days 

54 y = ti.seconds 

55 return x, y 

56 

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. 

63 

64 if ax is None: 

65 ax = plt.gca() 

66 

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 

98 

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) 

103 

104 ax.add_patch(rect) 

105 

106 # days border 

107 xticks = [] 

108 if daynames is None: 

109 daynames = list(calendar.day_name) 

110 

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]) 

122 

123 # invert y axis 

124 ax.invert_yaxis() 

125 

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) 

137 

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) 

157 

158 ax.tick_params(axis='x', rotation=30) 

159 

160 # value2 

161 if value2 is not None: 

162 value = value2.copy() 

163 if normalise: 

164 value = value / max_value 

165 

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 

180 

181 x2 = x1 + value[i] * h 

182 

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 = [], [] 

190 

191 xs.append(x2) 

192 ys.append((y1 + y2) / 2) 

193 

194 if len(xs) > 0: 

195 ax.plot(xs, ys, color='orange', linewidth=2) 

196 

197 return ax