Yes, my oversight.
So, I only had time to take this so far, but here is partial code structured the way I would recommend, that updates the data, rather than hiding/creating entirely new things. It shows 2, then 1, then 2 radar patches, and does not leak resources. You would need to continue the work to handle the “axes”. If their positions do not change, you can do it once in create_plot
. Otherwise they would need the same treatment to draw glyphs once with empy sources, then update the sources later. Note I have remove the double underscores on the instance vars. This is really dis-recommended outside of a few very special cases (this coming from CPython Core Devs I work with).
In general, my best advice to you is to separate out generating data, from using that data to draw things. That decoupling will lead to a much more flexible design.
# CELL 1
import numpy as np
form radar import RadarGraph
from bokeh.io import output_notebook
# CELL 2
output_notebook()
# CELL 3
text1 = [
'lexical richness max :0.326',
'ratio filling pause max :0.039',
'ratio feedback max :0.305',
'taille moyenne Ipu non feedback max :8.699',
'familiar language max :5.677'
]
flist1 = [
np.array([0.45669999, 1, 0.47506903, 0.96134016, 1]),
np.array([1, 0.58196036, 1, 1, 0.37513551])
]
text2 = [
'lexical richness max :0.326',
'ratio filling pause max :0.023',
'ratio feedback max :0.305',
'taille moyenne Ipu non feedback max :8.699',
'familiar language max :2.130'
]
flist2 = [
np.array([1, 1, 1, 1, 1])
]
text3 = [
'lexical richness max :0.326',
'ratio filling pause max :0.039',
'ratio feedback max :0.305',
'taille moyenne Ipu non feedback max :8.699',
'familiar language max :5.677'
]
flist3 = [
np.array([0.45669999, 1, 0.47506903, 0.96134016, 1]),
np.array([1, 0.58196036, 1, 1, 0.37513551])
]
# CELL 4
radar = RadarGraph()
radar.show()
radar.update(text1, flist1)
time.sleep(2)
radar.update(text2, flist2)
time.sleep(2)
radar.update(text3, flist3)
time.sleep(2)
And here is a (partial) radar.py
:
import time
from bokeh.io import push_notebook, show
from bokeh.models import ColumnDataSource, LabelSet
from bokeh.plotting import figure
DEFAULT_COLORS=["black", "blue", "red", "yellow", "green", "purple", "white", "brown", "orange", "pink", "black"]
DEFAULT_TITLE = "radar graph"
def unit_poly_verts(theta, center):
x0, y0, r = [center] * 3
verts = [(r * np.cos(t) + x0, r * np.sin(t) + y0) for t in theta]
return verts
def radar_patch(r, theta, center):
offset = 0
yt = (r * center + offset) * np.sin(theta) + center
xt = (r * center + offset) * np.cos(theta) + center
return xt, yt
class RadarGraph(object):
def __init__(self, colors=DEFAULT_COLORS, title=DEFAULT_TITLE):
self.colors = colors
self.title = title
self.handle = None
self.center = 0.5
self.label_source = ColumnDataSource(data=dict(x=[], y=[], text=[]))
self.radar_source = ColumnDataSource(data=dict(xs=[], ys=[], color=[]))
self.segment_source = ColumnDataSource(data=dict(x1=[], y1=[]))
self.plot = self.create_plot(title, colors)
def show(self):
self.handle = show(self.plot, notebook_handle=True)
def update(self, text, flist):
# update labels data source
text = text + [""]
nbVar = len(flist[0])
theta = np.linspace(0, 2 * np.pi, nbVar, endpoint=False)
theta += np.pi / 2
verts = unit_poly_verts(theta, self.center)
x = [v[0] for v in verts]
y = [v[1] for v in verts]
self.label_source.data = {'x': x+[self.center], 'y': y+[1], 'text': text}
# update radar data source
xs, ys = [], []
for i in range(len(flist)):
xt, yt = radar_patch(flist[i], theta, self.center)
xs.append(xt)
ys.append(yt)
self.radar_source.data = dict(xs=xs, ys=ys, color=self.colors[:len(xs)])
# push the changes
push_notebook(handle=self.handle)
def create_plot(self, title, colors):
plot = figure(title=title, tools="", x_range=(0, 2), y_range=(0, 1.5))
plot.segment(x0=0.5, y0=0.5, x1='x1', y1='y1', color="black", line_width=1,
source=self.segment_source)
labels = LabelSet(x="x", y="y", text="text", source=self.label_source)
plot.add_layout(labels)
plot.patches(xs='xs', ys='ys', fill_color='color', fill_alpha=0.15,
source=self.radar_source)
return plot
# You will have to take over here:
# drawRadar(x, y, self.__plot, 10)
# def drawRadar(x1, y1, p, nbOfRulerSegment):
# origine = 0.5
# ticSize = 0.01
# for i in range(0, len(x1)):
# p.segment(x0=origine, y0=origine, x1=x1[i],
# y1=y1[i], color="black", line_width=1)
# if x1[i] == origine:
# x1[i] = origine + 0.001 # prevent deviding by 0
# if y1[i] == origine:
# y1[i] = origine + 0.001 # prevent deviding by 0
# leadingCoefficient = (y1[i]-origine)/(x1[i]-origine)
# leadingCoefficientRuler = -(1/leadingCoefficient)
# deltaX = x1[i] - origine
# deltaY = y1[i] - origine
# rulerX0 = []
# rulerX1 = []
# rulerY0 = []
# rulerY1 = []
# # some quick maths: solving (a*x)^2 +x^2 - tickSize^2 = 0 -> pythagore theorem
# a = (leadingCoefficientRuler**2)+1
# c = -(ticSize**2)
# # b is equal to 0
# delta = - (4 * a * c) # b^2 - 4*a*c
# if delta == 0: # prevent sqrt(0)
# delta = 0.001
# solution = math.sqrt(delta)/(2*a)
# for i in range(1, nbOfRulerSegment):
# centreX = origine + ((float(i)/float(nbOfRulerSegment)) * deltaX)
# centreY = origine + ((float(i)/float(nbOfRulerSegment)) * deltaY)
# rulerX0.append(centreX + solution)
# rulerX1.append(centreX - solution)
# rulerY0.append(centreY + solution * leadingCoefficientRuler)
# rulerY1.append(centreY - solution * leadingCoefficientRuler)
# p.segment(x0=rulerX0, y0=rulerY0, x1=rulerX1, y1=rulerY1, color="black", line_width=0.5)