Hi, all. I use param. And I want to change graph axis when I click the table. the situation is below.
- display the graph and change screen from graph to table.
- I clicked the table But it is no response.
- I modified the table.
- I clicked the table , too . the graph axis has changed. ( It works well )
I want to know , when I am in 2 situation (I clicked the table But it is no response. ) , change the graph axis. What should I modify the code? Could someone tell me?
I have read the related document (below) . But I couldn’t figure out my problem.
Someone help me?
@param.depends with watch=True not working - Param - HoloViz Discourse
Screen shot 1 select v2 , But didn’t change graph x-axis
Screen shot 2 change the table value
Screen shot 3 click again . graph axis was changed . It works fine.
Screen shot 4 graph axis was changed . It works fine.
My simple code
from typing import Any
from bokeh.models.annotations import Title
import hvplot.pandas # noqa
import numpy as np
import pandas as pd
import panel as pn
import param
import holoviews as hv
from numpy.random import *
### FLAG_FIRST = {'first' : 0, 'string' : '---'} # Progress bar 計算用
### FLAG_FIRST['first'] = 1
# https://discourse.holoviz.org/t/formating-text-in-a-python-panel/1291
pn.extension('tabulator')
INDICES = ["v1", "v2", "v3", "v4"]
memory = set([])
#------------------------------------------------------------------------------------
# Definition ReactiveTable Class
#------------------------------------------------------------------------------------
class ReactiveTable(pn.viewable.Viewer):
table = param.DataFrame() # x,y table
count = param.Integer() # number of items
random = param.Action(lambda x: x.param.trigger("random"), label="Random")
x = param.ObjectSelector(INDICES[0], objects=INDICES, label="x") # selector x
y = param.ObjectSelector(INDICES[1], objects=INDICES, label="y") # selector y
int_list = param.ListSelector(default=[3, 5], objects=[1, 3, 5, 7, 9], precedence=0.5)
Autocomplete_area = pn.widgets.AutocompleteInput(name='Autocomplete Input', options=INDICES, placeholder='Write something here')
table_widget = pn.widgets.Tabulator()
Toggle_tf = param.Boolean() # <--- 20211220 add Toggle_tf param
from holoviews import opts
from holoviews import streams
selection = streams.Selection1D()
memory = set([])
first_flag = False ## Tap First Flag
def __init__(self, **params):
super().__init__(**params)
self.table_widget = pn.widgets.Tabulator.from_param(self.param.table)
self.table_widget.style.applymap(self.color_negative_red,subset='T3').apply(self.highlight_max,subset='T3')
self.Toggle = pn.widgets.Toggle.from_param(self.param.Toggle_tf,name='Push Toggle', button_type='default') # <<-- 20211220 add button_widget
# https://discourse.holoviz.org/t/param-depends-with-watch-true-not-working/921/3
# This works fine for me as long as you call super in the constructor:
# https://pandas.pydata.org/pandas-docs/stable/user_guide/style.html 色の付け方等
# https://panel.holoviz.org/api/panel.widgets.html?highlight=tabulator#panel.widgets.tables.Tabulator
# https://panel.holoviz.org/reference/widgets/Tabulator.html#widgets-gallery-tabulator
def color_negative_red(self,val):
"""
Takes a scalar and returns a string with
the css property `'color: red'` for negative
strings, black otherwise.
"""
color = 'red' if val > 60 else 'blue'
return 'color: %s' % color
def highlight_max(self,s):
'''
highlight the maximum in a Series yellow.
'''
is_max = s == s.max()
return ['background-color: yellow' if v else '' for v in is_max]
def change_data(self) :
size = np.random.randint(10, 15)
nums = np.random.randint(5, 10) # <--- 2021/12/06 Change nums of items
self.count = nums # <--- 2021/12/06 Display nums of items
INDICES = [ str('v'+str(i)) for i in range(nums) ] # <--- 2021/12/06 Make a new indices list
test = pd.DataFrame(
{i: np.arange(1, 100, size) for i in INDICES}) # numpy.arange([start, ]stop, [step, ]dtype = None)
for i in range(len(test)) :
for j in range(len(INDICES)) :
test.iloc[i][j]=test.iloc[i][j]*(rand()+1)+np.random.randint(15)
TEST = [ str('T'+str(i)) for i in range(len(test)) ] # <--- 2021/12/06 Make a new indices list
temp_A = test.T
temp_A.columns = TEST
self.table = temp_A
self.param.x.objects = INDICES # change selector x lists
self.param.y.objects = INDICES # change selector y lists
self.Autocomplete_area.options = INDICES
print ('change_data')
# Definition click random button
#@param.depends("random", watch=True, on_init=True)
@param.depends('Toggle_tf',watch=True, on_init=True)
def _fill_table_with_random_data(self):
if hasattr(self, "tabs") :
self.memory=[]
self.first_flag = True
if self.Toggle_tf == True :
self.change_data()
self.Toggle.name = 'Finish'
self.Toggle.button_type = 'primary'
else :
self.Toggle.name = 'Push Toggle'
self.Toggle.button_type = 'default'
else :
self.change_data()
# Definition click table widget
@param.depends("table_widget.selection", watch=True)
def _set_x_with_table_selection(self):
OBJECT = (self.param.x.objects)
if self.table_widget.selection:
self.x = OBJECT[self.table_widget.selection[0]]
if hasattr(self, "tabs") : self.tabs.active = 0 # if it is not initial , self has tabs objects. so change active tab=0
else:
self.x = OBJECT[0]
# self.count += 1 # <--- 2021/12/06 Comment out
# Definition select x value
@param.depends("x",watch=True)
def _set_table_selection_with_x(self):
OBJECT = (self.param.x.objects)
self.table_widget.selection = [OBJECT.index(self.x)]
self.Autocomplete_area.value = self.x
self.memory=[]
self.first_flag = True
@param.depends("Autocomplete_area.value",watch=True)
def _set_table_selection_with_Autocomplete_area(self):
OBJECT = (self.Autocomplete_area.options)
sel = self.Autocomplete_area.value
if (sel in OBJECT) :
self.table_widget.selection = [OBJECT.index(self.Autocomplete_area.value)]
self.memory=[]
self.first_flag = True
# Definition select x,y value or click random button
# @param.depends("x", "y", "random")
@param.depends("x", "y", "selection.index" , 'Toggle_tf')
def plot(self):
index = self.selection.index
if self.first_flag == True or self.Toggle_tf == True :
index = []
self.first_flag = False
print ('debug', index)
df = pd.DataFrame ({"x": self.table.loc[self.x], "y": self.table.loc[self.y]})
for i in index:
self.memory.append(i) # add previous indices
df2 = df.loc[~df.index.isin(self.memory)] # remove all past indices
new_graph = hv.Scatter(df2, "x", "y").opts(size=5, color="yellow") # plot
#相関係数を計算
x_corr = df2['x'].to_numpy().tolist()
y_corr = df2['y'].to_numpy().tolist()
# 相関行列を計算
coef = np.corrcoef(x_corr, y_corr)
# 相関行列を表示
print('20220105 Debug correlation value : ',coef)
a, b = np.polyfit(x_corr, y_corr, 1)
if b>0 :
plot_title = 'Rxy='+str(coef[0][1])[0:4] + ' : y='+str(a)[0:5]+'x+'+str(b)[0:5]
else :
plot_title = 'Rxy='+str(coef[0][1])[0:5] + ' : y='+str(a)[0:5]+'x'+str(b)[0:6]
graph_1 = self.table.T.hvplot.scatter(
x=self.x, y=self.y, color="red", grid=True, xlim=(0, 100), ylim=(0, 100)
).opts(toolbar='right',size=8, title=plot_title, tools=['tap','box_select', 'lasso_select'])
graph_2 = hv.Slope(a, b).opts(color='green', line_width=4 , line_dash='dashed')
# Declare points as source of selection stream
self.selection.source=graph_1
plot = graph_1 * graph_2 * new_graph
return plot
# Definition update data table
@param.depends("table")
def table_list(self):
ret_table = pn.pane.DataFrame(self.table.style.applymap(self.color_negative_red).apply(self.highlight_max))
return ret_table
# https://holoviews.org/reference/apps/bokeh/selection_stream.html
# https://holoviews.org/reference/containers/bokeh/DynamicMap.html
# Definition panel layout
def __panel__(self):
# Layout
graph_layout = pn.Row(
pn.Column(
pn.pane.Markdown("## Update table"),
self.param.x,
self.Autocomplete_area,
self.param.y,
# self.param.random,
self.Toggle,
),
pn.panel(pn.Row(self.plot), sizing_mode="fixed"),
)
# list_layout = pn.Column(self.tabulator, self.param.count, self.Toggle)
list_layout = pn.Column(self.table_widget, self.param.count, self.Toggle)
self.tabs = pn.Tabs(
("Graph", graph_layout),
("List", list_layout),
active=self.tabs.active if hasattr(self, "tabs") else 0,
)
return pn.template.FastListTemplate(
site="Panel",
main=self.tabs,
title="Panel Sample",
theme="dark",
)
# run app
if __name__ == "__main__":
app = ReactiveTable()
app.show(port=5007)
elif __name__.startswith("bokeh"):
app = ReactiveTable()
app.servable()
Could some tell me how to change graph-axis when I click the table first.
Best regards,