oegedijk / explainerdashboard

Quickly build Explainable AI dashboards that show the inner workings of so-called "blackbox" machine learning models.
http://explainerdashboard.readthedocs.io
MIT License
2.3k stars 331 forks source link

Question regarding deployment on Heroku #38

Closed hkoppen closed 3 years ago

hkoppen commented 3 years ago

I just tried to deploy my app on Heroku by directly importing the github project. However, I did not manage to "add the buildpack" correctly - I'm still generating a slug larger than 500MB. I did

oegedijk commented 3 years ago

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.

oegedijk commented 3 years ago

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

oegedijk commented 3 years ago

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

oegedijk commented 3 years ago

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.

hkoppen commented 3 years ago

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?

oegedijk commented 3 years ago

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

explainerdashboard==0.2.13.2 gunicorn

hkoppen commented 3 years ago

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
hkoppen commented 3 years ago

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

oegedijk commented 3 years ago

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

oegedijk commented 3 years ago

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?

hkoppen commented 3 years ago

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.

oegedijk commented 3 years ago

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.

hkoppen commented 3 years ago

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?

oegedijk commented 3 years ago

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.

hkoppen commented 3 years ago

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/react@16.v1_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/polyfill@7.v1_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/react@16.v1_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/polyfill@7.v1_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/react-dom@16.v1_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/prop-types@15.v1_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/react-dom@16.v1_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/prop-types@15.v1_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
oegedijk commented 3 years ago

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?)
hkoppen commented 3 years ago

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?

hkoppen commented 3 years ago

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 ...

oegedijk commented 3 years ago

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.

hkoppen commented 3 years ago

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

oegedijk commented 3 years ago

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?

hkoppen commented 3 years ago

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

oegedijk commented 3 years ago

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.

oegedijk commented 3 years ago

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.

oegedijk commented 3 years ago

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

oegedijk commented 3 years ago

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...

hkoppen commented 3 years ago

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 ;-)

carlryn commented 3 years ago

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? :)

oegedijk commented 3 years ago

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()
oegedijk commented 3 years ago

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

oegedijk commented 3 years ago

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!

oegedijk commented 3 years ago

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

carlryn commented 3 years ago

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 '@'?"
oegedijk commented 3 years ago

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

Is this a custom layout or a default dashboard?

oegedijk commented 3 years ago

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...

carlryn commented 3 years ago

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

oegedijk commented 3 years ago

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

oegedijk commented 3 years ago

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/

oegedijk commented 3 years ago

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?

moeller84 commented 3 years ago

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?

oegedijk commented 3 years ago

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

oegedijk commented 3 years ago

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)>}]
moeller84 commented 3 years ago

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>}]

oegedijk commented 3 years ago

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.

moeller84 commented 3 years ago

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?

oegedijk commented 3 years ago

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)

oegedijk commented 3 years ago

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
oegedijk commented 3 years ago

@moeller84 Did you manage to get it to work?

moeller84 commented 3 years ago

@moeller84 Did you manage to get it to work?

Sorry. No i did not, unfortunatly.

moeller84 commented 3 years ago

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.