Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/plotext/_monitor.py: 25%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

627 statements  

1from plotext._default import default_monitor_class 

2from plotext._matrix import matrix_class 

3from plotext._build import build_class 

4import plotext._utility as ut 

5from copy import deepcopy 

6import math 

7 

8# This file defines the monitor class, i.e. the plot, where actual data is plotted; the plot is build separately in the build class for clarity; here only the main tools and drawing methods are written 

9 

10class monitor_class(build_class): 

11 

12 def __init__(self): 

13 self.default = default_monitor_class() # default values 

14 self.labels_init() 

15 self.axes_init() 

16 self.color_init() 

17 self.data_init() 

18 self.matrix = matrix_class() 

19 

20 def copy(self): # to deep copy  

21 return deepcopy(self) 

22 

23 def set_size(self, size): # called externally by the figure containing it, to pass the size 

24 self.size = size 

25 

26 def set_date(self, date): # called externally by the figure containing it, to pass the date tools so that they share the same settings 

27 self.date = date 

28 

29############################################## 

30######### Internal Variables ########### 

31############################################## 

32 

33 def labels_init(self): 

34 self.title = None 

35 self.xlabel = [None, None] 

36 self.ylabel = [None, None] 

37 

38 def axes_init(self): 

39 self.xscale = [self.default.xscale[0]] * 2 # the scale on x axis 

40 self.yscale = [self.default.xscale[0]] * 2 

41 

42 self.xticks = self.default.xticks # xticks coordinates for both axes 

43 self.xlabels = self.default.xticks[:] # xlabels for both axes 

44 self.xfrequency = self.default.xfrequency # lower and upper xaxes ticks frequency 

45 self.xdirection = self.default.xdirection 

46 

47 self.yticks = self.default.yticks 

48 self.ylabels = self.default.yticks[:] 

49 self.yfrequency = self.default.yfrequency # left and right yaxes ticks frequency 

50 self.ydirection = self.default.ydirection 

51 

52 self.xaxes = self.default.xaxes # whatever to show the lower and upper x axis 

53 self.yaxes = self.default.yaxes # whatever to show the left and right y axis 

54 

55 self.grid = self.default.grid # whatever to show the horizontal and vertical grid lines 

56 

57 def color_init(self): 

58 self.set_theme('default') 

59 

60 def data_init(self): 

61 self.xlim = [[None, None], [None, None]] # the x axis plot limits for lower and upper xside 

62 self.ylim = [[None, None], [None, None]] # the y axis plot limits for left and right yside 

63 

64 self.fast_plot = False 

65 self.lines_init() 

66 self.text_init() 

67 self.draw_init() 

68 

69 def lines_init(self): 

70 self.vcoord = [[], []] # those are user defined extra grid lines, vertical or horizontal, for each axis 

71 self.hcoord = [[], []] 

72 self.vcolors = [[], []] # their color 

73 self.hcolors = [[], []] 

74 

75 def text_init(self): 

76 self.text = [] 

77 self.tx = [] 

78 self.ty = [] 

79 self.txside = [] 

80 self.tyside = [] 

81 self.torien = [] 

82 self.talign = [] 

83 self.tfull = [] 

84 self.tback = [] 

85 self.tstyle = [] 

86 

87 def draw_init(self): # Variables Set with Draw internal Arguments 

88 self.xside = [] # which side the x axis should go, for each plot (lower or upper) 

89 self.yside = [] # which side the y axis should go, for each plot (left or right) 

90 

91 self.x = [] # list of x coordinates  

92 self.y = [] # list of y coordinates 

93 self.x_date = [False, False] # True if x axis is for date time plots 

94 self.y_date = [False, False] 

95 self.signals = 0 # number of signals to plot 

96 

97 self.lines = [] # whatever to draw lines between points 

98 

99 self.marker = [] # list of markers used for each plot 

100 self.color = [] # list of marker colors used for each plot 

101 self.past_colors = [] 

102 self.style = [] 

103 

104 self.fillx = [] # fill data vertically (till x axis) 

105 self.filly = [] # fill data horizontally (till y axis) 

106 

107 self.label = [] # subplot list of labels 

108 

109############################################## 

110####### External Set Functions ######### 

111############################################## 

112 

113 def set_title(self, title = None): 

114 self.title = self.set_label(title) 

115 

116 def set_xlabel(self, label = None, xside = None): 

117 pos = self.xside_to_pos(xside) 

118 self.xlabel[pos] = self.set_label(label) 

119 

120 def set_ylabel(self, label = None, yside = None): 

121 pos = self.yside_to_pos(yside) 

122 self.ylabel[pos] = self.set_label(label) 

123 

124 def set_xlim(self, left = None, right = None, xside = None): 

125 left = self.date.string_to_time(left) if isinstance(left, str) else left 

126 right = self.date.string_to_time(right) if isinstance(right, str) else right 

127 left = None if left is None else float(left) 

128 right = None if right is None else float(right) 

129 xlim = [left, right] 

130 xlim = xlim if None in xlim else [min(xlim), max(xlim)] 

131 pos = self.xside_to_pos(xside) 

132 self.xlim[pos] = xlim 

133 

134 def set_ylim(self, lower = None, upper = None, yside = None): 

135 lower = self.date.string_to_time(lower) if isinstance(lower, str) else lower 

136 upper = self.date.string_to_time(upper) if isinstance(upper, str) else upper 

137 lower = None if lower is None else float(lower) 

138 upper = None if upper is None else float(upper) 

139 ylim = [lower, upper] 

140 ylim = ylim if None in ylim else [min(ylim), max(ylim)] 

141 pos = self.yside_to_pos(yside) 

142 self.ylim[pos] = ylim 

143 

144 def set_xscale(self, scale = None, xside = None): 

145 default_case = (scale is None or scale not in self.default.xscale) 

146 scale = self.default.xscale[0] if default_case else scale 

147 pos = self.xside_to_pos(xside) 

148 self.xscale[pos] = scale 

149 

150 def set_yscale(self, scale = None, yside = None): 

151 default_case = (scale is None or scale not in self.default.yscale) 

152 scale = self.default.yscale[0] if default_case else scale 

153 pos = self.yside_to_pos(yside) 

154 self.yscale[pos] = scale 

155 

156 def set_xticks(self, ticks = None, labels = None, xside = None): 

157 pos = self.xside_to_pos(xside) 

158 ticks = self.default.xticks[pos] if ticks is None else list(ticks) 

159 string_ticks = any([isinstance(el, str) for el in ticks]) 

160 labels = ticks if string_ticks and labels is None else labels 

161 ticks = self.date.strings_to_time(ticks) if string_ticks else ticks 

162 labels = ut.get_labels(ticks) if labels is None else list(labels) 

163 labels = list(map(str, labels)) 

164 ticks, labels = ut.brush(ticks, labels) 

165 self.xticks[pos] = ticks 

166 self.xlabels[pos] = labels 

167 self.xfrequency[pos] = self.xfrequency[pos] if ticks is None else len(ticks) 

168 

169 def set_yticks(self, ticks = None, labels = None, yside = None): 

170 pos = self.yside_to_pos(yside) 

171 ticks = self.default.yticks[pos] if ticks is None else list(ticks) 

172 string_ticks = any([isinstance(el, str) for el in ticks]) 

173 labels = ticks if string_ticks and labels is None else labels 

174 ticks = self.date.strings_to_time(ticks) if string_ticks else ticks 

175 labels = ut.get_labels(ticks) if labels is None else list(labels) 

176 labels = list(map(str, labels)) 

177 ticks, labels = ut.brush(ticks, labels) 

178 self.yticks[pos] = ticks 

179 self.ylabels[pos] = labels 

180 self.yfrequency[pos] = self.yfrequency[pos] if ticks is None else len(ticks) 

181 

182 def set_xfrequency(self, frequency = None, xside = None): 

183 pos = self.xside_to_pos(xside) 

184 frequency = self.default.xfrequency[pos] if frequency is None else int(frequency) 

185 self.xfrequency[pos] = frequency 

186 

187 def set_yfrequency(self, frequency = None, yside = None): 

188 pos = self.yside_to_pos(yside) 

189 frequency = self.default.yfrequency[pos] if frequency is None else int(frequency) 

190 self.yfrequency[pos] = frequency 

191 

192 def set_xreverse(self, reverse = None, xside = None): 

193 pos = self.xside_to_pos(xside) 

194 direction = self.default.xdirection[pos] if reverse is None else 2 * int(not reverse) - 1 

195 self.xdirection[pos] = direction 

196 

197 def set_yreverse(self, reverse = None, yside = None): 

198 pos = self.yside_to_pos(yside) 

199 direction = self.default.ydirection[pos] if reverse is None else 2 * int(not reverse) - 1 

200 self.ydirection[pos] = direction 

201 

202 def set_xaxes(self, lower = None, upper = None): 

203 self.xaxes[0] = self.default.xaxes[0] if lower is None else bool(lower) 

204 self.xaxes[1] = self.default.xaxes[1] if upper is None else bool(upper) 

205 

206 def set_yaxes(self, left = None, right = None): 

207 self.yaxes[0] = self.default.yaxes[0] if left is None else bool(left) 

208 self.yaxes[1] = self.default.yaxes[1] if right is None else bool(right) 

209 

210 def set_frame(self, frame = None): 

211 self.set_xaxes(frame, frame) 

212 self.set_yaxes(frame, frame) 

213 

214 def set_grid(self, horizontal = None, vertical = None): 

215 horizontal = self.default.grid[0] if horizontal is None else bool(horizontal) 

216 vertical = self.default.grid[1] if vertical is None else bool(vertical) 

217 self.grid = [horizontal, vertical] 

218 

219 def set_color(self, color = None): 

220 color = color if ut.is_color(color) else None 

221 return self.default.canvas_color if color is None else color 

222 

223 def set_canvas_color(self, color = None): 

224 self.canvas_color = self.set_color(color) 

225 

226 def set_axes_color(self, color = None): 

227 self.axes_color = self.set_color(color) 

228 

229 def set_ticks_color(self, color = None): 

230 self.ticks_color = self.set_color(color) 

231 

232 def set_ticks_style(self, style = None): 

233 style = style if ut.is_style(style) else None 

234 style = self.default.ticks_style if style is None else ut.clean_styles(style) 

235 self.ticks_style = style 

236 

237 def set_theme(self, theme = None): 

238 theme = 'default' if theme is None or theme not in ut.themes else theme 

239 self._set_theme(*ut.themes[theme]) 

240 

241 def clear_color(self): 

242 self.set_theme('clear') 

243 

244############################################## 

245####### Set Functions Utilities ######## 

246############################################## 

247 

248 def set_label(self, label = None): 

249 label = None if label is None else str(label).strip() 

250 spaces = ut.only_spaces(label) 

251 label = None if spaces else label 

252 return label 

253 

254 def correct_xside(self, xside = None): # from axis side to position 

255 xaxis = self.default.xside 

256 xside = xaxis[xside - 1] if isinstance(xside, int) and 1 <= xside <= 2 else xaxis[0] if xside is None or xside.strip() not in xaxis else xside.strip() 

257 return xside 

258 

259 def correct_yside(self, yside = None): 

260 yaxis = self.default.yside 

261 yside = yaxis[yside - 1] if isinstance(yside, int) and 1 <= yside <= 2 else yaxis[0] if yside is None or yside.strip() not in yaxis else yside.strip() 

262 return yside 

263 

264 def xside_to_pos(self, xside = None): # from axis side to position 

265 xside = self.correct_xside(xside) 

266 pos = self.default.xside.index(xside) 

267 return pos 

268 

269 def yside_to_pos(self, yside = None): 

270 yside = self.correct_yside(yside) 

271 pos = self.default.yside.index(yside) 

272 return pos 

273 

274 def _set_theme(self, canvas_color, axes_color, ticks_color, ticks_style, color_sequence): 

275 self.canvas_color = canvas_color 

276 self.axes_color = axes_color 

277 self.ticks_color = ticks_color 

278 self.ticks_style = ticks_style 

279 self.color_sequence = color_sequence 

280 

281############################################## 

282########## Draw() Function ############# 

283############################################## 

284 

285 def draw(self, *args, **kwargs): # from draw() comes directly the functions scatter() and plot() 

286 self.add_xside(kwargs.get("xside")) 

287 self.add_yside(kwargs.get("yside")) 

288 self.add_data(*args) 

289 self.add_lines(kwargs.get("lines")) 

290 self.add_markers(kwargs.get("marker")) 

291 self.add_colors(kwargs.get("color")) 

292 self.add_styles(kwargs.get("style")) 

293 self.add_fillx(kwargs.get("fillx")) 

294 self.add_filly(kwargs.get("filly")) 

295 self.add_label(kwargs.get("label")) 

296 

297############################################## 

298####### Draw() Called Functions ######## 

299############################################## 

300 

301 def add_xside(self, xside = None): 

302 xside = self.correct_xside(xside) 

303 self.xside.append(xside) 

304 

305 def add_yside(self, yside = None): 

306 yside = self.correct_yside(yside) 

307 self.yside.append(yside) 

308 

309 def add_data(self, *args): 

310 x, y = ut.set_data(*args) 

311 x, x_date = self.to_time(x) 

312 y, y_date = self.to_time(y) 

313 self.x_date[self.xside_to_pos(self.xside[-1])] = x_date 

314 self.y_date[self.yside_to_pos(self.yside[-1])] = y_date 

315 self.x.append(x) 

316 self.y.append(y) 

317 self.signals += 1 

318 

319 def add_lines(self, lines): 

320 lines = self.default.lines if lines is None else bool(lines) 

321 self.lines.append(lines) 

322 

323 def add_markers(self, marker = None): 

324 single_marker = isinstance(marker, str) or marker is None 

325 marker = self.check_marker(marker) if single_marker else list(map(self.check_marker, marker)) 

326 length = len(self.x[-1]) 

327 marker = ut.to_list(marker, length) 

328 self.marker.append(marker) 

329 

330 def add_colors(self, color = None): 

331 list_color = isinstance(color, list) 

332 color = list(map(self.check_color, color)) if list_color else self.check_color(color) 

333 length = len(self.x[-1]) 

334 self.past_colors = self.past_colors + [color] if color not in self.past_colors else self.past_colors 

335 color = ut.to_list(color, length) 

336 self.color.append(color) 

337 

338 def add_styles(self, style = None): 

339 single_style = isinstance(style, str) or style is None 

340 style = self.check_style(style) if single_style else list(map(self.check_style, style)) 

341 length = len(self.x[-1]) 

342 style = ut.to_list(style, length) 

343 self.style.append(style) 

344 

345 def add_fillx(self, fillx = None): 

346 fillx = self.check_fill(fillx) 

347 self.fillx.append(fillx) 

348 

349 def add_filly(self, filly = None): 

350 filly = self.check_fill(filly) 

351 self.filly.append(filly) 

352 

353 def add_label(self, label = None): 

354 spaces = ut.only_spaces(label) 

355 label = self.default.label if label is None or spaces else str(label).strip() # strip to remove spaces before and after 

356 self.label.append(label) 

357 #figure.subplot.label_show.append(default.label_show) 

358 

359############################################## 

360###### Draw() Functions Utilities ####### 

361############################################## 

362 

363 def to_time(self, data): 

364 dates = any([isinstance(el, str) for el in data]) 

365 data = self.date.strings_to_time(data) if dates else data 

366 return data, dates 

367 

368 def check_marker(self, marker = None): 

369 marker = None if marker is None else str(marker) 

370 marker = self.default.marker if marker is None else marker 

371 marker = ut.marker_codes[marker] if marker in ut.marker_codes else marker 

372 marker = marker if marker in ut.hd_symbols else marker[0] 

373 return marker 

374 

375 def check_color(self, color = None): 

376 color = color if ut.is_color(color) else None 

377 color = self.next_color() if color is None else color 

378 return color 

379 

380 def next_color(self): 

381 color = ut.difference(self.color_sequence, self.past_colors) 

382 color = color[0] if len(color) > 0 else self.color_sequence[0] 

383 return color 

384 

385 def check_style(self, style = None): 

386 style = None if style is None else str(style) 

387 style = style if ut.is_style(style) else ut.no_color 

388 return style 

389 

390 def check_fill(self, fill = None): 

391 fill = self.default.fill if fill is None else fill 

392 fill = False if isinstance(fill, str) and fill != self.default.fill_internal else fill 

393 fill = 0 if fill is True else fill 

394 return fill 

395 

396############################################## 

397###### Other Plotting Functions ######## 

398############################################## 

399 

400 def draw_bar(self, *args, marker = None, color = None, fill = None, width = None, orientation = None, minimum = None, offset = None, reset_ticks = None, xside = None, yside = None, label = None): 

401 x, y = ut.set_data(*args) 

402 marker = self.default.bar_marker if marker is None else marker 

403 fill = self.default.bar_fill if fill is None else fill 

404 width = self.default.bar_width if width is None else width 

405 width = 1 if width > 1 else 0 if width < 0 else width 

406 orientation = self.check_orientation(orientation, 1) 

407 minimum = 0 if minimum is None else minimum 

408 offset = 0 if offset is None else offset 

409 reset_ticks = True if reset_ticks is None else reset_ticks 

410 

411 x_string = any([type(el) == str for el in x]) # if x are strings 

412 l = len(x) 

413 xticks = range(1, l + 1) if x_string else x 

414 xlabels = x if x_string else map(str, x) 

415 x = xticks if x_string else x 

416 x = [el + offset for el in x] 

417 xbar, ybar = ut.bars(x, y, width, minimum) 

418 xbar, ybar = [xbar, ybar] if orientation[0] == 'v' else [ybar, xbar] 

419 (self.set_xticks(xticks, xlabels, xside) if orientation[0] == 'v' else self.set_yticks(xticks, xlabels, yside)) if reset_ticks else None 

420 

421 

422 firstbar = min([b for b in range(len(x)) if ybar[b][1] != 0], default = 0) # finds the position of the first non zero bar 

423 

424 for b in range(len(x)): 

425 xb = xbar[b]; yb = ybar[b] 

426 plot_label = label if b == firstbar else None 

427 plot_color = color if b == 0 else self.color[-1] 

428 nobar = (yb[1] == 0 and orientation[0] == 'v') or (xb[1] == 0 and orientation[0] == 'h') 

429 plot_marker = " " if nobar else marker 

430 plot_color = color if b == 0 else self.color[-1][-1] 

431 self.draw_rectangle(xb, yb, 

432 xside = xside, 

433 yside = yside, 

434 lines = True, 

435 marker = plot_marker, 

436 color = plot_color, 

437 fill = fill, 

438 label = plot_label) 

439 

440 def draw_multiple_bar(self, *args, marker = None, color = None, fill = None, width = None, orientation = None, minimum = None, offset = None, reset_ticks = None, xside = None, yside = None, labels = None): 

441 x, Y = ut.set_multiple_bar_data(*args) 

442 ly = len(Y) 

443 width = self.default.bar_width if width is None else width 

444 marker = [marker] * ly if marker is None or type(marker) != list else marker 

445 color = [color] * ly if color is None else color 

446 labels = [labels] * ly if labels is None else labels 

447 width = width / ly if ly != 0 else 0 

448 offset = ut.linspace(-1 / 2 + 1 / (2 * ly), 1 / 2 - 1 / (2 * ly), ly) if ly != 0 else [] 

449 

450 for i in range(ly): 

451 self.draw_bar(x, Y[i], 

452 marker = marker[i], 

453 color = color[i], 

454 fill = fill, 

455 width = width, 

456 orientation = orientation, 

457 minimum = minimum, 

458 offset = offset[i], 

459 xside = xside, 

460 yside = yside, 

461 label = labels[i], 

462 reset_ticks = reset_ticks) 

463 

464 def draw_stacked_bar(self, *args, marker = None, color = None, fill = None, width = None, orientation = None, minimum = None, offset = None, reset_ticks = None, xside = None, yside = None, labels = None): 

465 x, Y = ut.set_multiple_bar_data(*args) 

466 ly = len(Y) 

467 marker = [marker] * ly if marker is None or type(marker) != list else marker 

468 color = [color] * ly if color is None else color 

469 labels = [labels] * ly if labels is None else labels 

470 Y = ut.transpose([ut.cumsum(el) for el in ut.transpose(Y)]) 

471 for i in range(ly - 1, -1, -1): 

472 self.draw_bar(x, Y[i], 

473 xside = xside, 

474 yside = yside, 

475 marker = marker[i], 

476 color = color[i], 

477 fill = fill, 

478 width = width, 

479 orientation = orientation, 

480 label = labels[i], 

481 minimum = minimum, 

482 reset_ticks = reset_ticks) 

483 

484 def draw_hist(self, data, bins = None, marker = None, color = None, fill = None, norm = None, width = None, orientation = None, minimum = None, xside = None, yside = None, label = None): 

485 bins = self.default.hist_bins if bins is None else bins 

486 norm = False if norm is None else norm 

487 x, y = ut.hist_data(data, bins, norm) 

488 self.draw_bar(x, y, 

489 xside = xside, 

490 yside = yside, 

491 marker = marker, 

492 color = color, 

493 fill = fill, 

494 width = width, 

495 orientation = orientation, 

496 label = label, 

497 minimum = None, 

498 reset_ticks = False) 

499 

500 def draw_candlestick(self, dates, data, colors = None, orientation = None, xside = None, yside = None, label = None): 

501 orientation = self.check_orientation(orientation, 1) 

502 markers = ['sd', '│', '─'] #if markers is None else markers 

503 colors = ['green', 'red'] if colors is None else colors 

504 x = []; y = []; color = [] 

505 ln = len(dates) 

506 data = {"Open": [], "Close": [], "High": [], "Low": []} if len(data) == 0 else data 

507 Open = data["Open"]; Close = data["Close"]; High = data["High"]; Low = data["Low"] 

508 for i in range(ln): 

509 d = dates[i] 

510 o, c, h, l = Open[i], Close[i], High[i], Low[i] 

511 color = colors[0] if c > o else colors[1] 

512 m, M = min(o, c), max(o, c) 

513 lab = label if i == 0 else None 

514 if orientation in ['v', 'vertical']: 

515 self.draw([d, d], [M, h], xside = xside, yside = yside, color = color, marker = markers[1], lines = True) 

516 self.draw([d, d], [l, m], xside = xside, yside = yside, color = color, marker = markers[1], lines = True) 

517 self.draw([d, d], [m, M], xside = xside, yside = yside, color = color, marker = markers[0], lines = True, label = lab) 

518 elif orientation in ['h', 'horizontal']: 

519 self.draw([M, h], [d, d], xside = xside, yside = yside, color = color, marker = markers[2], lines = True) 

520 self.draw([l, m], [d, d], xside = xside, yside = yside, color = color, marker = markers[2], lines = True) 

521 self.draw([m, M], [d, d], xside = xside, yside = yside, color = color, marker = markers[0], lines = True, label = lab) 

522 

523 def draw_box(self, *args, xside = None, yside = None, orientation = None, colors = None, label = None, fill = None, width = None, minimum = None, offset = None, reset_ticks = None, quintuples = None): 

524 x, y = ut.set_data(*args) 

525 fill = self.default.bar_fill if fill is None else fill 

526 width = self.default.bar_width if width is None else width 

527 width = 1 if width > 1 else 0 if width < 0 else width 

528 orientation = self.check_orientation(orientation, 1) 

529 minimum = 0 if minimum is None else minimum 

530 offset = 0 if offset is None else offset 

531 reset_ticks = True if reset_ticks is None else reset_ticks 

532 colors = ['green', 'red'] if colors is None else colors 

533 quintuples = False if quintuples is None else quintuples 

534 

535 x_string = any([type(el) == str for el in x]) # if x are strings 

536 l = len(x) 

537 xticks = range(1, l + 1) if x_string else x 

538 xlabels = x if x_string else map(str, x) 

539 x = xticks if x_string else x 

540 x = [el + offset for el in x] 

541 (self.set_xticks(xticks, xlabels, xside) if orientation[0] == 'v' else self.set_yticks(xticks, xlabels, yside)) if reset_ticks else None 

542 if quintuples: 

543 # todo: check y is aligned. 

544 _, _, _, _, _, c, xbar = ut.box(x, y, width, minimum) 

545 q1, q2, q3, max_, min_ = [], [], [], [], [] 

546 for d in y: 

547 max_.append(d[0]) 

548 q3.append(d[1]) 

549 q2.append(d[2]) 

550 q1.append(d[3]) 

551 min_.append(d[4]) 

552 else: 

553 q1, q2, q3, max_, min_, c, xbar = ut.box(x, y, width, minimum) 

554 markers = ['sd', '│', '─'] #if markers is None else markers 

555 

556 for i in range(l): 

557 lab = label if i == 0 else None 

558 color = colors[0] 

559 mcolor = colors[1] 

560 d, l, h, m, E, M = c[i], min_[i], max_[i], q1[i], q2[i], q3[i] 

561 Ew = (M - m) / 30 

562 if orientation in ['v', 'vertical']: 

563 self.draw([d, d], [M, h], xside = xside, yside = yside, color = color, marker = markers[1], lines = True) 

564 self.draw([d, d], [l, m], xside = xside, yside = yside, color = color, marker = markers[1], lines = True) 

565 self.draw_rectangle(xbar[i], [m, M], xside = xside, yside = yside, 

566 lines = True, color = color, fill = fill, marker = markers[0], label = lab) 

567 self.draw_rectangle(xbar[i], [E, E], xside = xside, yside = yside, 

568 lines = True, color = mcolor, fill = fill, marker = markers[2]) 

569 #self.draw([d, d], [m, M], xside = xside, yside = yside, color = color, marker = markers[0], lines = True, label = lab) 

570 #self.draw(xbar[i], [E, E], xside = xside, yside = yside, color = mcolor, marker = markers[0], lines = False) 

571 elif orientation in ['h', 'horizontal']: 

572 self.draw([M, h], [d, d], xside = xside, yside = yside, color = color, marker = markers[2], lines = True) 

573 self.draw([l, m], [d, d], xside = xside, yside = yside, color = color, marker = markers[2], lines = True) 

574 self.draw_rectangle([m, M], xbar[i], xside = xside, yside = yside, 

575 lines = True, color = color, fill = fill, marker = markers[0], label = lab) 

576 self.draw_rectangle([E, E], xbar[i], xside = xside, yside = yside, 

577 lines = True, color = mcolor, fill = fill, marker = markers[1]) 

578 #self.draw([m, M], [d, d], xside = xside, yside = yside, color = color, marker = markers[0], lines = True, label = lab) 

579 #self.draw([E, E], [d, d], xside = xside, yside = yside, color = 'red', marker = markers[0], lines = True) 

580 

581############################################## 

582########### Plotting Tools ############# 

583############################################## 

584 

585 def draw_error(self, *args, xerr = None, yerr = None, color = None, xside = None, yside = None, label = None): 

586 x, y = ut.set_data(*args) 

587 l = len(x) 

588 xerr = [0] * l if xerr is None else xerr 

589 yerr = [0] * l if yerr is None else yerr 

590 for i in range(l): 

591 col = self.color[-1][-1] if i > 0 else color 

592 self.draw([x[i], x[i]], [y[i] - yerr[i] / 2, y[i] + yerr[i] / 2], xside = xside, yside = yside, marker = "│", color = col, lines = True) 

593 col = self.color[-1][-1] if i == 0 else col 

594 self.draw([x[i] - xerr[i] / 2, x[i] + xerr[i] / 2], [y[i], y[i]], xside = xside, yside = yside, marker = "─", color = col, lines = True) 

595 self.draw([x[i]], [y[i]], xside = xside, yside = yside, marker = "┼", color = col, lines = True) 

596 

597 def draw_event_plot(self, data, marker = None, color = None, orientation = None, side = None): 

598 x, y = data, [1.1] * len(data) 

599 orientation = self.check_orientation(orientation, 1) 

600 if orientation in ['v', 'vertical']: 

601 self.draw(x, y, xside = side, marker = marker, color = color, fillx = True) 

602 self.set_ylim(0, 1) 

603 self.set_yfrequency(0) 

604 else: 

605 self.draw(y, x, yside = side, marker = marker, color = color, filly = True) 

606 self.set_xlim(0, 1) 

607 self.set_xfrequency(0) 

608 

609 def draw_vertical_line(self, coordinate, color = None, xside = None): 

610 coordinate = self.date.string_to_time(coordinate) if isinstance(coordinate, str) else coordinate 

611 pos = self.xside_to_pos(xside) 

612 self.vcoord[pos].append(coordinate) 

613 color = self.ticks_color if color is None else color 

614 self.vcolors[pos].append(self.check_color(color)) 

615 

616 def draw_horizontal_line(self, coordinate, color = None, yside = None): 

617 coordinate = self.date.string_to_time(coordinate) if isinstance(coordinate, str) else coordinate 

618 pos = self.xside_to_pos(yside) 

619 self.hcoord[pos].append(coordinate) 

620 color = self.ticks_color if color is None else color 

621 self.hcolors[pos].append(self.check_color(color)) 

622 

623 def draw_text(self, text, x, y, xside = None, yside = None, color = None, background = None, style = None, orientation = None, alignment = None): 

624 orientation = self.check_orientation(orientation) 

625 text = text if orientation is self.default.orientation[0] else text[::-1] 

626 self.text.append(str(text)) 

627 x = self.date.string_to_time(x) if isinstance(x, str) else x 

628 y = self.date.string_to_time(y) if isinstance(y, str) else y 

629 self.tx.append(x) 

630 self.ty.append(y) 

631 self.txside.append(self.correct_xside(xside)) 

632 self.tyside.append(self.correct_yside(yside)) 

633 color = self.next_color() if color is None or not ut.is_color(color) else color 

634 background = self.canvas_color if background is None or not ut.is_color(background) else background 

635 self.tfull.append(color) 

636 self.tback.append(background) 

637 self.tstyle.append(self.check_style(style)) 

638 alignment = self.check_alignment(alignment) 

639 self.torien.append(orientation) 

640 self.talign.append(alignment) 

641 

642 def draw_rectangle(self, x = None, y = None, marker = None, color = None, lines = None, fill = None, reset_lim = False, xside = None, yside = None, label = None): 

643 x = [0, 1] if x is None or len(x) < 2 else x 

644 y = [0, 1] if y is None or len(y) < 2 else y 

645 xpos = self.xside_to_pos(xside) 

646 ypos = self.yside_to_pos(yside) 

647 lines = True if lines is None else lines 

648 fill = False if fill is None else fill 

649 xm = min(x); xM = max(x); 

650 ym = min(y); yM = max(y); 

651 dx = abs(xM - xm); dy = abs(yM - ym); 

652 if reset_lim: 

653 self.xlim[xpos] = [xm - 0.5 * dx, xM + 0.5 * dx] 

654 self.ylim[xpos] = [ym - 0.5 * dy, yM + 0.5 * dy] 

655 x, y = [xm, xm, xM, xM, xm], [ym, yM, yM, ym, ym] 

656 self.draw(x, y, 

657 xside = xside, 

658 yside = yside, 

659 lines = True if fill else lines, 

660 marker = marker, 

661 color = color, 

662 fillx = "internal" if fill else False, 

663 filly = False, 

664 label = label) 

665 

666 def draw_polygon(self, x = None, y = None, radius = None, sides = None, marker = None, color = None, lines = None, fill = None, reset_lim = False, xside = None, yside = None, label = None): 

667 x = 0 if x is None else x 

668 y = 0 if y is None else y 

669 radius = 1 if radius is None else abs(int(radius)) 

670 sides = 3 if sides is None else max(3, int(abs(sides))) 

671 xpos = self.xside_to_pos(xside) 

672 ypos = self.yside_to_pos(yside) 

673 lines = True if lines is None else lines 

674 fill = False if fill is None else fill 

675 

676 alpha = 2 * math.pi / sides 

677 init = alpha / 2 + math.pi / 2 if sides % 2 == 0 else alpha / 4 * ((-1) ** (sides // 2))# * math.pi #- ((-1) ** (sides)) * alpha / 4 

678 #init = 0 * init 

679 get_point = lambda i: [x + math.cos(alpha * i + init) * radius, y + math.sin(alpha * i + init) * radius] 

680 # the rounding is needed so that results like 9.9999 are rounded to 10 and display as same coordinate in the plot, otherwise the floor function will turn 9.999 into 9 

681 points = [get_point(i) for i in range(sides + 1)] 

682 if reset_lim: 

683 self.xlim[xpos] = [x - 1.5 * radius, x + 1.5 * radius] 

684 self.ylim[xpos] = [y - 1.5 * radius, y + 1.5 * radius] 

685 self.draw(*ut.transpose(points), 

686 xside = xside, 

687 yside = yside, 

688 lines = True if fill else lines, 

689 marker = marker, 

690 color = color, 

691 fillx = "internal" if fill else False, 

692 filly = False, 

693 label = label) 

694 

695 def draw_confusion_matrix(self, actual, predicted, color = None, style = None, labels = None): 

696 color = self.default.cmatrix_color if color is None else self.check_color(color) 

697 style = self.default.cmatrix_style if style is None else self.check_style(style) 

698 

699 L = len(actual) 

700 n_labels = sorted(ut.no_duplicates(actual)) 

701 labels = n_labels if labels is None else list(labels) 

702 l = len(n_labels) 

703 

704 get_sum = lambda a, p: sum([actual[i] == a and predicted[i] == p for i in range(L)]) 

705 cmatrix = [[get_sum(n_labels[r], n_labels[c]) for c in range(l)] for r in range(l)] 

706 cm = ut.join(cmatrix); m, M, t = min(cm), max(cm), sum(cm) 

707 

708 lm = 253; lM = 80 

709 to_255 = lambda l: round(lm + (lM - lm) * (l - m) / (M - m)) # l=m -> lm; l=M->lM 

710 to_color = lambda l: tuple([to_255(l)] * 3) 

711 to_text = lambda n: str(round(n, 2)) + ' - ' + str(round(100 * n / t, 2)) + '%' 

712 for r in range(l): 

713 for c in range(l): 

714 count = cmatrix[r][c] 

715 col = to_color(count) 

716 self.draw_rectangle([c - 0.5, c + 0.5], [r - 0.5, r + 0.5], color = col, fill = True) 

717 self.draw_text(to_text(count), c, r, color = color, background = col, style = style) 

718 

719 self.set_yreverse(True) 

720 self.set_xticks(n_labels, labels) 

721 self.set_yticks(n_labels, labels) 

722 self.set_ticks_color(color); self.set_ticks_style(style); 

723 self.set_axes_color('default'); self.set_canvas_color('default'); 

724 self.set_title('Confusion Matrix') 

725 self.set_xlabel('Predicted') 

726 self.set_ylabel('Actual') 

727 

728 def draw_indicator(self, value, label = None, color = None, style = None): 

729 color = self.default.cmatrix_color if color is None else self.check_color(color) 

730 style = self.default.cmatrix_style if style is None else self.check_style(style) 

731 

732 self.set_title(label) 

733 self.set_ticks_color(color); 

734 self.set_ticks_style(style); 

735 self.set_axes_color('default') 

736 self.set_canvas_color('default') 

737 self.set_xfrequency(0) 

738 self.set_yfrequency(0) 

739 

740 self.draw_text(str(value), 0, 0, color = color, style = style, alignment = 'center') 

741 

742############################################## 

743############## 2D Plots ################ 

744##############################################  

745 

746 def draw_matrix(self, matrix, marker = None, style = None, fast = False): 

747 matrix = [l.copy() for l in matrix] 

748 marker = [marker] if type(marker) != list else marker 

749 marker = [self.check_marker("sd") if el in ut.join([None, ut.hd_symbols]) else self.check_marker(el) for el in marker] 

750 style = ut.no_color if style is None else self.check_style(style) 

751 cols, rows = ut.matrix_size(matrix) 

752 rows = 0 if cols == 0 else rows 

753 matrix = matrix if rows * cols != 0 and ut.is_rgb_color(matrix[0][0]) else ut.turn_gray(matrix) 

754 marker = ut.repeat(marker, cols) 

755 if not fast: 

756 for r in range(rows): 

757 xyc = [(c, r, matrix[rows - 1 - r][c]) for c in range(cols)] 

758 x, y, color = ut.transpose(xyc, 3) 

759 self.draw(x, y, marker = marker, color = color, style = style) 

760 self.set_canvas_color("black") 

761 self.set_xlabel('column') 

762 self.set_ylabel('row') 

763 xf, yf = min(self.xfrequency[0], cols), min(self.yfrequency[0], rows) 

764 xt = ut.linspace(0, cols - 1, xf) 

765 xl = ut.get_labels([el + 1 for el in xt]) 

766 yt = ut.linspace(0, rows - 1, yf) 

767 yl = ut.get_labels([rows - el for el in yt]) 

768 self.set_xticks(xt, xl) 

769 self.set_yticks(yt, yl) 

770 else: # if fast 

771 for r in range(rows): 

772 for c in range(cols): 

773 ansi = ut.colors_to_ansi(matrix[r][c], style, "black") 

774 matrix[r][c] = ansi + marker[c] + ut.ansi_end 

775 self.matrix.canvas = '\n'.join([''.join(row) for row in matrix]) 

776 self.fast_plot = True 

777 

778 def draw_heatmap(self, dataframe, color = None, style=None): 

779 color = self.default.cmatrix_color if color is None else self.check_color(color) 

780 style = self.default.cmatrix_style if style is None else self.check_style(style) 

781 

782 xlabels = dataframe.columns.tolist() 

783 ylabels = dataframe.index.tolist() 

784 

785 cmatrix = dataframe.values.tolist() 

786 cm = ut.join(cmatrix) 

787 m, M, t = min(cm), max(cm), sum(cm) 

788 

789 lm = 253 

790 lM = 80 

791 to_255 = lambda l: round(lm + (lM - lm) * (l - m) / (M - m)) # l=m -> lm; l=M->lM 

792 to_color = lambda l: tuple([to_255(l)] * 3) 

793 

794 for r in range(len(dataframe.index.tolist())): 

795 for c in range(len(dataframe.columns.tolist())): 

796 count = cmatrix[r][c] 

797 col = to_color(count) 

798 self.draw_rectangle([c - 0.5, c + 0.5], [r - 0.5, r + 0.5], marker= 'sd', color=col, fill=True) 

799 

800 y_labels = list(set(range(len(dataframe.columns)))) 

801 x_labels = list(set(range(len(dataframe.columns)))) 

802 

803 self.set_yreverse(True) 

804 self.set_xticks(x_labels, xlabels) 

805 self.set_yticks(y_labels, ylabels) 

806 self.set_ticks_color(color); 

807 self.set_ticks_style(style); 

808 self.set_axes_color('default'); 

809 self.set_canvas_color('default'); 

810 self.set_title('Heatmap') 

811 print(dataframe) 

812 

813 def draw_image(self, path, marker = None, style = None, fast = False, grayscale = False): 

814 from PIL import Image 

815 path = ut.correct_path(path) 

816 if not ut.is_file(path): 

817 return 

818 image = Image.open(path) 

819 self._draw_image(image, marker = marker, style = style, grayscale = grayscale, fast = fast) 

820 

821############################################## 

822####### Plotting Tools Utilities ####### 

823############################################## 

824 

825 def check_orientation(self, orientation = None, default_index = 0): 

826 default = self.default.orientation 

827 default_first_letter = [el[0] for el in default] 

828 orientation = default[default_first_letter.index(orientation)] if orientation in default_first_letter else orientation 

829 orientation = default[default_index] if orientation not in default else orientation 

830 return orientation 

831 

832 def check_alignment(self, alignment = None): 

833 default = self.default.alignment[0:-1] 

834 default_first_letter = [el[0] for el in default] 

835 alignment = default[default_first_letter.index(alignment)] if alignment in default_first_letter else alignment 

836 alignment = default[1] if alignment not in default else alignment 

837 return alignment 

838 

839 def _draw_image(self, image, marker = None, style = None, fast = False, grayscale = False): 

840 from PIL import ImageOps 

841 image = ImageOps.grayscale(image) if grayscale else image 

842 image = image.convert('RGB') 

843 size = ut.update_size(image.size, self.size) 

844 image = image.resize(size, resample = True) 

845 matrix = ut.image_to_matrix(image) 

846 self.set_xfrequency(0); self.set_yfrequency(0); 

847 self.draw_matrix(matrix, marker = marker, style = style, fast = fast) 

848 self.set_xlabel(); self.set_ylabel()