Giter Club home page Giter Club logo

Comments (67)

oegedijk avatar oegedijk commented on May 18, 2024

Ah, yes, you need to add the buildpack through the heroku GUI. Just go to the settings page of your heroku project and there you should find the add buildpack option. THere you can drop in the https://github.com/niteoweb/heroku-buildpack-shell.git link and it should be added to your heroku slug.

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

also, you need to run pip uninstall -y xgboost instead of pip install...

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

(noticed that is actually a typo in the docs, just fixed it)

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Did you manage to get it to work? I opened an issue with dtreeviz here to make the xgboost dependency optional, so that in the future it should be less of a headache.

from explainerdashboard.

hkoppen avatar hkoppen commented on May 18, 2024

Unfortunately, I'm not quite there. Building worked (after adding the Python buildpack as well 👼 ), but the app itself crashes due to 2020-12-07T10:45:58.008591+00:00 app[web.1]: bash: gunicorn: command not found.
I am using

from unittest.mock import MagicMock
import sys
sys.modules["xgboost"] = MagicMock()

from explainerdashboard import ExplainerDashboard

app = ExplainerDashboard.from_config("dashboard_v3.yaml").flask_server()
server = app.server

if __name__ == '__main__':
    app.run_server()

and the procfile
web: gunicorn app:server
following this article. Do I have to adapt to explainerdashboards' architecture somehow?

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Ah, you need to add a requirements.txt file with:

explainerdashboard==0.2.13.2
gunicorn

from explainerdashboard.

hkoppen avatar hkoppen commented on May 18, 2024

Ok I forgot to mention the file, which was not contain gunicorn.

Now I am facing an en-/decoding error:

2020-12-07T11:25:14.091849+00:00 app[web.1]: [2020-12-07 11:25:14 +0000] [18] [ERROR] Exception in worker process
2020-12-07T11:25:14.091872+00:00 app[web.1]: Traceback (most recent call last):
2020-12-07T11:25:14.091873+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/arbiter.py", line 583, in spawn_worker
2020-12-07T11:25:14.091874+00:00 app[web.1]:     worker.init_process()
2020-12-07T11:25:14.091874+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/workers/base.py", line 119, in init_process
2020-12-07T11:25:14.091874+00:00 app[web.1]:     self.load_wsgi()
2020-12-07T11:25:14.091874+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/workers/base.py", line 144, in load_wsgi
2020-12-07T11:25:14.091875+00:00 app[web.1]:     self.wsgi = self.app.wsgi()
2020-12-07T11:25:14.091875+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/app/base.py", line 67, in wsgi
2020-12-07T11:25:14.091876+00:00 app[web.1]:     self.callable = self.load()
2020-12-07T11:25:14.091876+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 49, in load
2020-12-07T11:25:14.091876+00:00 app[web.1]:     return self.load_wsgiapp()
2020-12-07T11:25:14.091876+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 39, in load_wsgiapp
2020-12-07T11:25:14.091877+00:00 app[web.1]:     return util.import_app(self.app_uri)
2020-12-07T11:25:14.091877+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/util.py", line 358, in import_app
2020-12-07T11:25:14.091877+00:00 app[web.1]:     mod = importlib.import_module(module)
2020-12-07T11:25:14.091877+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/importlib/__init__.py", line 126, in import_module
2020-12-07T11:25:14.091878+00:00 app[web.1]:     return _bootstrap._gcd_import(name[level:], package, level)
2020-12-07T11:25:14.091878+00:00 app[web.1]:   File "<frozen importlib._bootstrap>", line 994, in _gcd_import
2020-12-07T11:25:14.091879+00:00 app[web.1]:   File "<frozen importlib._bootstrap>", line 971, in _find_and_load
2020-12-07T11:25:14.091879+00:00 app[web.1]:   File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
2020-12-07T11:25:14.091879+00:00 app[web.1]:   File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
2020-12-07T11:25:14.091879+00:00 app[web.1]:   File "<frozen importlib._bootstrap_external>", line 678, in exec_module
2020-12-07T11:25:14.091880+00:00 app[web.1]:   File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
2020-12-07T11:25:14.091880+00:00 app[web.1]:   File "/app/app.py", line 7, in <module>
2020-12-07T11:25:14.091880+00:00 app[web.1]:     app = ExplainerDashboard.from_config("dashboard_v3.yaml").flask_server()
2020-12-07T11:25:14.091881+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/explainerdashboard/dashboards.py", line 467, in from_config
2020-12-07T11:25:14.091881+00:00 app[web.1]:     explainer = BaseExplainer.from_file(config['dashboard']['explainerfile'])
2020-12-07T11:25:14.091881+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/explainerdashboard/explainers.py", line 189, in from_file
2020-12-07T11:25:14.091881+00:00 app[web.1]:     return joblib.load(filepath)
2020-12-07T11:25:14.091882+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/joblib/numpy_pickle.py", line 585, in load
2020-12-07T11:25:14.091882+00:00 app[web.1]:     obj = _unpickle(fobj, filename, mmap_mode)
2020-12-07T11:25:14.091882+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/joblib/numpy_pickle.py", line 504, in _unpickle
2020-12-07T11:25:14.091882+00:00 app[web.1]:     obj = unpickler.load()
2020-12-07T11:25:14.091882+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/pickle.py", line 1050, in load
2020-12-07T11:25:14.091883+00:00 app[web.1]:     dispatch[key[0]](self)
2020-12-07T11:25:14.091883+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/pickle.py", line 1398, in load_reduce
2020-12-07T11:25:14.091883+00:00 app[web.1]:     stack[-1] = func(*args)
2020-12-07T11:25:14.091883+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/numba/core/serialize.py", line 208, in _unpickle__CustomPickled
2020-12-07T11:25:14.091883+00:00 app[web.1]:     ctor, states = loads(serialized)
2020-12-07T11:25:14.091884+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/numba/core/serialize.py", line 112, in _rebuild_function
2020-12-07T11:25:14.091884+00:00 app[web.1]:     code = _rebuild_code(*code_reduced)
2020-12-07T11:25:14.091884+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/numba/core/serialize.py", line 132, in _rebuild_code
2020-12-07T11:25:14.091884+00:00 app[web.1]:     raise RuntimeError("incompatible bytecode version")
2020-12-07T11:25:14.091890+00:00 app[web.1]: RuntimeError: incompatible bytecode version
2020-12-07T11:25:14.093752+00:00 app[web.1]: [2020-12-07 11:25:14 +0000] [18] [INFO] Worker exiting (pid: 18)


2020-12-07T11:25:14.169747+00:00 app[web.1]: [2020-12-07 11:25:14 +0000] [10] [ERROR] Exception in worker process
2020-12-07T11:25:14.169749+00:00 app[web.1]: Traceback (most recent call last):
2020-12-07T11:25:14.169756+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/arbiter.py", line 583, in spawn_worker
2020-12-07T11:25:14.169757+00:00 app[web.1]:     worker.init_process()
2020-12-07T11:25:14.169757+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/workers/base.py", line 119, in init_process
2020-12-07T11:25:14.169757+00:00 app[web.1]:     self.load_wsgi()
2020-12-07T11:25:14.169758+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/workers/base.py", line 144, in load_wsgi
2020-12-07T11:25:14.169758+00:00 app[web.1]:     self.wsgi = self.app.wsgi()
2020-12-07T11:25:14.169759+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/app/base.py", line 67, in wsgi
2020-12-07T11:25:14.169759+00:00 app[web.1]:     self.callable = self.load()
2020-12-07T11:25:14.169760+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 49, in load
2020-12-07T11:25:14.169760+00:00 app[web.1]:     return self.load_wsgiapp()
2020-12-07T11:25:14.169760+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 39, in load_wsgiapp
2020-12-07T11:25:14.169761+00:00 app[web.1]:     return util.import_app(self.app_uri)
2020-12-07T11:25:14.169761+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/util.py", line 358, in import_app
2020-12-07T11:25:14.169762+00:00 app[web.1]:     mod = importlib.import_module(module)
2020-12-07T11:25:14.169762+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/importlib/__init__.py", line 126, in import_module
2020-12-07T11:25:14.169763+00:00 app[web.1]:     return _bootstrap._gcd_import(name[level:], package, level)
2020-12-07T11:25:14.169763+00:00 app[web.1]:   File "<frozen importlib._bootstrap>", line 994, in _gcd_import
2020-12-07T11:25:14.169764+00:00 app[web.1]:   File "<frozen importlib._bootstrap>", line 971, in _find_and_load
2020-12-07T11:25:14.169764+00:00 app[web.1]:   File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
2020-12-07T11:25:14.169765+00:00 app[web.1]:   File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
2020-12-07T11:25:14.169765+00:00 app[web.1]:   File "<frozen importlib._bootstrap_external>", line 678, in exec_module
2020-12-07T11:25:14.169765+00:00 app[web.1]:   File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
2020-12-07T11:25:14.169766+00:00 app[web.1]:   File "/app/app.py", line 7, in <module>
2020-12-07T11:25:14.169766+00:00 app[web.1]:     app = ExplainerDashboard.from_config("dashboard_v3.yaml").flask_server()
2020-12-07T11:25:14.169767+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/explainerdashboard/dashboards.py", line 467, in from_config
2020-12-07T11:25:14.169767+00:00 app[web.1]:     explainer = BaseExplainer.from_file(config['dashboard']['explainerfile'])
2020-12-07T11:25:14.169768+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/explainerdashboard/explainers.py", line 189, in from_file
2020-12-07T11:25:14.169768+00:00 app[web.1]:     return joblib.load(filepath)
2020-12-07T11:25:14.169768+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/joblib/numpy_pickle.py", line 585, in load
2020-12-07T11:25:14.169769+00:00 app[web.1]:     obj = _unpickle(fobj, filename, mmap_mode)
2020-12-07T11:25:14.169769+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/joblib/numpy_pickle.py", line 504, in _unpickle
2020-12-07T11:25:14.169770+00:00 app[web.1]:     obj = unpickler.load()
2020-12-07T11:25:14.169770+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/pickle.py", line 1050, in load
2020-12-07T11:25:14.169771+00:00 app[web.1]:     dispatch[key[0]](self)
2020-12-07T11:25:14.169771+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/pickle.py", line 1398, in load_reduce
2020-12-07T11:25:14.169771+00:00 app[web.1]:     stack[-1] = func(*args)
2020-12-07T11:25:14.169772+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/numba/core/serialize.py", line 208, in _unpickle__CustomPickled
2020-12-07T11:25:14.169772+00:00 app[web.1]:     ctor, states = loads(serialized)
2020-12-07T11:25:14.169778+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/numba/core/serialize.py", line 112, in _rebuild_function
2020-12-07T11:25:14.169778+00:00 app[web.1]:     code = _rebuild_code(*code_reduced)
2020-12-07T11:25:14.169778+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/numba/core/serialize.py", line 132, in _rebuild_code
2020-12-07T11:25:14.169779+00:00 app[web.1]:     raise RuntimeError("incompatible bytecode version")
2020-12-07T11:25:14.169779+00:00 app[web.1]: RuntimeError: incompatible bytecode version

from explainerdashboard.

hkoppen avatar hkoppen commented on May 18, 2024

Maybe it's because gunicorn is not designed for Windows and I should use waitress instead?

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Ah, then the python version on heroku and the one you used to pickle the explainer is probably not compatible (pickle does not guarantee unpickles across versions):

You can learn about setting runtime versions here and here

Basically you should add a runtime.txt file with the python version:

python-3.8.6

Supported versions are python-3.9.0, python-3.8.6, python-3.7.9 and python-3.6.12

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Maybe it's because gunicorn is not designed for Windows and I should use waitress instead?

Ah, yes. I don't know much about windows deployment (didnt even knew that heroku supported it to be honest :), but waitress is then probably the way to go. If you manage to get it to work could you let me know so that I can add instructions to the docs?

from explainerdashboard.

hkoppen avatar hkoppen commented on May 18, 2024

I managed to track down every bug/mistake and got it running with gunicorn :-)

For future reference, I'm using the following app.py:

from unittest.mock import MagicMock
import sys
sys.modules["xgboost"] = MagicMock()

from explainerdashboard import ExplainerDashboard

app = ExplainerDashboard.from_config("dashboard_v3.yaml").app
server = app.server

if __name__ == '__main__':
    app.run_server()

Using .flask_server() is somehow not so smart.

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

ah, really? That is strange, should be equivalent. db.flask_server() literally just returns self.app.server. Anyway, I will just use the db.app.server notation then in the documentation from now on. I've also clarified the deployment to heroku documentation a bit: https://explainerdashboard.readthedocs.io/en/latest/deployment.html#deploying-to-heroku Could you let me know if there is anything I should add to make the chance of accidental bugs smaller for future users?

Do you have a public link for the dashboard? Would be curious what you built.

from explainerdashboard.

hkoppen avatar hkoppen commented on May 18, 2024

Uh, never mind, I guess I put it into the wrong spot. The following works fine as well.

db = ExplainerDashboard.from_config("dashboard_v3.yaml")
app = db.app
server = db.flask_server()

The documentation looks great. Maybe you want to explain why you are using --preload --timeout 60 since it's strictly speaking not needed?

Sure, I will share it, but I have another problem: the plots are empty 😁 although I have loaded the data in the Github project. What could be the reason for this? Do you even need the data to be present when loading a dashboard from disk?

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Ah yeah: --preload is only needed when you have multiple workers. It assures that all the code is properly loaded for each worker, otherwise they somehow seem to get out of sync. The --timeout is the amount of time that the startup of your server is allowed to take, so when you have some expensive imports or calculations to be done before starting the dashboard, it can help to to increase this.

If the plots are empty, something is definitely broken: you would want to check the logs for the stacktrace. All the data should be contained in the explainer.joblib (or explainer.pkl or whatever). So you would need to have both the explainer.joblib (or .pkl) and the dashboard.yaml in the repo to launch the dashboard.

from explainerdashboard.

hkoppen avatar hkoppen commented on May 18, 2024

I have the yaml and the explainer-file in the repo. Locally, loading works fine, but on Heroku, the plots are empty. I can't find anything in the logs:

2020-12-09T16:27:58.344000+00:00 heroku[web.1]: Starting process with command `gunicorn app:server`
2020-12-09T16:28:00.564417+00:00 app[web.1]: [2020-12-09 16:28:00 +0000] [4] [INFO] Starting gunicorn 20.0.4
2020-12-09T16:28:00.564982+00:00 app[web.1]: [2020-12-09 16:28:00 +0000] [4] [INFO] Listening at: http://0.0.0.0:12619 (4)
2020-12-09T16:28:00.565101+00:00 app[web.1]: [2020-12-09 16:28:00 +0000] [4] [INFO] Using worker: sync
2020-12-09T16:28:00.569038+00:00 app[web.1]: [2020-12-09 16:28:00 +0000] [10] [INFO] Booting worker with pid: 10
2020-12-09T16:28:00.603277+00:00 app[web.1]: [2020-12-09 16:28:00 +0000] [11] [INFO] Booting worker with pid: 11
2020-12-09T16:28:00.977409+00:00 heroku[web.1]: State changed from starting to up
2020-12-09T16:28:11.766502+00:00 app[web.1]: Building ExplainerDashboard..
2020-12-09T16:28:11.769214+00:00 app[web.1]: Building ExplainerDashboard..
2020-12-09T16:28:11.778317+00:00 app[web.1]: Generating layout...
2020-12-09T16:28:11.781447+00:00 app[web.1]: Generating layout...
2020-12-09T16:28:11.795301+00:00 app[web.1]: Calculating dependencies...
2020-12-09T16:28:11.795376+00:00 app[web.1]: Registering callbacks...
2020-12-09T16:28:11.798934+00:00 app[web.1]: Calculating dependencies...
2020-12-09T16:28:11.799016+00:00 app[web.1]: Registering callbacks...
2020-12-09T16:28:59.000000+00:00 app[api]: Build succeeded
2020-12-09T16:30:33.057134+00:00 heroku[router]: at=info method=GET path="/" host=... request_id=6fd55fb9-c7f6-4981-b2c3-c0a8ebb43632 fwd="62.216.209.193" dyno=web.1 connect=1ms service=28ms status=200 bytes=973 protocol=https
2020-12-09T16:30:33.051409+00:00 app[web.1]: 10.14.25.48 - - [09/Dec/2020:16:30:33 +0000] "GET / HTTP/1.1" 200 765 "https://dashboard.heroku.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
2020-12-09T16:30:33.230979+00:00 app[web.1]: 10.35.77.100 - - [09/Dec/2020:16:30:33 +0000] "GET /_dash-component-suites/dash_renderer/[email protected]_8_3m1607531128.14.0.min.js HTTP/1.1" 200 4898 "https://.../" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
2020-12-09T16:30:33.236167+00:00 app[web.1]: 10.14.25.48 - - [09/Dec/2020:16:30:33 +0000] "GET /_dash-component-suites/dash_renderer/[email protected]_8_3m1607531128.8.7.min.js HTTP/1.1" 200 34243 "https://.../" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
2020-12-09T16:30:33.235239+00:00 heroku[router]: at=info method=GET path="/_dash-component-suites/dash_renderer/[email protected]_8_3m1607531128.14.0.min.js" host=... request_id=d6d60976-eb2a-41b6-9dfc-04c56486f20d fwd="62.216.209.193" dyno=web.1 connect=5ms service=11ms status=200 bytes=5153 protocol=https
2020-12-09T16:30:33.375435+00:00 heroku[router]: at=info method=GET path="/_dash-component-suites/dash_html_components/dash_html_components.v1_1_1m1607531134.min.js" host=... request_id=60ba5ea1-0689-4227-aa4d-7f9116124bb9 fwd="62.216.209.193" dyno=web.1 connect=4ms service=6ms status=200 bytes=19163 protocol=https
2020-12-09T16:30:33.357114+00:00 heroku[router]: at=info method=GET path="/_dash-component-suites/dash_core_components/dash_core_components-shared.v1_13_0m1607531133.js" host=... request_id=4ac3b6c0-2002-4b08-9470-b26216374cea fwd="62.216.209.193" dyno=web.1 connect=7ms service=6ms status=200 bytes=9940 protocol=https
2020-12-09T16:30:33.362676+00:00 heroku[router]: at=info method=GET path="/_dash-component-suites/dash_core_components/dash_core_components.v1_13_0m1607531133.min.js" host=... request_id=9df2dd92-7f4c-43db-adc4-00cf14ff37d9 fwd="62.216.209.193" dyno=web.1 connect=4ms service=24ms status=200 bytes=119092 protocol=https
2020-12-09T16:30:33.402190+00:00 heroku[router]: at=info method=GET path="/_dash-component-suites/dash_bootstrap_components/_components/dash_bootstrap_components.v0_10_7m1607531131.min.js" host=... request_id=def225a7-91a2-4f8c-8776-5102851f66cf fwd="62.216.209.193" dyno=web.1 connect=1ms service=14ms status=200 bytes=52759 protocol=https
2020-12-09T16:30:33.244014+00:00 heroku[router]: at=info method=GET path="/_dash-component-suites/dash_renderer/[email protected]_8_3m1607531128.8.7.min.js" host=... request_id=6e91a5c4-2867-489c-81bb-fecc106db2ed fwd="62.216.209.193" dyno=web.1 connect=2ms service=27ms status=200 bytes=34499 protocol=https
2020-12-09T16:30:33.342814+00:00 heroku[router]: at=info method=GET path="/_dash-component-suites/dash_table/bundle.v4_11_0m1607531132.js" host=... request_id=c08e9a18-2af6-4d16-9c9c-11264040ad02 fwd="62.216.209.193" dyno=web.1 connect=1ms service=5ms status=200 bytes=11269 protocol=https
2020-12-09T16:30:33.307532+00:00 app[web.1]: 10.35.77.100 - - [09/Dec/2020:16:30:33 +0000] "GET /_dash-component-suites/dash_renderer/[email protected]_8_3m1607531128.14.0.min.js HTTP/1.1" 200 38049 "https://.../" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
2020-12-09T16:30:33.329677+00:00 app[web.1]: 10.39.236.138 - - [09/Dec/2020:16:30:33 +0000] "GET /_dash-component-suites/dash_renderer/[email protected]_8_3m1607531128.7.2.min.js HTTP/1.1" 200 832 "https://.../" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
2020-12-09T16:30:33.342219+00:00 app[web.1]: 10.10.227.188 - - [09/Dec/2020:16:30:33 +0000] "GET /_dash-component-suites/dash_table/bundle.v4_11_0m1607531132.js HTTP/1.1" 200 11013 "https://.../" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
2020-12-09T16:30:33.353983+00:00 app[web.1]: 10.11.191.99 - - [09/Dec/2020:16:30:33 +0000] "GET /_dash-component-suites/dash_core_components/dash_core_components-shared.v1_13_0m1607531133.js HTTP/1.1" 200 9685 "https://.../" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
2020-12-09T16:30:33.355899+00:00 app[web.1]: 10.10.88.48 - - [09/Dec/2020:16:30:33 +0000] "GET /_dash-component-suites/dash_core_components/dash_core_components.v1_13_0m1607531133.min.js HTTP/1.1" 200 118835 "https://.../" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
2020-12-09T16:30:33.371658+00:00 app[web.1]: 10.14.25.48 - - [09/Dec/2020:16:30:33 +0000] "GET /_dash-component-suites/dash_html_components/dash_html_components.v1_1_1m1607531134.min.js HTTP/1.1" 200 18907 "https://.../" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
2020-12-09T16:30:33.396720+00:00 app[web.1]: 10.39.236.138 - - [09/Dec/2020:16:30:33 +0000] "GET /_dash-component-suites/dash_bootstrap_components/_components/dash_bootstrap_components.v0_10_7m1607531131.min.js HTTP/1.1" 200 52503 "https://.../" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
2020-12-09T16:30:33.425132+00:00 app[web.1]: 10.10.227.188 - - [09/Dec/2020:16:30:33 +0000] "GET /_dash-component-suites/dash_renderer/dash_renderer.v1_8_3m1607531128.min.js HTTP/1.1" 200 59285 "https://.../" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
2020-12-09T16:30:33.311666+00:00 heroku[router]: at=info method=GET path="/_dash-component-suites/dash_renderer/[email protected]_8_3m1607531128.14.0.min.js" host=... request_id=6ffa561e-09ac-485e-b804-4881c6a82682 fwd="62.216.209.193" dyno=web.1 connect=7ms service=9ms status=200 bytes=38305 protocol=https
2020-12-09T16:30:33.330282+00:00 heroku[router]: at=info method=GET path="/_dash-component-suites/dash_renderer/[email protected]_8_3m1607531128.7.2.min.js" host=... request_id=faee708b-efb1-4a18-9c66-9e3ccc437a23 fwd="62.216.209.193" dyno=web.1 connect=1ms service=2ms status=200 bytes=1086 protocol=https
2020-12-09T16:30:33.427968+00:00 heroku[router]: at=info method=GET path="/_dash-component-suites/dash_renderer/dash_renderer.v1_8_3m1607531128.min.js" host=... request_id=ba2d4b1e-dade-43ec-ba46-cd064861c039 fwd="62.216.209.193" dyno=web.1 connect=5ms service=13ms status=200 bytes=59541 protocol=https
2020-12-09T16:30:33.716655+00:00 heroku[router]: at=info method=GET path="/_dash-layout" host=... request_id=f3f91682-0f88-4e52-92c6-d2c034867c14 fwd="62.216.209.193" dyno=web.1 connect=2ms service=27ms status=200 bytes=131727 protocol=https
2020-12-09T16:30:33.694495+00:00 app[web.1]: 10.39.236.138 - - [09/Dec/2020:16:30:33 +0000] "GET /_dash-dependencies HTTP/1.1" 200 730 "https://.../" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
2020-12-09T16:30:33.711493+00:00 app[web.1]: 10.10.88.48 - - [09/Dec/2020:16:30:33 +0000] "GET /_dash-layout HTTP/1.1" 200 131524 "https://.../" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
2020-12-09T16:30:33.696281+00:00 heroku[router]: at=info method=GET path="/_dash-dependencies" host=... request_id=4b5d7967-3082-4aa2-9f44-57819bd9665e fwd="62.216.209.193" dyno=web.1 connect=4ms service=4ms status=200 bytes=930 protocol=https

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Hmm, that is quite strange. Two potential reasons:

  1. somehow no callbacks at all are triggered/firing. Could you check if other interaction functionality on the dashboard works (e.g. group cats should change the features in the dropdown, etc)
  2. The stacktrace of the failed callbacks somehow do not show up in the log (perhaps looking at the wrong log, or not the right filter settings?)

from explainerdashboard.

hkoppen avatar hkoppen commented on May 18, 2024

Group cats is disabled atm, but choosing/changing an index in FeatureInputComponent does not show/change anything as well.
Logs - I'm using More > View Logs in Heroku next to Open App where I can only filter for web processes (which I'm not), are there any alternatives to view the logs?

from explainerdashboard.

hkoppen avatar hkoppen commented on May 18, 2024

I tried loading the explainer file only, building the dashboard in app.py - no difference. I also tried constructing the dashboard it self in app.py, which is not working due to memory limitations on Heroku. My next & last idea is to deploy one of your Titanic dashboards ...

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Yeah, probably handy to first test something that you know should work:

generate_dashboard.py:

from explainerdashboard import ClassifierExplainer, ExplainerDashboard
from explainerdashboard.custom import *

explainer = ClassifierExplainer(model, X_test, y_test)

# building an ExplainerDashboard ensures that all necessary properties 
# get calculated:
db = ExplainerDashboard(explainer, [ShapDependenceComposite, WhatIfComposite],
                        title='Awesome Dashboard', hide_whatifpdp=True)

# store both the explainer and the dashboard configuration:
explainer.dump("explainer.joblib")
db.to_yaml("dashboard.yaml")

Now run python generate_dashboard.py

dashboard.py:

from explainerdashboard import ClassifierExplainer, ExplainerDashboard

explainer = ClassifierExplainer.from_file("explainer.joblib")
# you can override params during load from_config:
db = ExplainerDashboard.from_config(explainer, "dashboard.yaml", title="Awesomer Title")

app = db.flask_server()

Now run gunicorn dashboard:app.

If it works, push the repo and see if it works on heroku as well.

from explainerdashboard.

hkoppen avatar hkoppen commented on May 18, 2024

Locally it works, but the result is https://hk-db-test.herokuapp.com/. :(
(Note that hide_whatifpdp=True is not doing anything)

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Hmm, weird. Do you maybe have the dashboard linked to a public github repo so that I can fork it and see if I can get it to work?

from explainerdashboard.

hkoppen avatar hkoppen commented on May 18, 2024

Yup, it's here: https://github.com/hkoppen/Dashboard_Test

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Found the problem (now just need to find the solution):

(Btw, if you install the papertrail add-on (it's for free), you can see real time logs including stacktraces like below to help you debug)

Dec 15 02:06:02 db-test-oege app/web.1 Exception on /_dash-update-component [POST]
Dec 15 02:06:02 db-test-oege app/web.1 Traceback (most recent call last):
Dec 15 02:06:02 db-test-oege app/web.1   File "/app/.heroku/python/lib/python3.7/site-packages/dash/dash.py", line 1072, in dispatch
Dec 15 02:06:02 db-test-oege app/web.1     func = self.callback_map[output]["callback"]
Dec 15 02:06:02 db-test-oege app/web.1 KeyError: 'pdp-graph-AGdDw6dDQk.figure'
Dec 15 02:06:02 db-test-oege app/web.1 During handling of the above exception, another exception occurred:
Dec 15 02:06:02 db-test-oege app/web.1 Traceback (most recent call last):
Dec 15 02:06:02 db-test-oege app/web.1   File "/app/.heroku/python/lib/python3.7/site-packages/flask/app.py", line 2447, in wsgi_app
Dec 15 02:06:02 db-test-oege app/web.1     response = self.full_dispatch_request()
Dec 15 02:06:02 db-test-oege app/web.1   File "/app/.heroku/python/lib/python3.7/site-packages/flask/app.py", line 1952, in full_dispatch_request
Dec 15 02:06:02 db-test-oege app/web.1     rv = self.handle_user_exception(e)
Dec 15 02:06:02 db-test-oege app/web.1   File "/app/.heroku/python/lib/python3.7/site-packages/flask/app.py", line 1821, in handle_user_exception
Dec 15 02:06:02 db-test-oege app/web.1     reraise(exc_type, exc_value, tb)
Dec 15 02:06:02 db-test-oege app/web.1   File "/app/.heroku/python/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
Dec 15 02:06:02 db-test-oege app/web.1     raise value
Dec 15 02:06:02 db-test-oege app/web.1   File "/app/.heroku/python/lib/python3.7/site-packages/flask/app.py", line 1950, in full_dispatch_request
Dec 15 02:06:02 db-test-oege app/web.1     rv = self.dispatch_request()
Dec 15 02:06:02 db-test-oege app/web.1   File "/app/.heroku/python/lib/python3.7/site-packages/flask/app.py", line 1936, in dispatch_request
Dec 15 02:06:02 db-test-oege app/web.1     return self.view_functions[rule.endpoint](**req.view_args)
Dec 15 02:06:02 db-test-oege app/web.1   File "/app/.heroku/python/lib/python3.7/site-packages/dash/dash.py", line 1075, in dispatch
Dec 15 02:06:02 db-test-oege app/web.1     raise KeyError(msg.format(output))
Dec 15 02:06:02 db-test-oege app/web.1 KeyError: "Callback function not found for output 'pdp-graph-AGdDw6dDQk.figure', perhaps you forgot to prepend the '@'?"

So the callbacks are not working for some reason (hence why you don't see the graphs, nor does the random index button work)...

I will have to investigate why, but it is very odd that it works for the titanicexplainer.herokuapp.com deployment but not for this one. Hmm.

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Got it: change Procfile to:

web: gunicorn --preload dashboard:app

Not sure exactly what is so magical about preload (not a gunicorn expert), but if you don't add it all kind of weird stuff starts to happen.

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

http://db-test-oege.herokuapp.com/

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

I think I have an idea what is going one:

https://docs.gunicorn.org/en/stable/settings.html:

preload_app
--preload
False
Load application code before the worker processes are forked.

In order to make sure that all dash elements are unique, I add a random uuid string at the end of each component id. This makes it so that you can include multiple ExplainerComponents of the same type in the same layout, as they will all have unique ids and callbacks. However if you do not pass --preload then each gunicorn worker instantiates its own app with its own random uuid strings at the end of components! So then the different workers do not agree on the names of the ids and the callbacks fail.

Will add a clearer warning to the docs that --preload is essential...

from explainerdashboard.

hkoppen avatar hkoppen commented on May 18, 2024

Yup, that's it. Damn it, we talked about it exactly 7 days ago!

Edit: Now I can move on to deploy the app via Docker ;-)

from explainerdashboard.

carlryn avatar carlryn commented on May 18, 2024

I'm having the same error about callbacks not found. The --preload option solves for running from one cotainer with gunicorn, but when scaling with docker swarm the error shows again.
Should this be expected? :) Anything I can do to make it work? :)

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Ahh, have never tried docker swarm, but could be the same issue: each docker container initializes the component ids with different random uuid, and so they do not agree on the id's of the components.

The way to get around that is by simply passing every component explicit name parameters.
Something that is easy to add for your own custom layouts, but I could also do it for the default Composites.

Could you perhaps try to get the following to run during docker swarm? So I set the name of the ImportancesComponent to "0" and
pass block_selector_callbacks=True to the ExplainerDashboard.

class ImportancesCompositeWithName(ExplainerComponent):
    def __init__(self, explainer):
        super().__init__(explainer)

        self.importances = ImportancesComponent(explainer, name="0")
        self.register_components()

    def layout(self):
        return html.Div([self.importances.layout()])

db = ExplainerDashboard(explainer, ImportancesCompositeWithName, block_selector_callbacks=True)
db.run()

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Figured out a way to make component names deterministic without too much hassle, try the new release: https://github.com/oegedijk/explainerdashboard/releases/tag/v0.2.16.1

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

hmm, heroku deployment still fails without --preload even with 0.2.16.1, so not sure it fixed the issue for docker swarm. But worth a try!

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

in any case this should make it easier to introduce url querystrings later!

from explainerdashboard.

carlryn avatar carlryn commented on May 18, 2024

Thanks for such as quick response and update. The error remains though.

I deployed it with the new version from pip (0.2.16.1). Double checked the logs so the correct version was installed.

 ERROR Exception on /dashboard/_dash-update-component [POST]
Traceback (most recent call last):
  File "/opt/venv/lib/python3.7/site-packages/dash/dash.py", line 1072, in dispatch
    func = self.callback_map[output]["callback"]
KeyError: '..contributions-table-5f9XA4.children...contributions-table-depth-5f9XA4.options..'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/opt/venv/lib/python3.7/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "/opt/venv/lib/python3.7/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/opt/venv/lib/python3.7/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/opt/venv/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/opt/venv/lib/python3.7/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "/opt/venv/lib/python3.7/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/opt/venv/lib/python3.7/site-packages/dash/dash.py", line 1075, in dispatch
    raise KeyError(msg.format(output))
KeyError: "Callback function not found for output '..contributions-table-5f9XA4.children...contributions-table-depth-5f9XA4.options..', perhaps you forgot to prepend the '@'?"

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

I still see a uuid name: the 5f9XA4 in contributions-table-5f9XA4.

Is this a custom layout or a default dashboard?

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Ah, I guess you're probably using ExplainerDashboard.from_config()? I realize I had not adjusted that code yet, so the composites (tabs) still get random uuid names when you load it that way. Will have a look at a fix...

from explainerdashboard.

carlryn avatar carlryn commented on May 18, 2024

Yes, I am using the ExplainerDashboard.from_config().
Assuming this will contain the changes?
ExplainerDashboard(explainer, ...)

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Yes, that should work. Need to still make sure I store the component name and load it correctly with from_config. Next release.

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

So with this latest release (https://github.com/oegedijk/explainerdashboard/releases/tag/v0.2.16.2), it should also work with .from_config(), at least with the default Composites. When you write your own custom components, you still need to make sure that the subcomponents get some definite and unique name parameter. Will think of a way to autodetect when uuid names get created so that I can issue warnings.

Also tested deploying to heroku without the --preload parameter and that seems to work as well now: https://db-test-oege.herokuapp.com/

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Could you test it on your docker swarm? I'm actually also curious why you need to deploy on a docker swarm, do you have that many users simultaneously, or is it just that all dashboard by default are deployed to swarms at your organization?

from explainerdashboard.

moeller84 avatar moeller84 commented on May 18, 2024

Could you test it on your docker swarm? I'm actually also curious why you need to deploy on a docker swarm, do you have that many users simultaneously, or is it just that all dashboard by default are deployed to swarms at your organization?

Hi, it is still not working. All our dashboards are deployed by default via docker. It seems like it still assigns uuid names to callbacks. Could it have something to do with running the dashboard through gunicorn and wsgi?

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Is this the default dashboard or did you make your own custom dashboards?

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

So for example, when I call:

db = ExplainerDashboard(explainer)
list(db.app.callback_map.values())

I get the following output, where you can see that all the callback id's end with two digits ('10', '23', etc) instead of a uuid string of length 5.

Do you see the same?

[{'inputs': [{'id': 'importances-depth-10', 'property': 'value'},
   {'id': 'importances-group-cats-10', 'property': 'value'},
   {'id': 'importances-permutation-or-shap-10', 'property': 'value'},
   {'id': 'pos-label-10', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.overview_components.ImportancesComponent.component_callbacks.<locals>.update_importances(depth, cats, permutation_shap, pos_label)>},
 {'inputs': [{'id': 'clas-model-summary-cutoff-20', 'property': 'value'},
   {'id': 'pos-label-20', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.ClassifierModelSummaryComponent.component_callbacks.<locals>.update_classifier_summary(cutoff, pos_label)>},
 {'inputs': [{'id': 'precision-binsize-or-quantiles-21', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.PrecisionComponent.component_callbacks.<locals>.update_div_visibility(bins_or_quantiles)>},
 {'inputs': [{'id': 'precision-binsize-21', 'property': 'value'},
   {'id': 'precision-quantiles-21', 'property': 'value'},
   {'id': 'precision-binsize-or-quantiles-21', 'property': 'value'},
   {'id': 'precision-cutoff-21', 'property': 'value'},
   {'id': 'precision-multiclass-21', 'property': 'value'},
   {'id': 'pos-label-21', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.PrecisionComponent.component_callbacks.<locals>.update_precision_graph(bin_size, quantiles, bins, cutoff, multiclass, pos_label)>},
 {'inputs': [{'id': 'confusionmatrix-cutoff-22', 'property': 'value'},
   {'id': 'confusionmatrix-percentage-22', 'property': 'value'},
   {'id': 'confusionmatrix-binary-22', 'property': 'value'},
   {'id': 'pos-label-22', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.ConfusionMatrixComponent.component_callbacks.<locals>.update_confusionmatrix_graph(cutoff, normalized, binary, pos_label)>},
 {'inputs': [{'id': 'cumulative-precision-percentile-23', 'property': 'value'},
   {'id': 'pos-label-23', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.CumulativePrecisionComponent.component_callbacks.<locals>.update_cumulative_precision_graph(percentile, pos_label)>},
 {'inputs': [{'id': 'cumulative-precision-cutoff-23', 'property': 'value'},
   {'id': 'pos-label-23', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.CumulativePrecisionComponent.component_callbacks.<locals>.update_cumulative_precision_percentile(cutoff, pos_label)>},
 {'inputs': [{'id': 'liftcurve-cutoff-24', 'property': 'value'},
   {'id': 'liftcurve-percentage-24', 'property': 'value'},
   {'id': 'pos-label-24', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.LiftCurveComponent.component_callbacks.<locals>.update_precision_graph(cutoff, percentage, pos_label)>},
 {'inputs': [{'id': 'classification-cutoff-25', 'property': 'value'},
   {'id': 'classification-percentage-25', 'property': 'value'},
   {'id': 'pos-label-25', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.ClassificationComponent.component_callbacks.<locals>.update_precision_graph(cutoff, percentage, pos_label)>},
 {'inputs': [{'id': 'rocauc-cutoff-26', 'property': 'value'},
   {'id': 'pos-label-26', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.RocAucComponent.component_callbacks.<locals>.update_precision_graph(cutoff, pos_label)>},
 {'inputs': [{'id': 'prauc-cutoff-27', 'property': 'value'},
   {'id': 'pos-label-27', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.PrAucComponent.component_callbacks.<locals>.update_precision_graph(cutoff, pos_label)>},
 {'inputs': [{'id': 'cutoffconnector-percentile-28', 'property': 'value'},
   {'id': 'pos-label-28', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.connectors.CutoffPercentileComponent.component_callbacks.<locals>.update_cutoff(percentile, pos_label)>},
 {'inputs': [{'id': 'cutoffconnector-cutoff-28', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.connectors.CutoffConnector.component_callbacks.<locals>.update_cutoffs(cutoff)>},
 {'inputs': [{'id': 'random-index-clas-button-30', 'property': 'n_clicks'}],
  'state': [{'id': 'random-index-clas-slider-30', 'property': 'value'},
   {'id': 'random-index-clas-labels-30', 'property': 'value'},
   {'id': 'random-index-clas-pred-or-perc-30', 'property': 'value'},
   {'id': 'pos-label-30', 'property': 'value'}],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.ClassifierRandomIndexComponent.component_callbacks.<locals>.update_index(n_clicks, slider_range, labels, pred_or_perc, pos_label)>},
 {'inputs': [{'id': 'random-index-clas-pred-or-perc-30', 'property': 'value'},
   {'id': 'pos-label-30', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.ClassifierRandomIndexComponent.component_callbacks.<locals>.update_slider_label(pred_or_perc, pos_label)>},
 {'inputs': [{'id': 'clas-prediction-index-31', 'property': 'value'},
   {'id': 'pos-label-31', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.ClassifierPredictionSummaryComponent.component_callbacks.<locals>.update_output_div(index, pos_label)>},
 {'inputs': [{'id': 'contributions-graph-index-32', 'property': 'value'},
   {'id': 'contributions-graph-depth-32', 'property': 'value'},
   {'id': 'contributions-graph-sorting-32', 'property': 'value'},
   {'id': 'contributions-graph-orientation-32', 'property': 'value'},
   {'id': 'contributions-graph-group-cats-32', 'property': 'value'},
   {'id': 'pos-label-32', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.shap_components.ShapContributionsGraphComponent.component_callbacks.<locals>.update_output_div(index, depth, sort, orientation, cats, pos_label)>},
 {'inputs': [{'id': 'pdp-group-cats-33', 'property': 'value'}],
  'state': [{'id': 'pos-label-33', 'property': 'value'}],
  'callback': <function explainerdashboard.dashboard_components.overview_components.PdpComponent.component_callbacks.<locals>.update_pdp_graph(cats, pos_label)>},
 {'inputs': [{'id': 'pdp-index-33', 'property': 'value'},
   {'id': 'pdp-col-33', 'property': 'value'},
   {'id': 'pdp-dropna-33', 'property': 'value'},
   {'id': 'pdp-sample-33', 'property': 'value'},
   {'id': 'pdp-gridlines-33', 'property': 'value'},
   {'id': 'pdp-gridpoints-33', 'property': 'value'},
   {'id': 'pos-label-33', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.overview_components.PdpComponent.component_callbacks.<locals>.update_pdp_graph(index, col, drop_na, sample, gridlines, gridpoints, pos_label)>},
 {'inputs': [{'id': 'contributions-table-index-34', 'property': 'value'},
   {'id': 'contributions-table-depth-34', 'property': 'value'},
   {'id': 'contributions-table-sorting-34', 'property': 'value'},
   {'id': 'contributions-table-group-cats-34', 'property': 'value'},
   {'id': 'pos-label-34', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.shap_components.ShapContributionsTableComponent.component_callbacks.<locals>.update_output_div(index, depth, sort, cats, pos_label)>},
 {'inputs': [{'id': 'random-index-clas-index-30', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.connectors.IndexConnector.component_callbacks.<locals>.update_indexes(index)>},
 {'inputs': [{'id': 'feature-input-index-40', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.overview_components.FeatureInputComponent.component_callbacks.<locals>.update_whatif_inputs(index)>},
 {'inputs': [{'id': 'random-index-clas-button-41', 'property': 'n_clicks'}],
  'state': [{'id': 'random-index-clas-slider-41', 'property': 'value'},
   {'id': 'random-index-clas-labels-41', 'property': 'value'},
   {'id': 'random-index-clas-pred-or-perc-41', 'property': 'value'},
   {'id': 'pos-label-41', 'property': 'value'}],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.ClassifierRandomIndexComponent.component_callbacks.<locals>.update_index(n_clicks, slider_range, labels, pred_or_perc, pos_label)>},
 {'inputs': [{'id': 'random-index-clas-pred-or-perc-41', 'property': 'value'},
   {'id': 'pos-label-41', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.ClassifierRandomIndexComponent.component_callbacks.<locals>.update_slider_label(pred_or_perc, pos_label)>},
 {'inputs': [{'id': 'pos-label-42', 'property': 'value'},
   {'id': 'feature-input-Sex-input-40', 'property': 'value'},
   {'id': 'feature-input-Deck-input-40', 'property': 'value'},
   {'id': 'feature-input-PassengerClass-input-40', 'property': 'value'},
   {'id': 'feature-input-Fare-input-40', 'property': 'value'},
   {'id': 'feature-input-Embarked-input-40', 'property': 'value'},
   {'id': 'feature-input-Age-input-40', 'property': 'value'},
   {'id': 'feature-input-No_of_siblings_plus_spouses_on_board-input-40',
    'property': 'value'},
   {'id': 'feature-input-No_of_parents_plus_children_on_board-input-40',
    'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.ClassifierPredictionSummaryComponent.component_callbacks.<locals>.update_output_div(pos_label, *inputs)>},
 {'inputs': [{'id': 'contributions-graph-depth-43', 'property': 'value'},
   {'id': 'contributions-graph-sorting-43', 'property': 'value'},
   {'id': 'contributions-graph-orientation-43', 'property': 'value'},
   {'id': 'contributions-graph-group-cats-43', 'property': 'value'},
   {'id': 'pos-label-43', 'property': 'value'},
   {'id': 'feature-input-Sex-input-40', 'property': 'value'},
   {'id': 'feature-input-Deck-input-40', 'property': 'value'},
   {'id': 'feature-input-PassengerClass-input-40', 'property': 'value'},
   {'id': 'feature-input-Fare-input-40', 'property': 'value'},
   {'id': 'feature-input-Embarked-input-40', 'property': 'value'},
   {'id': 'feature-input-Age-input-40', 'property': 'value'},
   {'id': 'feature-input-No_of_siblings_plus_spouses_on_board-input-40',
    'property': 'value'},
   {'id': 'feature-input-No_of_parents_plus_children_on_board-input-40',
    'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.shap_components.ShapContributionsGraphComponent.component_callbacks.<locals>.update_output_div(depth, sort, orientation, cats, pos_label, *inputs)>},
 {'inputs': [{'id': 'contributions-table-depth-44', 'property': 'value'},
   {'id': 'contributions-table-sorting-44', 'property': 'value'},
   {'id': 'contributions-table-group-cats-44', 'property': 'value'},
   {'id': 'pos-label-44', 'property': 'value'},
   {'id': 'feature-input-Sex-input-40', 'property': 'value'},
   {'id': 'feature-input-Deck-input-40', 'property': 'value'},
   {'id': 'feature-input-PassengerClass-input-40', 'property': 'value'},
   {'id': 'feature-input-Fare-input-40', 'property': 'value'},
   {'id': 'feature-input-Embarked-input-40', 'property': 'value'},
   {'id': 'feature-input-Age-input-40', 'property': 'value'},
   {'id': 'feature-input-No_of_siblings_plus_spouses_on_board-input-40',
    'property': 'value'},
   {'id': 'feature-input-No_of_parents_plus_children_on_board-input-40',
    'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.shap_components.ShapContributionsTableComponent.component_callbacks.<locals>.update_output_div(depth, sort, cats, pos_label, *inputs)>},
 {'inputs': [{'id': 'pdp-group-cats-45', 'property': 'value'}],
  'state': [{'id': 'pos-label-45', 'property': 'value'}],
  'callback': <function explainerdashboard.dashboard_components.overview_components.PdpComponent.component_callbacks.<locals>.update_pdp_graph(cats, pos_label)>},
 {'inputs': [{'id': 'pdp-col-45', 'property': 'value'},
   {'id': 'pdp-dropna-45', 'property': 'value'},
   {'id': 'pdp-sample-45', 'property': 'value'},
   {'id': 'pdp-gridlines-45', 'property': 'value'},
   {'id': 'pdp-gridpoints-45', 'property': 'value'},
   {'id': 'pos-label-45', 'property': 'value'},
   {'id': 'feature-input-Sex-input-40', 'property': 'value'},
   {'id': 'feature-input-Deck-input-40', 'property': 'value'},
   {'id': 'feature-input-PassengerClass-input-40', 'property': 'value'},
   {'id': 'feature-input-Fare-input-40', 'property': 'value'},
   {'id': 'feature-input-Embarked-input-40', 'property': 'value'},
   {'id': 'feature-input-Age-input-40', 'property': 'value'},
   {'id': 'feature-input-No_of_siblings_plus_spouses_on_board-input-40',
    'property': 'value'},
   {'id': 'feature-input-No_of_parents_plus_children_on_board-input-40',
    'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.overview_components.PdpComponent.component_callbacks.<locals>.update_pdp_graph(col, drop_na, sample, gridlines, gridpoints, pos_label, *inputs)>},
 {'inputs': [{'id': 'random-index-clas-index-41', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.connectors.IndexConnector.component_callbacks.<locals>.update_indexes(index)>},
 {'inputs': [{'id': 'shap-summary-graph-50', 'property': 'clickData'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.shap_components.ShapSummaryComponent.component_callbacks.<locals>.display_scatter_click_data(clickdata)>},
 {'inputs': [{'id': 'shap-summary-type-50', 'property': 'value'},
   {'id': 'shap-summary-group-cats-50', 'property': 'value'},
   {'id': 'shap-summary-depth-50', 'property': 'value'},
   {'id': 'shap-summary-index-50', 'property': 'value'},
   {'id': 'pos-label-50', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.shap_components.ShapSummaryComponent.component_callbacks.<locals>.update_shap_summary_graph(summary_type, cats, depth, index, pos_label)>},
 {'inputs': [{'id': 'shap-dependence-col-51', 'property': 'value'}],
  'state': [{'id': 'shap-dependence-group-cats-51', 'property': 'value'},
   {'id': 'pos-label-51', 'property': 'value'}],
  'callback': <function explainerdashboard.dashboard_components.shap_components.ShapDependenceComponent.component_callbacks.<locals>.set_color_col_dropdown(col, cats, pos_label)>},
 {'inputs': [{'id': 'shap-dependence-color-col-51', 'property': 'value'},
   {'id': 'shap-dependence-index-51', 'property': 'value'},
   {'id': 'pos-label-51', 'property': 'value'}],
  'state': [{'id': 'shap-dependence-col-51', 'property': 'value'}],
  'callback': <function explainerdashboard.dashboard_components.shap_components.ShapDependenceComponent.component_callbacks.<locals>.update_dependence_graph(color_col, index, pos_label, col)>},
 {'inputs': [{'id': 'shap-dependence-group-cats-51', 'property': 'value'}],
  'state': [{'id': 'shap-dependence-col-51', 'property': 'value'}],
  'callback': <function explainerdashboard.dashboard_components.shap_components.ShapDependenceComponent.component_callbacks.<locals>.update_dependence_shap_scatter_graph(cats, old_col)>},
 {'inputs': [{'id': 'shap-summary-group-cats-50', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.shap_components.ShapSummaryDependenceConnector.component_callbacks.<locals>.update_dependence_shap_scatter_graph(cats)>},
 {'inputs': [{'id': 'shap-summary-graph-50', 'property': 'clickData'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.shap_components.ShapSummaryDependenceConnector.component_callbacks.<locals>.display_scatter_click_data(clickdata)>},
 {'inputs': [{'id': 'interaction-summary-graph-60', 'property': 'clickData'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.shap_components.InteractionSummaryComponent.component_callbacks.<locals>.display_scatter_click_data(clickdata)>},
 {'inputs': [{'id': 'interaction-summary-group-cats-60', 'property': 'value'},
   {'id': 'pos-label-60', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.shap_components.InteractionSummaryComponent.component_callbacks.<locals>.update_interaction_scatter_graph(cats, pos_label)>},
 {'inputs': [{'id': 'interaction-summary-col-60', 'property': 'value'},
   {'id': 'interaction-summary-depth-60', 'property': 'value'},
   {'id': 'interaction-summary-type-60', 'property': 'value'},
   {'id': 'interaction-summary-index-60', 'property': 'value'},
   {'id': 'pos-label-60', 'property': 'value'},
   {'id': 'interaction-summary-group-cats-60', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.shap_components.InteractionSummaryComponent.component_callbacks.<locals>.update_interaction_scatter_graph(col, depth, summary_type, index, pos_label, cats)>},
 {'inputs': [{'id': 'interaction-dependence-group-cats-61',
    'property': 'value'},
   {'id': 'pos-label-61', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.shap_components.InteractionDependenceComponent.component_callbacks.<locals>.update_interaction_dependence_interact_col(cats, pos_label)>},
 {'inputs': [{'id': 'interaction-dependence-col-61', 'property': 'value'},
   {'id': 'pos-label-61', 'property': 'value'}],
  'state': [{'id': 'interaction-dependence-group-cats-61',
    'property': 'value'},
   {'id': 'interaction-dependence-interact-col-61', 'property': 'value'}],
  'callback': <function explainerdashboard.dashboard_components.shap_components.InteractionDependenceComponent.component_callbacks.<locals>.update_interaction_dependence_interact_col(col, pos_label, cats, old_interact_col)>},
 {'inputs': [{'id': 'interaction-dependence-interact-col-61',
    'property': 'value'},
   {'id': 'interaction-dependence-index-61', 'property': 'value'},
   {'id': 'pos-label-61', 'property': 'value'},
   {'id': 'interaction-dependence-col-61', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.shap_components.InteractionDependenceComponent.component_callbacks.<locals>.update_dependence_graph(interact_col, index, pos_label, col)>},
 {'inputs': [{'id': 'interaction-summary-group-cats-60', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.shap_components.InteractionSummaryDependenceConnector.component_callbacks.<locals>.update_dependence_shap_scatter_graph(cats)>},
 {'inputs': [{'id': 'interaction-summary-col-60', 'property': 'value'},
   {'id': 'interaction-summary-graph-60', 'property': 'clickData'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.shap_components.InteractionSummaryDependenceConnector.component_callbacks.<locals>.update_interact_col_highlight(col, clickdata)>},
 {'inputs': [{'id': 'decisiontrees-index-70', 'property': 'value'},
   {'id': 'decisiontrees-highlight-70', 'property': 'value'},
   {'id': 'pos-label-70', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.decisiontree_components.DecisionTreesComponent.component_callbacks.<locals>.update_tree_graph(index, highlight, pos_label)>},
 {'inputs': [{'id': 'decisiontrees-graph-70', 'property': 'clickData'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.decisiontree_components.DecisionTreesComponent.component_callbacks.<locals>.update_highlight(clickdata)>},
 {'inputs': [{'id': 'decisionpath-table-index-71', 'property': 'value'},
   {'id': 'decisionpath-table-highlight-71', 'property': 'value'},
   {'id': 'pos-label-71', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.decisiontree_components.DecisionPathTableComponent.component_callbacks.<locals>.update_decisiontree_table(index, highlight, pos_label)>},
 {'inputs': [{'id': 'random-index-clas-button-72', 'property': 'n_clicks'}],
  'state': [{'id': 'random-index-clas-slider-72', 'property': 'value'},
   {'id': 'random-index-clas-labels-72', 'property': 'value'},
   {'id': 'random-index-clas-pred-or-perc-72', 'property': 'value'},
   {'id': 'pos-label-72', 'property': 'value'}],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.ClassifierRandomIndexComponent.component_callbacks.<locals>.update_index(n_clicks, slider_range, labels, pred_or_perc, pos_label)>},
 {'inputs': [{'id': 'random-index-clas-pred-or-perc-72', 'property': 'value'},
   {'id': 'pos-label-72', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.classifier_components.ClassifierRandomIndexComponent.component_callbacks.<locals>.update_slider_label(pred_or_perc, pos_label)>},
 {'inputs': [{'id': 'decisionpath-button-73', 'property': 'n_clicks'}],
  'state': [{'id': 'decisionpath-index-73', 'property': 'value'},
   {'id': 'decisionpath-highlight-73', 'property': 'value'},
   {'id': 'pos-label-73', 'property': 'value'}],
  'callback': <function explainerdashboard.dashboard_components.decisiontree_components.DecisionPathGraphComponent.component_callbacks.<locals>.update_tree_graph(n_clicks, index, highlight, pos_label)>},
 {'inputs': [{'id': 'random-index-clas-index-72', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.connectors.IndexConnector.component_callbacks.<locals>.update_indexes(index)>},
 {'inputs': [{'id': 'decisiontrees-highlight-70', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.connectors.HighlightConnector.component_callbacks.<locals>.update_highlights(highlight)>},
 {'inputs': [{'id': 'pos-label-0', 'property': 'value'}],
  'state': [],
  'callback': <function explainerdashboard.dashboard_components.connectors.PosLabelConnector.component_callbacks.<locals>.update_pos_labels(pos_label)>}]

from explainerdashboard.

moeller84 avatar moeller84 commented on May 18, 2024

I am using the default dashboard, and just switch things off and on in the dashboard.yaml file.

So when i do

dashboard = ExplainerDashboard(explainer, server=app, url_base_pathname="/dashboard/", **params)
list(dashboard.app.callback_map.values())

i get the following output, which seems to have the uuid extension.
[{'inputs': [{'id': 'random-index-clas-button-DKVkx0', 'property': 'n_clicks'}], 'state': [{'id': 'random-index-clas-slider-DKVkx0', 'property': 'value'}, {'id': 'random-index-clas-labels-DKVkx0', 'property': 'value'}, {'id': 'random-index-clas-pred-or-perc-DKVkx0', 'property': 'value'}, {'id': 'pos-label-DKVkx0', 'property': 'value'}], 'callback': <function ClassifierRandomIndexComponent.component_callbacks.<locals>.update_index at 0x7efd29c67200>}, {'inputs': [{'id': 'random-index-clas-pred-or-perc-DKVkx0', 'property': 'value'}, {'id': 'pos-label-DKVkx0', 'property': 'value'}], 'state': [], 'callback': <function ClassifierRandomIndexComponent.component_callbacks.<locals>.update_slider_label at 0x7efd29c673b0>}, {'inputs': [{'id': 'clas-prediction-index-DKVkx1', 'property': 'value'}, {'id': 'pos-label-DKVkx1', 'property': 'value'}], 'state': [], 'callback': <function ClassifierPredictionSummaryComponent.component_callbacks.<locals>.update_output_div at 0x7efd29c674d0>}, {'inputs': [{'id': 'contributions-graph-index-DKVkx2', 'property': 'value'}, {'id': 'contributions-graph-depth-DKVkx2', 'property': 'value'}, {'id': 'contributions-graph-sorting-DKVkx2', 'property': 'value'}, {'id': 'contributions-graph-orientation-DKVkx2', 'property': 'value'}, {'id': 'contributions-graph-group-cats-DKVkx2', 'property': 'value'}, {'id': 'pos-label-DKVkx2', 'property': 'value'}], 'state': [], 'callback': <function ShapContributionsGraphComponent.component_callbacks.<locals>.update_output_div at 0x7efd29c675f0>}, {'inputs': [{'id': 'pdp-group-cats-DKVkx3', 'property': 'value'}], 'state': [{'id': 'pos-label-DKVkx3', 'property': 'value'}], 'callback': <function PdpComponent.component_callbacks.<locals>.update_pdp_graph at 0x7efd29c67710>}, {'inputs': [{'id': 'pdp-index-DKVkx3', 'property': 'value'}, {'id': 'pdp-col-DKVkx3', 'property': 'value'}, {'id': 'pdp-dropna-DKVkx3', 'property': 'value'}, {'id': 'pdp-sample-DKVkx3', 'property': 'value'}, {'id': 'pdp-gridlines-DKVkx3', 'property': 'value'}, {'id': 'pdp-gridpoints-DKVkx3', 'property': 'value'}, {'id': 'pos-label-DKVkx3', 'property': 'value'}], 'state': [], 'callback': <function PdpComponent.component_callbacks.<locals>.update_pdp_graph at 0x7efd29c677a0>}, {'inputs': [{'id': 'contributions-table-index-DKVkx4', 'property': 'value'}, {'id': 'contributions-table-depth-DKVkx4', 'property': 'value'}, {'id': 'contributions-table-sorting-DKVkx4', 'property': 'value'}, {'id': 'contributions-table-group-cats-DKVkx4', 'property': 'value'}, {'id': 'pos-label-DKVkx4', 'property': 'value'}], 'state': [], 'callback': <function ShapContributionsTableComponent.component_callbacks.<locals>.update_output_div at 0x7efd29c678c0>}, {'inputs': [{'id': 'random-index-clas-index-DKVkx0', 'property': 'value'}], 'state': [], 'callback': <function IndexConnector.component_callbacks.<locals>.update_indexes at 0x7efd29c679e0>}, {'inputs': [{'id': 'feature-input-index-a4zhT0', 'property': 'value'}], 'state': [], 'callback': <function FeatureInputComponent.component_callbacks.<locals>.update_whatif_inputs at 0x7efd29c67b00>}, {'inputs': [{'id': 'random-index-clas-button-a4zhT1', 'property': 'n_clicks'}], 'state': [{'id': 'random-index-clas-slider-a4zhT1', 'property': 'value'}, {'id': 'random-index-clas-labels-a4zhT1', 'property': 'value'}, {'id': 'random-index-clas-pred-or-perc-a4zhT1', 'property': 'value'}, {'id': 'pos-label-a4zhT1', 'property': 'value'}], 'callback': <function ClassifierRandomIndexComponent.component_callbacks.<locals>.update_index at 0x7efd29c67c20>}, {'inputs': [{'id': 'random-index-clas-pred-or-perc-a4zhT1', 'property': 'value'}, {'id': 'pos-label-a4zhT1', 'property': 'value'}], 'state': [], 'callback': <function ClassifierRandomIndexComponent.component_callbacks.<locals>.update_slider_label at 0x7efd29c67dd0>}, {'inputs': [{'id': 'pos-label-a4zhT2', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_mitkundeoverblik_login-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-rejse_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-sum_bestandspraemie-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-alder-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-avg_police_levetid-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_tilbud-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-max_police_levetid-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-nyeste_forsikringsprodukt-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_nyeste_forsikringsprodukt-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_rejse-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_rejse-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-min_police_levetid-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-uddannelsesfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-indbo_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_skade-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_personbil-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_indbo-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-postcode-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ejd_vaerdi-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-bebo_arl-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-personbil_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-habitation_zone_navn-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-bilfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ejerforholdsfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ulykke_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-conzoom_gruppe_navn-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_indbo-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-boernefaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-civilstandsfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-rejse_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_personbil-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-boligtypefaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-conzoom_type_navn-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-formuefaktor_v2-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-seneste_salgskanal_mds-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-personindkomstfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-indbo_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-personbil_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_policelinje-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ulykke_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-husstandsindkomstfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_acc-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_police-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-beskaeftigelsesfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_svar_nps-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-aldersfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_forste_varsling-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_motor_acc_1y-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_barn_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_barn_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_varsling-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_personbil_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_rejse_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_personbil_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_rejse_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_motorcykel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_motorcykel_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-max_nps_svar-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-min_nps_svar-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-seneste_svar_nps-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-barn_ulykke_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_nyvaerdi-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-AU_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_aarlig_koersel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-sum_personbil_nyvaerdi-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-sum_aarlig_koersel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_indbo_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-AU_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-motorcykel_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-barn_ulykke_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_indbo_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_segment-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-AU_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-HU_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_ulykke_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_varslinger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_ulykke_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_marketing_segment-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-HU_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_motorcykel_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-RP_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_biler_i_huset-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-RP_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-unikke_varslinger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-motorcykel_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_motorcykel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-FP_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_forste_redning-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-HU_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_redninger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-UP_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_redning-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-unikke_redninger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_barn_ulykke_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-RP_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-UP_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-FP_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-UP_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-FP_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_personer-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_barn_ulykke_1m-input-a4zhT0', 'property': 'value'}], 'state': [], 'callback': <function ClassifierPredictionSummaryComponent.component_callbacks.<locals>.update_output_div at 0x7efd29c67e60>}, {'inputs': [{'id': 'contributions-graph-depth-a4zhT3', 'property': 'value'}, {'id': 'contributions-graph-sorting-a4zhT3', 'property': 'value'}, {'id': 'contributions-graph-orientation-a4zhT3', 'property': 'value'}, {'id': 'contributions-graph-group-cats-a4zhT3', 'property': 'value'}, {'id': 'pos-label-a4zhT3', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_mitkundeoverblik_login-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-rejse_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-sum_bestandspraemie-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-alder-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-avg_police_levetid-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_tilbud-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-max_police_levetid-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-nyeste_forsikringsprodukt-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_nyeste_forsikringsprodukt-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_rejse-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_rejse-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-min_police_levetid-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-uddannelsesfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-indbo_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_skade-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_personbil-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_indbo-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-postcode-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ejd_vaerdi-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-bebo_arl-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-personbil_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-habitation_zone_navn-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-bilfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ejerforholdsfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ulykke_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-conzoom_gruppe_navn-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_indbo-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-boernefaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-civilstandsfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-rejse_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_personbil-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-boligtypefaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-conzoom_type_navn-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-formuefaktor_v2-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-seneste_salgskanal_mds-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-personindkomstfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-indbo_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-personbil_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_policelinje-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ulykke_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-husstandsindkomstfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_acc-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_police-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-beskaeftigelsesfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_svar_nps-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-aldersfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_forste_varsling-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_motor_acc_1y-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_barn_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_barn_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_varsling-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_personbil_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_rejse_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_personbil_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_rejse_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_motorcykel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_motorcykel_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-max_nps_svar-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-min_nps_svar-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-seneste_svar_nps-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-barn_ulykke_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_nyvaerdi-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-AU_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_aarlig_koersel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-sum_personbil_nyvaerdi-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-sum_aarlig_koersel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_indbo_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-AU_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-motorcykel_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-barn_ulykke_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_indbo_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_segment-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-AU_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-HU_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_ulykke_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_varslinger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_ulykke_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_marketing_segment-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-HU_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_motorcykel_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-RP_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_biler_i_huset-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-RP_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-unikke_varslinger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-motorcykel_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_motorcykel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-FP_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_forste_redning-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-HU_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_redninger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-UP_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_redning-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-unikke_redninger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_barn_ulykke_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-RP_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-UP_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-FP_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-UP_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-FP_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_personer-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_barn_ulykke_1m-input-a4zhT0', 'property': 'value'}], 'state': [], 'callback': <function ShapContributionsGraphComponent.component_callbacks.<locals>.update_output_div at 0x7efd29bc6050>}, {'inputs': [{'id': 'contributions-table-depth-a4zhT4', 'property': 'value'}, {'id': 'contributions-table-sorting-a4zhT4', 'property': 'value'}, {'id': 'contributions-table-group-cats-a4zhT4', 'property': 'value'}, {'id': 'pos-label-a4zhT4', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_mitkundeoverblik_login-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-rejse_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-sum_bestandspraemie-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-alder-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-avg_police_levetid-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_tilbud-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-max_police_levetid-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-nyeste_forsikringsprodukt-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_nyeste_forsikringsprodukt-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_rejse-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_rejse-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-min_police_levetid-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-uddannelsesfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-indbo_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_skade-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_personbil-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_indbo-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-postcode-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ejd_vaerdi-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-bebo_arl-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-personbil_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-habitation_zone_navn-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-bilfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ejerforholdsfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ulykke_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-conzoom_gruppe_navn-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_indbo-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-boernefaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-civilstandsfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-rejse_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_personbil-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-boligtypefaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-conzoom_type_navn-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-formuefaktor_v2-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-seneste_salgskanal_mds-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-personindkomstfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-indbo_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-personbil_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_policelinje-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ulykke_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-husstandsindkomstfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_acc-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_police-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-beskaeftigelsesfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_svar_nps-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-aldersfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_forste_varsling-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_motor_acc_1y-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_barn_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_barn_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_varsling-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_personbil_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_rejse_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_personbil_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_rejse_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_motorcykel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_motorcykel_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-max_nps_svar-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-min_nps_svar-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-seneste_svar_nps-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-barn_ulykke_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_nyvaerdi-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-AU_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_aarlig_koersel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-sum_personbil_nyvaerdi-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-sum_aarlig_koersel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_indbo_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-AU_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-motorcykel_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-barn_ulykke_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_indbo_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_segment-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-AU_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-HU_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_ulykke_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_varslinger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_ulykke_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_marketing_segment-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-HU_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_motorcykel_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-RP_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_biler_i_huset-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-RP_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-unikke_varslinger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-motorcykel_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_motorcykel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-FP_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_forste_redning-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-HU_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_redninger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-UP_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_redning-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-unikke_redninger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_barn_ulykke_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-RP_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-UP_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-FP_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-UP_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-FP_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_personer-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_barn_ulykke_1m-input-a4zhT0', 'property': 'value'}], 'state': [], 'callback': <function ShapContributionsTableComponent.component_callbacks.<locals>.update_output_div at 0x7efd29bc6170>}, {'inputs': [{'id': 'pdp-group-cats-a4zhT5', 'property': 'value'}], 'state': [{'id': 'pos-label-a4zhT5', 'property': 'value'}], 'callback': <function PdpComponent.component_callbacks.<locals>.update_pdp_graph at 0x7efd29bc6290>}, {'inputs': [{'id': 'pdp-col-a4zhT5', 'property': 'value'}, {'id': 'pdp-dropna-a4zhT5', 'property': 'value'}, {'id': 'pdp-sample-a4zhT5', 'property': 'value'}, {'id': 'pdp-gridlines-a4zhT5', 'property': 'value'}, {'id': 'pdp-gridpoints-a4zhT5', 'property': 'value'}, {'id': 'pos-label-a4zhT5', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_mitkundeoverblik_login-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-rejse_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-sum_bestandspraemie-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-alder-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-avg_police_levetid-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_tilbud-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-max_police_levetid-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-nyeste_forsikringsprodukt-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_nyeste_forsikringsprodukt-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_rejse-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_rejse-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-min_police_levetid-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-uddannelsesfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-indbo_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_skade-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_personbil-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_indbo-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-postcode-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ejd_vaerdi-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-bebo_arl-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-personbil_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-habitation_zone_navn-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-bilfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ejerforholdsfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ulykke_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-conzoom_gruppe_navn-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_indbo-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-boernefaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-civilstandsfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-rejse_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_personbil-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-boligtypefaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-conzoom_type_navn-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-formuefaktor_v2-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-seneste_salgskanal_mds-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-personindkomstfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-indbo_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-personbil_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_policelinje-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-ulykke_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-husstandsindkomstfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_acc-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_police-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-beskaeftigelsesfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_svar_nps-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-aldersfaktor-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_forste_varsling-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_motor_acc_1y-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_barn_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_barn_ulykke-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_varsling-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_personbil_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_rejse_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_personbil_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_rejse_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_nysalg_motorcykel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_motorcykel_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-max_nps_svar-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-min_nps_svar-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-seneste_svar_nps-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-barn_ulykke_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_nyvaerdi-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-AU_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_aarlig_koersel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-sum_personbil_nyvaerdi-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-sum_aarlig_koersel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_indbo_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-AU_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-motorcykel_seneste_salgsdato_id-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-barn_ulykke_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_indbo_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_segment-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-AU_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-HU_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_ulykke_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_varslinger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_ulykke_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-mest_brugte_bil_marketing_segment-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-HU_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_motorcykel_1m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-RP_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_biler_i_huset-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-RP_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-unikke_varslinger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-motorcykel_antal-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-har_haft_motorcykel-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-FP_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_forste_redning-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-HU_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_redninger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-UP_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-dage_siden_seneste_redning-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-unikke_redninger-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_barn_ulykke_3m-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-RP_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-UP_neg-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-FP_pos-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-UP_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-FP_overall-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_personer-input-a4zhT0', 'property': 'value'}, {'id': 'feature-input-antal_tilbud_barn_ulykke_1m-input-a4zhT0', 'property': 'value'}], 'state': [], 'callback': <function PdpComponent.component_callbacks.<locals>.update_pdp_graph at 0x7efd29bc63b0>}, {'inputs': [{'id': 'random-index-clas-index-a4zhT1', 'property': 'value'}], 'state': [], 'callback': <function IndexConnector.component_callbacks.<locals>.update_indexes at 0x7efd29bc6440>}, {'inputs': [{'id': 'shap-summary-graph-64E3o0', 'property': 'clickData'}], 'state': [], 'callback': <function ShapSummaryComponent.component_callbacks.<locals>.display_scatter_click_data at 0x7efd29bc6560>}, {'inputs': [{'id': 'shap-summary-type-64E3o0', 'property': 'value'}, {'id': 'shap-summary-group-cats-64E3o0', 'property': 'value'}, {'id': 'shap-summary-depth-64E3o0', 'property': 'value'}, {'id': 'shap-summary-index-64E3o0', 'property': 'value'}, {'id': 'pos-label-64E3o0', 'property': 'value'}], 'state': [], 'callback': <function ShapSummaryComponent.component_callbacks.<locals>.update_shap_summary_graph at 0x7efd29bc6680>}, {'inputs': [{'id': 'shap-dependence-col-64E3o1', 'property': 'value'}], 'state': [{'id': 'shap-dependence-group-cats-64E3o1', 'property': 'value'}, {'id': 'pos-label-64E3o1', 'property': 'value'}], 'callback': <function ShapDependenceComponent.component_callbacks.<locals>.set_color_col_dropdown at 0x7efd29bc67a0>}, {'inputs': [{'id': 'shap-dependence-color-col-64E3o1', 'property': 'value'}, {'id': 'shap-dependence-index-64E3o1', 'property': 'value'}, {'id': 'pos-label-64E3o1', 'property': 'value'}], 'state': [{'id': 'shap-dependence-col-64E3o1', 'property': 'value'}], 'callback': <function ShapDependenceComponent.component_callbacks.<locals>.update_dependence_graph at 0x7efd29bc6950>}, {'inputs': [{'id': 'shap-dependence-group-cats-64E3o1', 'property': 'value'}], 'state': [{'id': 'shap-dependence-col-64E3o1', 'property': 'value'}], 'callback': <function ShapDependenceComponent.component_callbacks.<locals>.update_dependence_shap_scatter_graph at 0x7efd29bc6a70>}, {'inputs': [{'id': 'shap-summary-group-cats-64E3o0', 'property': 'value'}], 'state': [], 'callback': <function ShapSummaryDependenceConnector.component_callbacks.<locals>.update_dependence_shap_scatter_graph at 0x7efd29bc6b00>}, {'inputs': [{'id': 'shap-summary-graph-64E3o0', 'property': 'clickData'}], 'state': [], 'callback': <function ShapSummaryDependenceConnector.component_callbacks.<locals>.display_scatter_click_data at 0x7efd29bc6c20>}, {'inputs': [{'id': 'pos-label-0', 'property': 'value'}], 'state': [], 'callback': <function PosLabelConnector.component_callbacks.<locals>.update_pos_labels at 0x7efd29bc6d40>}]

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Are you sure you're on the latest (pypi) version (0.2.17)?

In case you're installing through conda: the conda version is a bit behind (0.2.15) because we're dealing with some conda-forge dependency conflicts, and that version has not yet has the uuid fix implemented.

from explainerdashboard.

moeller84 avatar moeller84 commented on May 18, 2024

Im quite sure it is version 0.2.17 we are deploying through docker

looking in indexes: http://artifactory-singlep.p001.alm.brand.dk/artifactory/api/pypi/pypi-virtual/simple Processing /project Requirement already satisfied: explainerdashboard in /opt/venv/lib/python3.7/site-packages (from for-p-afgang-dashboard==0.1.0) (**_0.2.17_**)

but we are also building a venv. So maybe that messes something up?

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Shouldn't mess things up, using virtual envs myself. Only thing I can think of right now is that you have a cached build step from the docker build that is still using 0.2.15. So could try to prune the cache and see if that helps.

Will build a dashboard myself inside a docker container, and see if I run into the same issue. Do you have a reproducible example with Dockerfile that generates the error? (can just be with the titanic dataset)

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

This seems to work fine, with no uuid strings. Haven't tried with docker swarm though:

generate_dashboard.py

from sklearn.ensemble import RandomForestClassifier

from explainerdashboard import *
from explainerdashboard.datasets import *

X_train, y_train, X_test, y_test = titanic_survive()
model = RandomForestClassifier(n_estimators=50, max_depth=5)
model.fit(X_train, y_train)

explainer = ClassifierExplainer(model, X_test, y_test, 
                                 cats=["Sex", 'Deck', 'Embarked'],
                                 labels=['Not Survived', 'Survived'],
                                 descriptions=feature_descriptions)

db = ExplainerDashboard(explainer)

db.to_yaml("dashboard.yaml", explainerfile="explainer.joblib", dump_explainer=True)

run_dashboard.py

import waitress
from explainerdashboard import *

db = ExplainerDashboard.from_config("dashboard.yaml")

print(list(db.app.callback_map.values()))

if __name__ == "__main__":
    waitress.serve(db.app.server, host='0.0.0.0', port=9050)

Dockerfile

FROM python:3.8

RUN pip install explainerdashboard

COPY generate_dashboard.py ./
COPY run_dashboard.py ./

RUN python generate_dashboard.py

EXPOSE 9050
CMD ["python", "./run_dashboard.py"]
$ docker build -t explainerdashboard .
$ docker run -p 9050:9050 explainerdashboard

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

@moeller84 Did you manage to get it to work?

from explainerdashboard.

moeller84 avatar moeller84 commented on May 18, 2024

@moeller84 Did you manage to get it to work?

Sorry. No i did not, unfortunatly.

from explainerdashboard.

moeller84 avatar moeller84 commented on May 18, 2024

When i read the source code it seems like you are still generating uuids when name is None, but then just suffixing a number in the end.

EDIT: when i recreate your example from above i dont get the generated uuids.

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Yes, each composite (base for a tab), simply adds a number to the end to self.name, e.g.

class ImportancesComposite(ExplainerComponent):
    def __init__(self, explainer, title="Feature Importances", name=None,
                    hide_importances=False,
                    hide_selector=True, **kwargs):
        """Overview tab of feature importances

        Can show both permutation importances and mean absolute shap values.

        Args:
            explainer (Explainer): explainer object constructed with either
                        ClassifierExplainer() or RegressionExplainer()
            title (str, optional): Title of tab or page. Defaults to 
                        "Feature Importances".
            name (str, optional): unique name to add to Component elements. 
                        If None then random uuid is generated to make sure 
                        it's unique. Defaults to None.
            hide_importances (bool, optional): hide the ImportancesComponent
            hide_selector (bool, optional): hide the post label selector. 
                Defaults to True.
        """
        super().__init__(explainer, title, name)

        self.importances = ImportancesComponent(
                explainer, name=self.name+"0", hide_selector=hide_selector, **kwargs)

    def layout(self):
        return html.Div([
            dbc.Row([
                make_hideable(
                    dbc.Col([
                        self.importances.layout(),
                    ]), hide=self.hide_importances),
            ], style=dict(margin=25))
        ])

Then ExplainerDashboard instantiates all the tabs using ExplainerTabsLayout, which has the line

self.tabs  = [instantiate_component(tab, explainer, name=str(i+1), **kwargs) for i, tab in enumerate(tabs)]

So each tab gets the name "1", "2", 3", etc. And then each subcomponent gets the name "11", "12", etc.

Are you defining custom components? Or defining them before you add them to ExplainerDashboard?

E.g.

tab = ImportancesComposite()
ExplainerDashboard(tab).run

Would result in a random uuid name for tab

from explainerdashboard.

moeller84 avatar moeller84 commented on May 18, 2024

Hi
We tried running the dashboard on a single container. That works. But running on multiple containers / swarm gives cause to the problem.

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

So in a swarm it starts generating uuid names but in a single container it doesn't?

That seems super strange... Again, the only thing I can think of is old versions of explainerdashboard in a cached docker layer.

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

I'm gonna see if I can build some diagnostic functionality that makes it easier to see the whole component tree, including .name properties, and would also give a warning when it detects any uuid .name...

from explainerdashboard.

moeller84 avatar moeller84 commented on May 18, 2024

it also does generate uuid names with a single container. But it seems that callback names are being mixed when running on more than one container

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

ah, okay, that at least is an easier to understand problem. So the example I gave you didn't give uuid names right?

Is there any code you can share on how you generate the dashboard? Because you have to be doing something custom otherwise it would just work out of the box.

from explainerdashboard.

moeller84 avatar moeller84 commented on May 18, 2024

dashboard.yaml

dashboard:
  explainerfile: data/processed/explainer.joblib
  params:
    title: Fastholdelses model
    hide_header: false
    hide_shapsummary: false
    header_hide_title: false
    header_hide_selector: false
    block_selector_callbacks: false
    pos_label: null
    fluid: true
    mode: dash
    width: 1000
    height: 800
    external_stylesheets: null
#    server: true
#    url_base_pathname: null
    responsive: true
    logins: null
    port: 8050
    tabs:
    #- importances
    #- model_summary
    - contributions
    - whatif
    - shap_dependence
    #- shap_interaction
    #- decision_trees

__init__.py

import logging
from pathlib import Path
from flask import Flask

from for_p_afgang_dashboard.extensions import setup_extensions
from explainerdashboard import ClassifierExplainer, ExplainerDashboard
import yaml

# Metadata for the package
# fmt: off
__version__ = "0.1.0"
__url__ = "https://lspgitlab01.alm.brand.dk/advanced-analytics/for_p_afgang_dashboard"
__description__ = "explainer dashboard for for_p_afgang model performance"
__author__ = "Niels Møller-Hansen"
__email__ = "[email protected]"
# fmt: on

logger = logging.getLogger("api_logger")
file_path = Path(__file__)


def create_app(config):
    logger.info("Starting app...")
    logger.debug(f"Using config {config}")
    app = Flask("for_p_afgang_dashboard")
    app.config.from_object(config)

    @app.route("/health")
    def healthcheck():
        return "Healthy", 200

    setup_extensions(app)

    dashboard_yaml_path = file_path.parent.joinpath("dashboard.yaml")
    explainerfile = str(file_path.parent.joinpath("data").joinpath("explainer.joblib"))
    logger.info(explainerfile)
    config = yaml.safe_load(open(dashboard_yaml_path, "r"))
    params = config["dashboard"]["params"]
    explainer = ClassifierExplainer.from_file(explainerfile)
    print("X:", len(explainer.X))
    logger.info(f"Explainer contains {len(explainer.X)} samples")
    dashboard = ExplainerDashboard(
        explainer, server=app, url_base_pathname="/", **params
    )
    print(list(dashboard.app.callback_map.values()))

    @app.route("/")
    def return_dashboard():
        return dashboard.app.index()

    logger.info("Explainer dashboard loaded")
    return app

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Ah, I think I got it!

In the yaml I see:

tabs:
    #- importances
    #- model_summary
    - contributions
    - whatif
    - shap_dependence
    #- shap_interaction
    #- decision_trees

So that equates to ExplainerDashboard(explainer, ["contributions", "whatif", "shap_dependence"]).

The string tab indicators get converted by

def _convert_str_tabs(self, component):

def _convert_str_tabs(self, component):
        if isinstance(component, str):
            if component == 'importances':
                return ImportancesTab
            elif component == 'model_summary':
                return ModelSummaryTab
            elif component == 'contributions':
                return ContributionsTab
            elif component == 'whatif':
                return WhatIfTab
            elif component == 'shap_dependence':
                return ShapDependenceTab
            elif component == 'shap_interaction':
                return ShapInteractionsTab
            elif component == 'decision_trees':
                return  DecisionTreesTab
        return component

These ImportancesTab, ModelSummaryTab, have actually been deprecated. They are only there for backward compatibility reasons: they have been deprecated in favor of ImportancesComposite, etc, but I had not adjusted this helper method. So I will fix this in the next release, but in the meanwhile, I think if you change dashboard.yaml to:

dashboard:
  explainerfile: data/processed/explainer.joblib
  params:
    title: Fastholdelses model
    hide_header: false
    hide_shapsummary: false
    header_hide_title: false
    header_hide_selector: false
    block_selector_callbacks: false
    pos_label: null
    fluid: true
    mode: dash
    width: 1000
    height: 800
    external_stylesheets: null
#    server: true
#    url_base_pathname: null
    responsive: true
    logins: null
    port: 8050
    importances: false
    model_summary: false
    shap_interaction: false
    decision_trees: false

So this is equivalent of passing booleans to switch off tabs: ExplainerDashboard(explainer, importances=False, model_summary=False, shap_interaction=False, decision_trees=False)

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Just released https://github.com/oegedijk/explainerdashboard/releases/tag/v0.2.20 which should fix this issue...

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

I think you can also simplify the loading of the dashboard:

def create_app(config):
    logger.info("Starting app...")
    logger.debug(f"Using config {config}")
    app = Flask("for_p_afgang_dashboard")
    app.config.from_object(config)

    @app.route("/health")
    def healthcheck():
        return "Healthy", 200

    setup_extensions(app)

    explainerfile = str(file_path.parent.joinpath("data").joinpath("explainer.joblib"))
    dashboard_yaml_path = file_path.parent.joinpath("dashboard.yaml")
    logger.info(explainerfile)
    
    dashboard = ExplainerDashboard.from_config(
        explainerfile , dashboard_yaml_path, server=app, url_base_pathname="/")
    logger.info(f"Explainer contains {len(dashboard.explainer)} samples")
    print(list(dashboard.app.callback_map.values()))

    @app.route("/")
    def return_dashboard():
        return dashboard.app.index()

    logger.info("Explainer dashboard loaded")
    return app

from explainerdashboard.

moeller84 avatar moeller84 commented on May 18, 2024

i updated to the latest version and also altered the .yaml file. That leaves me with this error (having touched anything else):

Traceback (most recent call last):
  File "/home/niels/.pyenv/versions/3.7.9/envs/for_p_afgang_dashboard/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/home/niels/.pyenv/versions/3.7.9/envs/for_p_afgang_dashboard/lib/python3.7/site-packages/flask/cli.py", line 184, in find_app_by_string
    app = call_factory(script_info, attr, args)
  File "/home/niels/.pyenv/versions/3.7.9/envs/for_p_afgang_dashboard/lib/python3.7/site-packages/flask/cli.py", line 115, in call_factory
    return app_factory(*arguments)
  File "/home/niels/projektmappe/for_p_afgang_dashboard/app/for_p_afgang_dashboard/__init__.py", line 43, in create_app
    explainer, server=app, url_base_pathname="/", **params
  File "/home/niels/.pyenv/versions/3.7.9/envs/for_p_afgang_dashboard/lib/python3.7/site-packages/explainerdashboard/dashboards.py", line 465, in __init__
    fluid=fluid))
  File "/home/niels/.pyenv/versions/3.7.9/envs/for_p_afgang_dashboard/lib/python3.7/site-packages/explainerdashboard/dashboards.py", line 88, in __init__
    self.tabs  = [instantiate_component(tab, explainer, name=str(i+1), **kwargs) for i, tab in enumerate(tabs)]
  File "/home/niels/.pyenv/versions/3.7.9/envs/for_p_afgang_dashboard/lib/python3.7/site-packages/explainerdashboard/dashboards.py", line 88, in <listcomp>
    self.tabs  = [instantiate_component(tab, explainer, name=str(i+1), **kwargs) for i, tab in enumerate(tabs)]
  File "/home/niels/.pyenv/versions/3.7.9/envs/for_p_afgang_dashboard/lib/python3.7/site-packages/explainerdashboard/dashboard_methods.py", line 431, in instantiate_component
    component = component(explainer, name=name, **kwargs)
  File "/home/niels/.pyenv/versions/3.7.9/envs/for_p_afgang_dashboard/lib/python3.7/site-packages/explainerdashboard/dashboard_components/composites.py", line 271, in __init__
    hide_selector=hide_selector, **kwargs)
  File "/home/niels/.pyenv/versions/3.7.9/envs/for_p_afgang_dashboard/lib/python3.7/site-packages/explainerdashboard/dashboard_components/shap_components.py", line 1027, in __init__
    if not self.explainer.onehot_cols:
AttributeError: 'XGBClassifierExplainer' object has no attribute 'onehot_cols'

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

ah, yeah, you have to rebuild the explainer with the new version: I made some breaking changes how categorical features and one hot encoded features are handled internally in order to support categorical features. (on the plus side: categorical features are supported now!)

from explainerdashboard.

carlryn avatar carlryn commented on May 18, 2024

Is there a reason why you are using UUIDs in the first place? Thinking you could just set seed and do randomization with numbers to get deterministic names.

E.g line 177 in dashboard_methods.py
if not hasattr(self, "name") or self.name is None: self.name = name or "uuid"+shortuuid.ShortUUID().random(length=5)

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Original goal was to generate a unique name that is both short and url-friendly (planning on adding querystring support at some point). But I guess that could be done simpler and without the shortuuid dependency, e.g.: https://proinsias.github.io/til/Python-UUID-generate-random-but-reproducible-with-seed/

Got a code suggestion?

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Is it working now? Shall I close the issue?

from explainerdashboard.

carlryn avatar carlryn commented on May 18, 2024

This seems to be working now! Ran with several workers on gunicorn and also saved callback id names which all matches.

from explainerdashboard.

oegedijk avatar oegedijk commented on May 18, 2024

Awesome!

from explainerdashboard.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.