简体   繁体   中英

Why is my plot updated by panel (twice) when I change a button setting that shouldn't trigger any updates? (Panel Holoviz)

I made a class to explore and train models.

When I change the value of dropdown 'choose_model_type' in the code example below, I would expect nothing to change in my dashboard, since there are no @param.depends('choose_model_type', watch=True) in my class. However, my dashboard gets updated, when I change the value of dropdown 'choose_model_type'. In this case function plot_y() gets triggered twice if I look at the logs:

2019-09-26 11:24:42,802 starting plot_y
2019-09-26 11:24:42,825 starting plot_y

This is for me unexpected behavior. I don't want plot_y() to be triggered when I change 'choose_model_type'.
How do i make sure that plot_y gets triggered only when 'y' changes (and my plot is updated in the dashboard) and not when other parameters such as dropdown change?
I want to control what gets triggered when, but for me there seems to be some magic going on.

Other related question is:
Why does plot_y() get triggered twice? If I change 'pred_target' it also triggers plot_y() twice. Same happens when I change the value of 'choose_model_type': plot_y() gets triggered twice.

# library imports    
import logging

import numpy as np
import pandas as pd

import hvplot
import hvplot.pandas

import holoviews as hv
from holoviews.operation.datashader import datashade, dynspread
hv.extension('bokeh', logo=False)

import panel as pn
import param

# create some sample data
df = pd.DataFrame(np.random.choice(100, size=[50, 2]), columns=['TARGET1', 'TARGET2'])

# class to train my models with some settings
class ModelTrainer(param.Parameterized):

    logging.info('initializing class')

    pred_target = param.Selector(
        default='TARGET1',
        objects=['TARGET1', 'TARGET2'],
        label='Choose prediction target'
    )

    choose_model_type = param.Selector(
        default='LINEAR', 
        objects=['LINEAR', 'LGBM', 'RANDOM_FOREST'],
        label='Choose type of model',
    )

    y = df[pred_target.default]


    # i expect this function only to be triggered when pred_target changes
    @param.depends('pred_target', watch=True)
    def _reset_variables(self):
        logging.info('starting reset variables')
        self.y = df[self.pred_target]

    # i expect plot_y() only to be triggered when y changes   
    @param.depends('y', watch=True)
    def plot_y(self):
        logging.info('starting plot_y')
        self.y_plot = dynspread(datashade(self.y.hvplot.scatter()))
        return self.y_plot

model_trainer = ModelTrainer()

# show model dashboard
pn.Column(
    pn.Row(model_trainer.param['pred_target']),
    pn.Row(model_trainer.param['choose_model_type']),
    pn.Row(model_trainer.plot_y)
).servable()

dropdown_changes_dashboard_shouldnt_happen

The problem here is one of validation, specifically the issue is here: @param.depends('y', watch=True) . y is not a parameter in your example, therefore param.depends can't resolve it and ends up falling back to depending on all parameters. I've filed an issue to resolve this here . If you change your example to:

y = param.Series(default=df[pred_target.default])

It will work, however you will still have the issue with the callback being called twice. This is because you have set watch=True in the depends declaration. Setting watch=True only makes sense for methods that have side-effects, if your method is something that returns a value then it will rarely make sense to set it. To expand on that, when you pass the method to panel, eg pn.Row(model_trainer.plot_y) , it will automatically watch the parameters and call the method to update the plot when the parameters change.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM