plotext-plus 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plotext_plus/__init__.py +31 -0
- plotext_plus/__main__.py +2 -0
- plotext_plus/_api.py +828 -0
- plotext_plus/_build.py +270 -0
- plotext_plus/_core.py +581 -0
- plotext_plus/_date.py +60 -0
- plotext_plus/_default.py +83 -0
- plotext_plus/_dict.py +210 -0
- plotext_plus/_doc.py +707 -0
- plotext_plus/_doc_utils.py +291 -0
- plotext_plus/_figure.py +488 -0
- plotext_plus/_global.py +370 -0
- plotext_plus/_matrix.py +171 -0
- plotext_plus/_monitor.py +848 -0
- plotext_plus/_output.py +136 -0
- plotext_plus/_shtab.py +9 -0
- plotext_plus/_themes.py +343 -0
- plotext_plus/_utility.py +853 -0
- plotext_plus/api.py +828 -0
- plotext_plus/charts.py +42 -0
- plotext_plus/core.py +581 -0
- plotext_plus/mcp_cli.py +79 -0
- plotext_plus/mcp_server.py +505 -0
- plotext_plus/plotext_cli.py +375 -0
- plotext_plus/plotting.py +92 -0
- plotext_plus/themes.py +29 -0
- plotext_plus/utilities.py +52 -0
- plotext_plus/utils.py +370 -0
- plotext_plus-1.0.1.dist-info/METADATA +303 -0
- plotext_plus-1.0.1.dist-info/RECORD +33 -0
- plotext_plus-1.0.1.dist-info/WHEEL +4 -0
- plotext_plus-1.0.1.dist-info/entry_points.txt +3 -0
- plotext_plus-1.0.1.dist-info/licenses/LICENSE +23 -0
plotext_plus/_monitor.py
ADDED
|
@@ -0,0 +1,848 @@
|
|
|
1
|
+
from plotext_plus._default import default_monitor_class
|
|
2
|
+
from plotext_plus._matrix import matrix_class
|
|
3
|
+
from plotext_plus._build import build_class
|
|
4
|
+
import plotext_plus._utility as ut
|
|
5
|
+
from copy import deepcopy
|
|
6
|
+
import 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
|
+
|
|
10
|
+
class 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()
|