From d715463dd2627cda2a5f21d810f685c46a5203a0 Mon Sep 17 00:00:00 2001
From: Caroline DE POURTALES <cdepourt@montana.irit.fr>
Date: Thu, 17 Feb 2022 13:17:59 +0100
Subject: [PATCH] decisiontree color

---
 app.py                                        |  83 +++-
 data_retriever.json                           |   9 +-
 pages/__pycache__/layout.cpython-38.pyc       | Bin 731 -> 0 bytes
 .../DecisionTree/DecisionTreeComponent.py     |  37 +-
 .../DecisionTreeComponent.cpython-38.pyc      | Bin 1308 -> 0 bytes
 .../map/australian/{ITI => }/australian.map   |   0
 .../map/cancer/{ITI => }/cancer.map           |   0
 .../DecisionTree/map/car/{ITI => }/car.map    |   0
 .../utils/__pycache__/_fileio.cpython-38.pyc  | Bin 3674 -> 0 bytes
 .../utils/__pycache__/dtree.cpython-38.pyc    | Bin 21333 -> 0 bytes
 .../utils/__pycache__/dtviz.cpython-38.pyc    | Bin 1471 -> 0 bytes
 pages/application/DecisionTree/utils/dtree.py |  50 +-
 pages/application/DecisionTree/utils/dtviz.py |  65 ++-
 .../NaiveBayesComponent.py}                   |   0
 .../layout_application.cpython-38.pyc         | Bin 4500 -> 0 bytes
 .../__pycache__/utils_data.cpython-38.pyc     | Bin 1467 -> 0 bytes
 pages/application/component.svg               | 467 ------------------
 pages/application/layout_application.py       | 109 +---
 pages/application/utils_data.py               |  18 +-
 pages/layout.py                               |  20 -
 requirements.txt                              |   4 +-
 21 files changed, 239 insertions(+), 623 deletions(-)
 delete mode 100644 pages/__pycache__/layout.cpython-38.pyc
 delete mode 100644 pages/application/DecisionTree/__pycache__/DecisionTreeComponent.cpython-38.pyc
 rename pages/application/DecisionTree/map/australian/{ITI => }/australian.map (100%)
 rename pages/application/DecisionTree/map/cancer/{ITI => }/cancer.map (100%)
 rename pages/application/DecisionTree/map/car/{ITI => }/car.map (100%)
 delete mode 100644 pages/application/DecisionTree/utils/__pycache__/_fileio.cpython-38.pyc
 delete mode 100644 pages/application/DecisionTree/utils/__pycache__/dtree.cpython-38.pyc
 delete mode 100644 pages/application/DecisionTree/utils/__pycache__/dtviz.cpython-38.pyc
 rename pages/application/{DecisionTree/decision_tree_explicability.py => NaiveBayes/NaiveBayesComponent.py} (100%)
 delete mode 100644 pages/application/__pycache__/layout_application.cpython-38.pyc
 delete mode 100644 pages/application/__pycache__/utils_data.cpython-38.pyc
 delete mode 100644 pages/application/component.svg
 delete mode 100644 pages/layout.py

diff --git a/app.py b/app.py
index 7ccf049..bb9a5c6 100644
--- a/app.py
+++ b/app.py
@@ -1,15 +1,88 @@
 # Run this app with `python app.py` and
 # visit http://127.0.0.1:8050/ in your web browser.
-from pages.layout import create_layout
-
 import dash
 import json 
+from dash import dcc
+from dash import html
+from dash import dcc, html, Input, Output
+import dash_bootstrap_components as dbc
+
+from pages.application.layout_application import Model, View
 
+'''
+Loading data
+'''
 models_data = open('data_retriever.json')
-data = json.load(models_data)
+data = json.load(models_data)["data"]
+
+app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
+
+
+
+
+
+'''
+Construction of the layout
+'''
+model = Model(data)
+view = View(model)
+tabs = dcc.Tabs([
+    dcc.Tab(label='Course on Explainable AI', children=[]),
+    view.tab,
+])
+
+app.layout = html.Div([
+        html.H1('FXToolKit'),
+        tabs])
+
+
+
+
+
+
+
+'''
+Callback for the app
+'''
+@app.callback(
+    Output('ml_datasets_choice', 'options'),
+    Output('ml_instances_choice', 'options'),
+    Output('graph', 'children'),
+    Output('explanation', 'children'),
+    Input('ml_model_choice', 'value'),
+    Input('ml_datasets_choice', 'value'),
+    Input('ml_instances_choice', 'value'), 
+    prevent_initial_call=True
+)
+def update_ml_type(value_ml_model, value_dataset, value_instance):
+
+
+    ctx = dash.callback_context
+    if ctx.triggered:
+        dropdown_id = ctx.triggered[0]['prop_id'].split('.')[0]
+        if dropdown_id == 'ml_model_choice' :
+                model.update_ml_model(value_ml_model)
+                return model.datasets, [], "", ""
+
+        elif dropdown_id == 'ml_datasets_choice':
+
+            model.update_dataset(value_dataset)
+            view.update_dataset()
+            return model.datasets, model.instances, view.component.network, ""
+
+        elif dropdown_id == 'ml_instances_choice' :
+
+            model.update_instance(value_instance)
+            view.update_instance()
+            return model.datasets, model.instances, view.component.network, view.component.explanation
+
+
+
+
 
-app = dash.Dash(__name__)
 
+'''
+Launching app
+'''
 if __name__ == '__main__':
-    app.layout = create_layout(data)
     app.run_server(debug=True)
\ No newline at end of file
diff --git a/data_retriever.json b/data_retriever.json
index 059c843..986722f 100644
--- a/data_retriever.json
+++ b/data_retriever.json
@@ -5,8 +5,15 @@
             "ml_type" : "DecisionTree",
             "trained_models" : "pages/application/DecisionTree/trained_models",
             "instances" : "pages/application/DecisionTree/instances/",
-            "explicability_algorithm" : "pages/application/DecisionTree/decision_tree_explicability",
+            "map" : "pages/application/DecisionTree/map/",
             "component" : "DecisionTreeComponent"
+        },
+        {
+            "ml_type" : "NaiveBayes",
+            "trained_models" : "pages/application/NaiveBayes/trained_models",
+            "instances" : "pages/application/NaiveBayes/instances/",
+            "map" : "pages/application/NaiveBayes/map/",
+            "component" : "NaiveBayesComponent"
         }
     ]
     
diff --git a/pages/__pycache__/layout.cpython-38.pyc b/pages/__pycache__/layout.cpython-38.pyc
deleted file mode 100644
index 7eafe19fdf636c56577100fc7a56db2e308501d6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 731
zcmWIL<>g{vU|{H3%$>BKk%8ech=Yuo7#J8F7#J9e0~i<>QW#Pga~N_NqZk=MY^EHB
zT&5@{Fq=7tIf^-jA%!`IC5jbHvqiC|Fr={LaO84EaWXQbu%@uJFhp^su%~deFhp^u
zFb6Yea=rxFrO9}UIVCw+lkpZyMoDgtpC;oivE-uE#FEtblEkF=#DapH%;dz9%>2A$
zMv!_a204w5fq}sp<U9oi28I%b1&k#OSxhO6=?p1My-X#{H4M#+=}Ze5YZ+@8gBdiL
z{i-CK^Gl10Qx)>_6kIC`auPH15|eUL6&yV^nW9*85|dJMqBxQ>GILUjQu8!fia>Tm
zv4kWh6-P0LBqkNHFfcG^GTmZHNi0c>Vkt>XDqhJ@1aibL8~u#@+*JMKl+=R!(xMW5
zr_!R-#8Ul&yrRrR-GZY0tke?Sl+@IMoYb<^BK?BI^weVgoW#of(h|Lb%3ItB{}zKH
z1WYh6vM};6vi)OYDdJ#YU_kLaC?Hs2o|k}oegRVo<3dJ+$AcL(nf#(S-6BHr^K-m2
zOEg)Em>C!tqL^JW%c7V(48aC#G8Tc10{a6(urn|)+~S403F1mYP$+|(0df@wBip|s
zPEc6zfWo3U0~}BgquI(bi&K)5qu2@(^HLIvqnHX(qFD1va|<e?nDPpuRKQ->LyA;A
zhy}>;3=U_o+rYL!90dwy2n*~y4x8Nkl+v73J5W#*^D!_m@G$Z)a)2Nc4>Jcl0JiO=
A&;S4c

diff --git a/pages/application/DecisionTree/DecisionTreeComponent.py b/pages/application/DecisionTree/DecisionTreeComponent.py
index 1aa7531..bfdc1aa 100644
--- a/pages/application/DecisionTree/DecisionTreeComponent.py
+++ b/pages/application/DecisionTree/DecisionTreeComponent.py
@@ -1,18 +1,35 @@
 from dash import dcc, html, Input, Output, callback
-from pages.application.DecisionTree.utils.dtviz import visualize
+from pages.application.DecisionTree.utils.dtviz import visualize, visualize_instance
 from pages.application.DecisionTree.utils.dtree import DecisionTree
+import dash_interactive_graphviz
 
 class DecisionTreeComponent():
 
-    def __init__(self):
+    def __init__(self, dataset):
 
-        self.network = html.Img()
+        map = "pages/application/DecisionTree/map/"+ dataset + "/" + dataset + ".map"
 
-    def update(self, dataset) :
-        print(dataset)
-        dt = DecisionTree(from_file = "pages/application/DecisionTree/trained_models/"+dataset+'/'+dataset+".dt")
-        visualize(dt, "svg", "pages/application/component.svg")
-        self.network = html.Img(src="pages/application/component.svg")
+        self.dt = DecisionTree(from_file = "pages/application/DecisionTree/trained_models/"+dataset+'/'+dataset+".dt", mapfile = map)
+        dot_source = visualize(self.dt)
 
-    def update_with_explicability(self, instance) :
-        self.network = self.network
\ No newline at end of file
+        self.network = dash_interactive_graphviz.DashInteractiveGraphviz(
+            dot_source=dot_source
+        )
+
+        self.explanation = dcc.Textarea(value = "", style =  { "font_size" : "15px",
+                                                                        "width": "40rem",
+                                                                        "height": "40rem",
+                                                                        "margin-bottom": "5rem",
+                                                                        "background-color": "#f8f9fa",
+                                                                    })
+
+    def update_with_explicability(self, dataset, instance) :
+        instance = open("pages/application/DecisionTree/instances/"+ dataset + "/" + instance, "r")
+        instance = str(instance.read()).strip().split(',')
+
+        dot_source = visualize_instance(self.dt, instance)
+        self.network = dash_interactive_graphviz.DashInteractiveGraphviz(
+            dot_source=dot_source
+        )
+    
+        self.explanation.value = self.dt.explain(instance)
diff --git a/pages/application/DecisionTree/__pycache__/DecisionTreeComponent.cpython-38.pyc b/pages/application/DecisionTree/__pycache__/DecisionTreeComponent.cpython-38.pyc
deleted file mode 100644
index f5252efad4cc8643fff3886e9cf6c5e95f5a45f9..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1308
zcmWIL<>g{vU|?Xm!JRaZiGkrUh=Yuo85kHG7#J9e-53}cQW#Pga~N_NqZo6UqL^}-
zqnLA9qF8cSqgWY1a!fgFx$IHwU^a6OM=oa+CnJM9Lkdd@YYRgPYco?6R|;D&gC_e+
zkPVuwx0q9slW(zPl;q~zV)e``C@s0g=3iO@qB)Wib8?arle0A$Z*i7o7MCXGWLBkW
zGT!2GNlnfy&dkpXDN0RE2APA5nP9F}VPIfLWr$)-VTfW%VN79aVTfW*VNPLbVTfW$
zVNGFcVTfW)VGm}|<hUh@Y@&01Zb5!tYF<e)Ba*owHppmaP(bi8Fff!bEMQ#7P{R<<
zl)@0qu#(YFleq{KQn#2rbJK6J=cSgE=NEz8&QhG3leUtf2&Cy(ihf3ZZmNEAN@_uV
zX;F#3Q)y9ZVyS*XUQuSEZb4CgR%(fEN@{9BPHI_dk$yp9dTOzLVnIPpW^!UlW`3SN
zvO`fga98UURNmr<kI&4@EQyb22e}sHYzC$(Ib^*E<uIkmU}aDrgFM2*z`y|ZEhz3w
z7-|@s85tQ$7*iN)7{wV<n8X>tG_yEE3QI3zEmH|o4HGC>o0%CIN|>`)QrJ@1dl_pO
z;#ooAqsie{rANR!B}Ivud8sM!x%nxnImP<F81<`|^-@Ye(Vte7pBtZ+nUi{pxwtI7
zN*>dsWLT`}6_=%JGTve?E=ty9yTw{il$lpj#KFM8Py`ALa1<4BgSb2l3=EpgMZ63Q
z47b=*5=#<`Q%i0!rIdgp3PSLM<k(6JQW8s2L3)Zo<}fgFFiJ7<FjmQ64^W75l;8vf
zEeL~y6J&1<Ll!9MFo1%l2$cIY8H+&TnoJP)aAf8cmn7yTr$XETN;*Yg$4Ej<k1x+G
z$%s#_04I#3%$&@UN<ny3Ggje9894R&X|jRROkQGcYJB`Hu6R%sl;(igJn`{`rHMHZ
znIZvDxJiHrDG&j67J>jdu!xg^fq@m|6gCD11`Z|;77jMBn4c#1EtZtT;*26tKtQ4a
zl){QYii<#sqqM-Ws)v-^^^ikYue2mHr&uqgq%5-v9G66EC`wI*qyUiPkU|vfbdalW
ZaoFVMr<CTT+JUlpF~~JCj694yOaLJOOZor+

diff --git a/pages/application/DecisionTree/map/australian/ITI/australian.map b/pages/application/DecisionTree/map/australian/australian.map
similarity index 100%
rename from pages/application/DecisionTree/map/australian/ITI/australian.map
rename to pages/application/DecisionTree/map/australian/australian.map
diff --git a/pages/application/DecisionTree/map/cancer/ITI/cancer.map b/pages/application/DecisionTree/map/cancer/cancer.map
similarity index 100%
rename from pages/application/DecisionTree/map/cancer/ITI/cancer.map
rename to pages/application/DecisionTree/map/cancer/cancer.map
diff --git a/pages/application/DecisionTree/map/car/ITI/car.map b/pages/application/DecisionTree/map/car/car.map
similarity index 100%
rename from pages/application/DecisionTree/map/car/ITI/car.map
rename to pages/application/DecisionTree/map/car/car.map
diff --git a/pages/application/DecisionTree/utils/__pycache__/_fileio.cpython-38.pyc b/pages/application/DecisionTree/utils/__pycache__/_fileio.cpython-38.pyc
deleted file mode 100644
index 7cbc69f32333697f6146d5270c0652e8d7410e9a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 3674
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
E0asMB-T(jq

diff --git a/pages/application/DecisionTree/utils/__pycache__/dtree.cpython-38.pyc b/pages/application/DecisionTree/utils/__pycache__/dtree.cpython-38.pyc
deleted file mode 100644
index 94a4fc9fd19d1416fbac2d2b8582f1ad6d77b472..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 21333
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

diff --git a/pages/application/DecisionTree/utils/__pycache__/dtviz.cpython-38.pyc b/pages/application/DecisionTree/utils/__pycache__/dtviz.cpython-38.pyc
deleted file mode 100644
index 0ad57d58fe6e5a6d997f8691f57169a46e1f1829..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1471
zcmWIL<>g{vU|?{%#GSO3je+4Yh=Yuo7#J8F7#J9eEf^RWQW#Pga~N_NqZk=MY^EHh
zD5eyK6s8>JC}uFt62$_hS)*7}m{V9<7^2uxn1UHJSzm(8)MUKH<C2=3S)7@l7gCg(
z>X*z65{6<<1_lNY28k7SF)}ceFk~^NfFM&ZQ!R4}a}9GAQwoa>LkepRa~5+7n+-z;
zLl%n*L##tBOAT`tYYj^Z`vNwQSPhFLLl%1t%R;6Uj$S54h8pGt95pNp8EaWnIBQt5
zI8(T67*e>wEFK$%LX8fFEG`#@SUWUxxIpG$HH8<f7uA#kkj*X(v0FMBL97l&Nro(L
z7lznHwO}_v_%&>j47Kbv%nNur7;D%ULfpm+Vb^k$@YS$2Gp6umGfiMD(&}Ky;x7?^
zo5fxt0G8#KWJqUfW|U;8;mG1l5wKy%(@GIsz?LGkK(L*moiU9mML0#Ig#)76g`t^o
zA>%?unA#LkFpI~AAw>+#61QO}JXW}^hB-?pMIuGAmk|_NS;93;HH;}zps-7k4rb7l
zsnX_BfP%2h;?l&N%&Js{L<N@+h4Rdj42ATf#Da{n%ql&wst`@4D2|lOqSWM))RbFn
z#U(|V$tAbg)AEbT6N^%AF{fk}-C_!HiefKH%*#&6EUMzFHmHrs)2KGA)zoCX#hR0t
zl$sO8mYi9XoRfNswKyZOAoV3L0|Ub?j<o!|lH$y&)LU%Dg{6r_sV^BB7#NDc1Umx*
zLltwiLG3L@u4Ir1G+i-(k}w+s1A{Oq8LKfcFr+ioFvQB$f})9`1Vl1{BZs+$5fZQy
znF?8g8CEiAvfN@#E6YtRxW!shS^#oNUVchy@hz6L)Wi}^rdv#U2Dg~Y5_48E-C{~9
zxy6)cSj5M`!0;<dKO;XkRX;f;wIIK=s6^kXv?w*PRKFmvC^J#FpeR2pwL~{1HMJlo
zwJfzrzaTL^wOBu~pdcqRIk6-&KTjVykLZ_{Wabp>r<8y~x1jPCmrYJ)aY=H1Zh@US
z$UiI$3=C`xObk`BWw3bBv&qR%PRuE`(?h6z3CiW^vu1+8OZzC!#G<17@?uaxGN<I1
zXfobnDJaTM*JQfIQjl1Zaf>Z2zbH4c#7|S?7FR(fEOw*V9Nj??9mScNlAao$SW;3H
z#RZlGvx@{77#N~B5>ry*LE%&+2Z|aYaI~eTmVm`>v4Aw)Vl7E6$}PUdQkIxg4AB5G
zFp2}D48*#{oRgYY#1GOh0MgHrm7ke+i!CRyGQYGWiY28ev0Rh0NDL&!oR(X1i!Hyj
zq@c9q7GwG?#=KiBAlq*-mfd2?GrGkBG7lUrMWP_{BtV)t;b8?bqu2<PQds#IxtO?^
zxftabxfr<^d6>BvW#B9UMgc}1Mjl2!MlB{SMlMD^W*){OeFg>wP4-(_;0V-1ia<T&
z2-E{dpk7KzQEF-tC@J1zOHVDyFDSXilwVvV4{{E3ab+>sG>AJvX$ZmsJC?&HH$SB`
SC)Ey=1ByZM$iv71fvf;PS%oS9

diff --git a/pages/application/DecisionTree/utils/dtree.py b/pages/application/DecisionTree/utils/dtree.py
index 32adfde..fd881ee 100644
--- a/pages/application/DecisionTree/utils/dtree.py
+++ b/pages/application/DecisionTree/utils/dtree.py
@@ -351,8 +351,7 @@ class DecisionTree():
         # returning the set of sets with no duplicates
         return list(dict.fromkeys(sets))
 
-    def explain(self, inst, enum=1, pathlits=False, solver='g3', xtype='abd',
-            htype='sorted'):
+    def explain(self, inst, enum=5, pathlits=False, solver='g3', htype='sorted'):
         """
             Compute a given number of explanations.
         """
@@ -364,14 +363,16 @@ class DecisionTree():
         else:
             # input expected by Yacine - 'value1,value2,...'
             inst = list(map(lambda i : tuple(['f{0}'.format(i[0]), int(i[1])]), [(i, j) for i,j in enumerate(inst)]))
-
+        print(inst)
         inst_orig = inst[:]
         path, term, depth = self.execute(inst, pathlits)
+        print(path)
 
+        explanation = str(inst) + "\n \n"
         #print('c instance: IF {0} THEN class={1}'.format(' AND '.join([self.fvmap[p] for p in inst_orig]), term))
         #print(term)
-        print('c instance: IF {0} THEN class={1}'.format(' AND '.join([self.fvmap[ inst_orig[self.feids[self.nodes[n].feat]] ] for n in path]), term))
-        print('c path len:', depth)
+        explanation += 'c instance: IF {0} THEN class={1}'.format(' AND '.join([self.fvmap[ inst_orig[self.feids[self.nodes[n].feat]] ] for n in path]), term) + "\n"
+        explanation +='c path len:'+ str(depth)+ "\n \n \n"
 
         if self.ohmap.dir:
             f2v = {fv[0]: fv[1] for fv in inst}
@@ -383,30 +384,34 @@ class DecisionTree():
         # computing the sets to hit
         to_hit = self.prepare_sets(inst, term)
 
-        if xtype == 'abd':
-            self.enumerate_abductive(to_hit, enum, solver, htype, term)
-        else:
-            self.enumerate_contrastive(to_hit, term)
+        explanation += "Abductive explanation : " + "\n \n"
+        explanation += self.enumerate_abductive(to_hit, enum, solver, htype, term)
+        explanation += "Contrastive explanation : "+ "\n \n"
+        explanation += self.enumerate_contrastive(to_hit, term)
+
+        return explanation 
 
 
     def enumerate_abductive(self, to_hit, enum, solver, htype, term):
         """
             Enumerate abductive explanations.
         """
-
+        explanation = ""
         with Hitman(bootstrap_with=to_hit, solver=solver, htype=htype) as hitman:
             expls = []
             for i, expl in enumerate(hitman.enumerate(), 1):
-                print('c expl: IF {0} THEN class={1}'.format(' AND '.join([self.fvmap[p] for p in sorted(expl, key=lambda p: p[0])]), term))
+                explanation += 'c expl: IF {0} THEN class={1}'.format(' AND '.join([self.fvmap[p] for p in sorted(expl, key=lambda p: p[0])]), term) + "\n"
 
                 expls.append(expl)
                 if i == enum:
                     break
 
-            print('c nof expls:', i)
-            print('c min expl:', min([len(e) for e in expls]))
-            print('c max expl:', max([len(e) for e in expls]))
-            print('c avg expl: {0:.2f}'.format(sum([len(e) for e in expls]) / len(expls)))
+            explanation += 'c nof expls:' + str(i)+ "\n"
+            explanation += 'c min expl:'+ str( min([len(e) for e in expls]))+ "\n"
+            explanation += 'c max expl:'+ str( max([len(e) for e in expls]))+ "\n"
+            explanation += 'c avg expl: {0:.2f}'.format(sum([len(e) for e in expls]) / len(expls))+ "\n \n \n"
+
+        return explanation
 
     def enumerate_contrastive(self, to_hit, term):
         """
@@ -424,14 +429,17 @@ class DecisionTree():
         to_hit = [set(s) for s in to_hit]
         to_hit.sort(key=lambda s: len(s))
         expls = list(reduce(process_set, to_hit, []))
-
+        explanation = ""
         for expl in expls:
-            print('c expl: IF {0} THEN class!={1}'.format(' OR '.join(['!{0}'.format(self.fvmap[p]) for p in sorted(expl, key=lambda p: p[0])]), term))
+            explanation += 'c expl: IF {0} THEN class!={1}'.format(' OR '.join(['!{0}'.format(self.fvmap[p]) for p in sorted(expl, key=lambda p: p[0])]), term)+ "\n"
+
+
+        explanation +='c nof expls:'+ str(len(expls))+ "\n"
+        explanation +='c min expl:'+ str( min([len(e) for e in expls]))+ "\n"
+        explanation +='c max expl:'+ str( max([len(e) for e in expls]))+ "\n"
+        explanation +='c avg expl: {0:.2f}'.format(sum([len(e) for e in expls]) / len(expls))+ "\n"
 
-        print('c nof expls:', len(expls))
-        print('c min expl:', min([len(e) for e in expls]))
-        print('c max expl:', max([len(e) for e in expls]))
-        print('c avg expl: {0:.2f}'.format(sum([len(e) for e in expls]) / len(expls)))
+        return explanation
 
     def execute_path(self, path):
         """
diff --git a/pages/application/DecisionTree/utils/dtviz.py b/pages/application/DecisionTree/utils/dtviz.py
index bc91989..f91189d 100755
--- a/pages/application/DecisionTree/utils/dtviz.py
+++ b/pages/application/DecisionTree/utils/dtviz.py
@@ -19,7 +19,7 @@ import sys
 
 #
 #==============================================================================
-def visualize(dt, fmt, output):
+def visualize(dt):
     """
         Visualize a DT with graphviz.
     """
@@ -57,6 +57,67 @@ def visualize(dt, fmt, output):
             edge.attr['arrowsize'] = 0.8
 
     # saving file
+    g.in_edges
     g.layout(prog='dot')
-    g.draw(path=output, format=fmt)
+    return(g.to_string())
 
+#
+#==============================================================================
+def visualize_instance(dt, instance):
+    """
+        Visualize a DT with graphviz and plot the running instance.
+    """
+    if '=' in instance[0]:
+        instance = list(map(lambda i: tuple([i[0], int(i[1])]), [i.split('=') for i in instance]))
+
+    else:
+        instance = list(map(lambda i : tuple(['f{0}'.format(i[0]), int(i[1])]), [(i, j) for i,j in enumerate(instance)]))
+
+    #path that follows the instance - colored in blue
+    path, term, depth = dt.execute(instance)
+    edges_instance = []
+    for i in range (len(path)-1) :
+        edges_instance.append((path[i], path[i+1]))
+    edges_instance.append((path[-1],"term:"+term))
+
+    g = pygraphviz.AGraph(directed=True, strict=True)
+    g.edge_attr['dir'] = 'forward'
+
+    g.graph_attr['rankdir'] = 'TB'
+
+    # non-terminal nodes
+    for n in dt.nodes:
+        g.add_node(n, label='{0}\\n({1})'.format(dt.nodes[n].feat, n))
+        node = g.get_node(n)
+        node.attr['shape'] = 'circle'
+        node.attr['fontsize'] = 13
+
+    # terminal nodes
+    for n in dt.terms:
+        g.add_node(n, label='{0}\\n({1})'.format(dt.terms[n], n))
+        node = g.get_node(n)
+        node.attr['shape'] = 'square'
+        node.attr['fontsize'] = 13
+
+    # transitions
+    for n1 in dt.nodes:
+        for v in dt.nodes[n1].vals:
+            n2 = dt.nodes[n1].vals[v]
+            n2_type = g.get_node(n2).attr['shape']
+            g.add_edge(n1, n2)
+            edge = g.get_edge(n1, n2)
+            if len(v) == 1:
+                edge.attr['label'] = dt.fvmap[tuple([dt.nodes[n1].feat, tuple(v)[0]])]
+            else:
+                edge.attr['label'] = '{0}'.format('\n'.join([dt.fvmap[tuple([dt.nodes[n1].feat, val])] for val in tuple(v)]))
+            
+            #instance path in blue
+            if ((n1,n2) in edges_instance) or (n2_type=='square' and (n1, "term:"+ dt.terms[n2]) in edges_instance): 
+                edge.attr['color'] = 'blue'
+
+            edge.attr['fontsize'] = 10
+            edge.attr['arrowsize'] = 0.8
+
+    # saving file
+    g.layout(prog='dot')
+    return(g.to_string())
diff --git a/pages/application/DecisionTree/decision_tree_explicability.py b/pages/application/NaiveBayes/NaiveBayesComponent.py
similarity index 100%
rename from pages/application/DecisionTree/decision_tree_explicability.py
rename to pages/application/NaiveBayes/NaiveBayesComponent.py
diff --git a/pages/application/__pycache__/layout_application.cpython-38.pyc b/pages/application/__pycache__/layout_application.cpython-38.pyc
deleted file mode 100644
index 3d0fbeba535a0e13a76bce789577bdda2964becc..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 4500
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
aF!3;PXfiP}F*5ySV`pMu`p?43rwITw+#HMm

diff --git a/pages/application/__pycache__/utils_data.cpython-38.pyc b/pages/application/__pycache__/utils_data.cpython-38.pyc
deleted file mode 100644
index 3ce531e0f54819e127c16fbc8130884baa58077d..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1467
zcmWIL<>g{vU|=|PjXP;M3j@Pr5C<7EF)%PVFfcF_moP9eq%fo~<}l<kMlt3xMKR?v
zM=|HJM6u+uMzJ!2<d||8q98O|6nhFo3R4b86bG2*jN$^*+)+F!3@OYxyt#Z)d|*8+
zIsCZ-Q3APwQG#GTQ;txSPzq}bTMI*!a0*i}gC_e+kb5*)Z!xDNC*NYpD9O#a#p;<?
zP+D?}&A+q+L~|r3=Hw(LCTII;GTvg($t*5O$t+sQbc-#sI4v_L^%hH3er8@WBgh;m
z=3ro8U}IolU<P@Ofsuisogs}eg)xPxg`<R_gt3F6nX#GCg`t_TmNAo|mMN2=mN}E5
zmL-#+mK9k(c2_4;2SW<8Btr+2Btr^|BttD55}#F)p@yY~&4vL<j7^fEh7}~1!k)s^
z!cc;&pF@%XVrvZ(NCn7NkZm9yNCqSqYgfw-;n%QBGITPwGqf|dGo>-7aDv^AY#Ns&
zLkFWc%tYo6MjM906*bH?%pkK1&1x8Hm};17SZY|)Sc4ffx&2TA2o&!u3=9mypa7C#
zU|>jRs9}f|sb%b7NMVp<s9}_3NMV#@n8;Mf63n2<bc-b=u_W;pdu~pANo7ImN{|qf
zp201~OijijZUzR1B3=dthF?DV8Tq-X`pGG&1^J~#CHhXKMX8CU`UQDKnTfguMfq8&
zCAul8sRcQyWvNB_1&QgY#rlZ_1v#0?i6xo&dHSU#nK{MrAglBWDsOSwfTB7%Kexb+
z2jonUd)XM67^)OfD@uwIlS?2fZF2IH6LX5~^bpD+P6BDV#R<}%pO>0fa!Wk50&G=M
zW=>{FWqe{zdVW!6Nk%S4z<^RsFvvy$Sio@9GS)Cc0t4(*FtL)Uh>wAR;TB^Wm<u65
zT8ac27#PGr2Ed#g#aB|4n3<QF5}%u&lA2SDVI?TBg5yFDW~Ev!BPek|;sc!g7$Ff}
z!`RH!%vi(%bC4zz*t@qlGxLf|67!N%i;F<Xzet3Efng;xM2;O4rp2ixMf|Y1DuO#y
z3gl3b{h+XwV<-YST$A}0Yf)ledg?9aoYcG`b_NCpO%8}rJb8(^sl`x#+!9F1gvJ9*
zL>M6gchD_<upm4tif?g(1wau}1db-gEJ*N)gWLyl+ASXBNahB)Q2~^>8KoFmAdpFb
zQGii|QG`*6k%du&k%!TMk&CHF5#&@6P<k!SC<3MOB2a7=fg+-a6=XQbr?(_i5{on9
zlk)RRic5+T3*wPn#+;Ire2c9tvlvtyu@xldr6d;LVk$_v#hO=|TTpq6DX-ubQ+_cx
zOm4B~7wZ)ymShxhf{X?gwYNAka|`l|N^&xjz;1zf3zRz`EU@1=Y;yBcN^?@}KxJ03
T5Ca2)3?l~|@-PZ8axeh^d}~nE

diff --git a/pages/application/component.svg b/pages/application/component.svg
deleted file mode 100644
index ad48872..0000000
--- a/pages/application/component.svg
+++ /dev/null
@@ -1,467 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
- "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<!-- Generated by graphviz version 2.43.0 (0)
- -->
-<!-- Pages: 1 -->
-<svg width="749pt" height="797pt"
- viewBox="0.00 0.00 749.00 796.82" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 792.82)">
-<polygon fill="white" stroke="transparent" points="-4,4 -4,-792.82 745,-792.82 745,4 -4,4"/>
-<!-- 1 -->
-<g id="node1" class="node">
-<title>1</title>
-<ellipse fill="none" stroke="black" cx="217" cy="-763.32" rx="25.5" ry="25.5"/>
-<text text-anchor="middle" x="217" y="-766.92" font-family="Times,serif" font-size="13.00">f7</text>
-<text text-anchor="middle" x="217" y="-752.92" font-family="Times,serif" font-size="13.00">(1)</text>
-</g>
-<!-- 2 -->
-<g id="node2" class="node">
-<title>2</title>
-<ellipse fill="none" stroke="black" cx="180" cy="-664" rx="27" ry="27"/>
-<text text-anchor="middle" x="180" y="-667.6" font-family="Times,serif" font-size="13.00">f11</text>
-<text text-anchor="middle" x="180" y="-653.6" font-family="Times,serif" font-size="13.00">(2)</text>
-</g>
-<!-- 1&#45;&gt;2 -->
-<g id="edge1" class="edge">
-<title>1&#45;&gt;2</title>
-<path fill="none" stroke="black" d="M208.23,-739.26C203.44,-726.66 197.44,-710.87 192.2,-697.1"/>
-<polygon fill="black" stroke="black" points="194.68,-695.74 189.22,-689.25 189.44,-697.72 194.68,-695.74"/>
-<text text-anchor="middle" x="213.5" y="-711.87" font-family="Times,serif" font-size="10.00">f7=0</text>
-</g>
-<!-- 9 -->
-<g id="node3" class="node">
-<title>9</title>
-<ellipse fill="none" stroke="black" cx="268" cy="-664" rx="25.5" ry="25.5"/>
-<text text-anchor="middle" x="268" y="-667.6" font-family="Times,serif" font-size="13.00">f8</text>
-<text text-anchor="middle" x="268" y="-653.6" font-family="Times,serif" font-size="13.00">(9)</text>
-</g>
-<!-- 1&#45;&gt;9 -->
-<g id="edge2" class="edge">
-<title>1&#45;&gt;9</title>
-<path fill="none" stroke="black" d="M228.57,-740.24C235.68,-726.67 244.87,-709.14 252.59,-694.41"/>
-<polygon fill="black" stroke="black" points="255.24,-695.38 256.47,-686.99 250.28,-692.78 255.24,-695.38"/>
-<text text-anchor="middle" x="257.5" y="-711.87" font-family="Times,serif" font-size="10.00">f7=1</text>
-</g>
-<!-- 3 -->
-<g id="node4" class="node">
-<title>3</title>
-<ellipse fill="none" stroke="black" cx="72" cy="-559.01" rx="25.5" ry="25.5"/>
-<text text-anchor="middle" x="72" y="-562.61" font-family="Times,serif" font-size="13.00">f6</text>
-<text text-anchor="middle" x="72" y="-548.61" font-family="Times,serif" font-size="13.00">(3)</text>
-</g>
-<!-- 2&#45;&gt;3 -->
-<g id="edge3" class="edge">
-<title>2&#45;&gt;3</title>
-<path fill="none" stroke="black" d="M160.94,-644.82C142.83,-627.55 115.53,-601.52 95.96,-582.86"/>
-<polygon fill="black" stroke="black" points="97.87,-580.82 90.15,-577.32 94.01,-584.87 97.87,-580.82"/>
-<text text-anchor="middle" x="148.5" y="-611.13" font-family="Times,serif" font-size="10.00">f11=3</text>
-</g>
-<!-- 6 -->
-<g id="node5" class="node">
-<title>6</title>
-<ellipse fill="none" stroke="black" cx="180" cy="-559.01" rx="25.5" ry="25.5"/>
-<text text-anchor="middle" x="180" y="-562.61" font-family="Times,serif" font-size="13.00">f6</text>
-<text text-anchor="middle" x="180" y="-548.61" font-family="Times,serif" font-size="13.00">(6)</text>
-</g>
-<!-- 2&#45;&gt;6 -->
-<g id="edge4" class="edge">
-<title>2&#45;&gt;6</title>
-<path fill="none" stroke="black" d="M180,-637.01C180,-623.56 180,-607.01 180,-592.7"/>
-<polygon fill="black" stroke="black" points="182.8,-592.57 180,-584.57 177.2,-592.57 182.8,-592.57"/>
-<text text-anchor="middle" x="195.5" y="-611.13" font-family="Times,serif" font-size="10.00">f11=2</text>
-</g>
-<!-- 10 -->
-<g id="node6" class="node">
-<title>10</title>
-<ellipse fill="none" stroke="black" cx="268" cy="-559.01" rx="31" ry="31"/>
-<text text-anchor="middle" x="268" y="-562.61" font-family="Times,serif" font-size="13.00">f3</text>
-<text text-anchor="middle" x="268" y="-548.61" font-family="Times,serif" font-size="13.00">(10)</text>
-</g>
-<!-- 9&#45;&gt;10 -->
-<g id="edge5" class="edge">
-<title>9&#45;&gt;10</title>
-<path fill="none" stroke="black" d="M268,-638.33C268,-626.4 268,-611.82 268,-598.48"/>
-<polygon fill="black" stroke="black" points="270.8,-598.37 268,-590.37 265.2,-598.37 270.8,-598.37"/>
-<text text-anchor="middle" x="280.5" y="-611.13" font-family="Times,serif" font-size="10.00">f8=0</text>
-</g>
-<!-- 23 -->
-<g id="node7" class="node">
-<title>23</title>
-<ellipse fill="none" stroke="black" cx="461" cy="-559.01" rx="31" ry="31"/>
-<text text-anchor="middle" x="461" y="-562.61" font-family="Times,serif" font-size="13.00">f9</text>
-<text text-anchor="middle" x="461" y="-548.61" font-family="Times,serif" font-size="13.00">(23)</text>
-</g>
-<!-- 9&#45;&gt;23 -->
-<g id="edge6" class="edge">
-<title>9&#45;&gt;23</title>
-<path fill="none" stroke="black" d="M290.14,-651.18C323.6,-633.33 387.34,-599.32 426.8,-578.27"/>
-<polygon fill="black" stroke="black" points="428.14,-580.73 433.87,-574.49 425.49,-575.79 428.14,-580.73"/>
-<text text-anchor="middle" x="383.5" y="-611.13" font-family="Times,serif" font-size="10.00">f8=1</text>
-</g>
-<!-- 4 -->
-<g id="node8" class="node">
-<title>4</title>
-<polygon fill="none" stroke="black" points="36,-467.79 0,-467.79 0,-431.79 36,-431.79 36,-467.79"/>
-<text text-anchor="middle" x="18" y="-453.39" font-family="Times,serif" font-size="13.00">0</text>
-<text text-anchor="middle" x="18" y="-439.39" font-family="Times,serif" font-size="13.00">(4)</text>
-</g>
-<!-- 3&#45;&gt;4 -->
-<g id="edge7" class="edge">
-<title>3&#45;&gt;4</title>
-<path fill="none" stroke="black" d="M55.85,-538.8C49.33,-530.34 42.15,-520.03 37,-509.9 31.55,-499.18 27.25,-486.58 24.12,-475.77"/>
-<polygon fill="black" stroke="black" points="26.78,-474.88 21.96,-467.91 21.38,-476.37 26.78,-474.88"/>
-<text text-anchor="middle" x="52.5" y="-501.9" font-family="Times,serif" font-size="10.00">f6=31</text>
-</g>
-<!-- 5 -->
-<g id="node9" class="node">
-<title>5</title>
-<polygon fill="none" stroke="black" points="90,-467.79 54,-467.79 54,-431.79 90,-431.79 90,-467.79"/>
-<text text-anchor="middle" x="72" y="-453.39" font-family="Times,serif" font-size="13.00">0</text>
-<text text-anchor="middle" x="72" y="-439.39" font-family="Times,serif" font-size="13.00">(5)</text>
-</g>
-<!-- 3&#45;&gt;5 -->
-<g id="edge8" class="edge">
-<title>3&#45;&gt;5</title>
-<path fill="none" stroke="black" d="M72,-533.41C72,-516.2 72,-493.27 72,-475.94"/>
-<polygon fill="black" stroke="black" points="74.8,-475.93 72,-467.93 69.2,-475.93 74.8,-475.93"/>
-<text text-anchor="middle" x="87.5" y="-501.9" font-family="Times,serif" font-size="10.00">f6=30</text>
-</g>
-<!-- 7 -->
-<g id="node10" class="node">
-<title>7</title>
-<polygon fill="none" stroke="black" points="144,-467.79 108,-467.79 108,-431.79 144,-431.79 144,-467.79"/>
-<text text-anchor="middle" x="126" y="-453.39" font-family="Times,serif" font-size="13.00">1</text>
-<text text-anchor="middle" x="126" y="-439.39" font-family="Times,serif" font-size="13.00">(7)</text>
-</g>
-<!-- 6&#45;&gt;7 -->
-<g id="edge9" class="edge">
-<title>6&#45;&gt;7</title>
-<path fill="none" stroke="black" d="M162.93,-540.03C155.59,-531.47 147.46,-520.74 142,-509.9 136.67,-499.31 132.94,-486.72 130.44,-475.89"/>
-<polygon fill="black" stroke="black" points="133.17,-475.25 128.76,-468.01 127.69,-476.42 133.17,-475.25"/>
-<text text-anchor="middle" x="160.5" y="-501.9" font-family="Times,serif" font-size="10.00">f6=133</text>
-</g>
-<!-- 8 -->
-<g id="node11" class="node">
-<title>8</title>
-<polygon fill="none" stroke="black" points="204,-467.79 168,-467.79 168,-431.79 204,-431.79 204,-467.79"/>
-<text text-anchor="middle" x="186" y="-453.39" font-family="Times,serif" font-size="13.00">0</text>
-<text text-anchor="middle" x="186" y="-439.39" font-family="Times,serif" font-size="13.00">(8)</text>
-</g>
-<!-- 6&#45;&gt;8 -->
-<g id="edge10" class="edge">
-<title>6&#45;&gt;8</title>
-<path fill="none" stroke="black" d="M181.38,-533.41C182.34,-516.2 183.62,-493.27 184.59,-475.94"/>
-<polygon fill="black" stroke="black" points="187.39,-476.07 185.04,-467.93 181.8,-475.76 187.39,-476.07"/>
-<text text-anchor="middle" x="202.5" y="-501.9" font-family="Times,serif" font-size="10.00">f6=132</text>
-</g>
-<!-- 11 -->
-<g id="node12" class="node">
-<title>11</title>
-<ellipse fill="none" stroke="black" cx="253" cy="-449.79" rx="31" ry="31"/>
-<text text-anchor="middle" x="253" y="-453.39" font-family="Times,serif" font-size="13.00">f12</text>
-<text text-anchor="middle" x="253" y="-439.39" font-family="Times,serif" font-size="13.00">(11)</text>
-</g>
-<!-- 10&#45;&gt;11 -->
-<g id="edge11" class="edge">
-<title>10&#45;&gt;11</title>
-<path fill="none" stroke="black" d="M263.82,-528.15C262.13,-516.05 260.15,-501.93 258.36,-489.09"/>
-<polygon fill="black" stroke="black" points="261.09,-488.4 257.21,-480.87 255.55,-489.18 261.09,-488.4"/>
-<text text-anchor="middle" x="273.5" y="-501.9" font-family="Times,serif" font-size="10.00">f3=1</text>
-</g>
-<!-- 20 -->
-<g id="node13" class="node">
-<title>20</title>
-<ellipse fill="none" stroke="black" cx="333" cy="-449.79" rx="31" ry="31"/>
-<text text-anchor="middle" x="333" y="-453.39" font-family="Times,serif" font-size="13.00">f4</text>
-<text text-anchor="middle" x="333" y="-439.39" font-family="Times,serif" font-size="13.00">(20)</text>
-</g>
-<!-- 10&#45;&gt;20 -->
-<g id="edge12" class="edge">
-<title>10&#45;&gt;20</title>
-<path fill="none" stroke="black" d="M283.73,-532.06C292.53,-517.55 303.59,-499.31 312.99,-483.79"/>
-<polygon fill="black" stroke="black" points="315.43,-485.17 317.18,-476.88 310.64,-482.27 315.43,-485.17"/>
-<text text-anchor="middle" x="315.5" y="-501.9" font-family="Times,serif" font-size="10.00">f3=0</text>
-</g>
-<!-- 24 -->
-<g id="node14" class="node">
-<title>24</title>
-<ellipse fill="none" stroke="black" cx="461" cy="-449.79" rx="31" ry="31"/>
-<text text-anchor="middle" x="461" y="-453.39" font-family="Times,serif" font-size="13.00">f5</text>
-<text text-anchor="middle" x="461" y="-439.39" font-family="Times,serif" font-size="13.00">(24)</text>
-</g>
-<!-- 23&#45;&gt;24 -->
-<g id="edge13" class="edge">
-<title>23&#45;&gt;24</title>
-<path fill="none" stroke="black" d="M461,-527.86C461,-515.87 461,-501.93 461,-489.22"/>
-<polygon fill="black" stroke="black" points="463.8,-489.09 461,-481.09 458.2,-489.09 463.8,-489.09"/>
-<text text-anchor="middle" x="476.5" y="-501.9" font-family="Times,serif" font-size="10.00">f9=23</text>
-</g>
-<!-- 27 -->
-<g id="node15" class="node">
-<title>27</title>
-<ellipse fill="none" stroke="black" cx="574" cy="-449.79" rx="31" ry="31"/>
-<text text-anchor="middle" x="574" y="-453.39" font-family="Times,serif" font-size="13.00">f4</text>
-<text text-anchor="middle" x="574" y="-439.39" font-family="Times,serif" font-size="13.00">(27)</text>
-</g>
-<!-- 23&#45;&gt;27 -->
-<g id="edge14" class="edge">
-<title>23&#45;&gt;27</title>
-<path fill="none" stroke="black" d="M483.33,-536.83C501.2,-519.87 526.39,-495.97 545.68,-477.66"/>
-<polygon fill="black" stroke="black" points="547.84,-479.47 551.72,-471.93 543.99,-475.4 547.84,-479.47"/>
-<text text-anchor="middle" x="536.5" y="-501.9" font-family="Times,serif" font-size="10.00">f9=22</text>
-</g>
-<!-- 13 -->
-<g id="node16" class="node">
-<title>13</title>
-<ellipse fill="none" stroke="black" cx="182" cy="-340.56" rx="31" ry="31"/>
-<text text-anchor="middle" x="182" y="-344.16" font-family="Times,serif" font-size="13.00">f5</text>
-<text text-anchor="middle" x="182" y="-330.16" font-family="Times,serif" font-size="13.00">(13)</text>
-</g>
-<!-- 11&#45;&gt;13 -->
-<g id="edge15" class="edge">
-<title>11&#45;&gt;13</title>
-<path fill="none" stroke="black" d="M230,-428.79C221.66,-420.68 212.65,-410.84 206,-400.68 201.45,-393.73 197.51,-385.77 194.2,-378.01"/>
-<polygon fill="black" stroke="black" points="196.7,-376.72 191.12,-370.34 191.5,-378.81 196.7,-376.72"/>
-<text text-anchor="middle" x="227.5" y="-392.68" font-family="Times,serif" font-size="10.00">f12=162</text>
-</g>
-<!-- 12 -->
-<g id="node17" class="node">
-<title>12</title>
-<polygon fill="none" stroke="black" points="275,-362.56 231,-362.56 231,-318.56 275,-318.56 275,-362.56"/>
-<text text-anchor="middle" x="253" y="-344.16" font-family="Times,serif" font-size="13.00">1</text>
-<text text-anchor="middle" x="253" y="-330.16" font-family="Times,serif" font-size="13.00">(12)</text>
-</g>
-<!-- 11&#45;&gt;12 -->
-<g id="edge16" class="edge">
-<title>11&#45;&gt;12</title>
-<path fill="none" stroke="black" d="M253,-418.64C253,-403.72 253,-385.79 253,-370.98"/>
-<polygon fill="black" stroke="black" points="255.8,-370.67 253,-362.67 250.2,-370.67 255.8,-370.67"/>
-<text text-anchor="middle" x="274.5" y="-392.68" font-family="Times,serif" font-size="10.00">f12=163</text>
-</g>
-<!-- 21 -->
-<g id="node18" class="node">
-<title>21</title>
-<polygon fill="none" stroke="black" points="339,-362.56 295,-362.56 295,-318.56 339,-318.56 339,-362.56"/>
-<text text-anchor="middle" x="317" y="-344.16" font-family="Times,serif" font-size="13.00">1</text>
-<text text-anchor="middle" x="317" y="-330.16" font-family="Times,serif" font-size="13.00">(21)</text>
-</g>
-<!-- 20&#45;&gt;21 -->
-<g id="edge17" class="edge">
-<title>20&#45;&gt;21</title>
-<path fill="none" stroke="black" d="M328.54,-418.92C326.3,-403.86 323.58,-385.67 321.35,-370.72"/>
-<polygon fill="black" stroke="black" points="324.12,-370.28 320.17,-362.78 318.58,-371.1 324.12,-370.28"/>
-<text text-anchor="middle" x="341.5" y="-392.68" font-family="Times,serif" font-size="10.00">f4=19</text>
-</g>
-<!-- 22 -->
-<g id="node19" class="node">
-<title>22</title>
-<polygon fill="none" stroke="black" points="401,-362.56 357,-362.56 357,-318.56 401,-318.56 401,-362.56"/>
-<text text-anchor="middle" x="379" y="-344.16" font-family="Times,serif" font-size="13.00">0</text>
-<text text-anchor="middle" x="379" y="-330.16" font-family="Times,serif" font-size="13.00">(22)</text>
-</g>
-<!-- 20&#45;&gt;22 -->
-<g id="edge18" class="edge">
-<title>20&#45;&gt;22</title>
-<path fill="none" stroke="black" d="M347.09,-421.8C350.48,-414.98 353.99,-407.61 357,-400.68 361.27,-390.85 365.48,-379.9 369.07,-370.12"/>
-<polygon fill="black" stroke="black" points="371.71,-371.05 371.8,-362.57 366.45,-369.14 371.71,-371.05"/>
-<text text-anchor="middle" x="376.5" y="-392.68" font-family="Times,serif" font-size="10.00">f4=18</text>
-</g>
-<!-- 25 -->
-<g id="node20" class="node">
-<title>25</title>
-<polygon fill="none" stroke="black" points="463,-362.56 419,-362.56 419,-318.56 463,-318.56 463,-362.56"/>
-<text text-anchor="middle" x="441" y="-344.16" font-family="Times,serif" font-size="13.00">1</text>
-<text text-anchor="middle" x="441" y="-330.16" font-family="Times,serif" font-size="13.00">(25)</text>
-</g>
-<!-- 24&#45;&gt;25 -->
-<g id="edge19" class="edge">
-<title>24&#45;&gt;25</title>
-<path fill="none" stroke="black" d="M453.91,-419.35C452.53,-413.19 451.15,-406.74 450,-400.68 448.15,-390.97 446.44,-380.32 445.01,-370.76"/>
-<polygon fill="black" stroke="black" points="447.76,-370.2 443.83,-362.69 442.22,-371.01 447.76,-370.2"/>
-<text text-anchor="middle" x="465.5" y="-392.68" font-family="Times,serif" font-size="10.00">f5=15</text>
-</g>
-<!-- 26 -->
-<g id="node21" class="node">
-<title>26</title>
-<polygon fill="none" stroke="black" points="525,-362.56 481,-362.56 481,-318.56 525,-318.56 525,-362.56"/>
-<text text-anchor="middle" x="503" y="-344.16" font-family="Times,serif" font-size="13.00">0</text>
-<text text-anchor="middle" x="503" y="-330.16" font-family="Times,serif" font-size="13.00">(26)</text>
-</g>
-<!-- 24&#45;&gt;26 -->
-<g id="edge20" class="edge">
-<title>24&#45;&gt;26</title>
-<path fill="none" stroke="black" d="M472.81,-420.88C475.55,-414.27 478.41,-407.24 481,-400.68 484.86,-390.89 488.92,-380.12 492.48,-370.48"/>
-<polygon fill="black" stroke="black" points="495.21,-371.17 495.35,-362.7 489.95,-369.24 495.21,-371.17"/>
-<text text-anchor="middle" x="500.5" y="-392.68" font-family="Times,serif" font-size="10.00">f5=14</text>
-</g>
-<!-- 28 -->
-<g id="node22" class="node">
-<title>28</title>
-<ellipse fill="none" stroke="black" cx="574" cy="-340.56" rx="31" ry="31"/>
-<text text-anchor="middle" x="574" y="-344.16" font-family="Times,serif" font-size="13.00">f6</text>
-<text text-anchor="middle" x="574" y="-330.16" font-family="Times,serif" font-size="13.00">(28)</text>
-</g>
-<!-- 27&#45;&gt;28 -->
-<g id="edge21" class="edge">
-<title>27&#45;&gt;28</title>
-<path fill="none" stroke="black" d="M574,-418.64C574,-406.64 574,-392.7 574,-380"/>
-<polygon fill="black" stroke="black" points="576.8,-379.86 574,-371.86 571.2,-379.86 576.8,-379.86"/>
-<text text-anchor="middle" x="589.5" y="-392.68" font-family="Times,serif" font-size="10.00">f4=15</text>
-</g>
-<!-- 31 -->
-<g id="node23" class="node">
-<title>31</title>
-<ellipse fill="none" stroke="black" cx="654" cy="-340.56" rx="31" ry="31"/>
-<text text-anchor="middle" x="654" y="-344.16" font-family="Times,serif" font-size="13.00">f13</text>
-<text text-anchor="middle" x="654" y="-330.16" font-family="Times,serif" font-size="13.00">(31)</text>
-</g>
-<!-- 27&#45;&gt;31 -->
-<g id="edge22" class="edge">
-<title>27&#45;&gt;31</title>
-<path fill="none" stroke="black" d="M592.15,-424.46C603.53,-409.21 618.3,-389.41 630.56,-372.98"/>
-<polygon fill="black" stroke="black" points="633.1,-374.26 635.64,-366.17 628.61,-370.91 633.1,-374.26"/>
-<text text-anchor="middle" x="632.5" y="-392.68" font-family="Times,serif" font-size="10.00">f4=14</text>
-</g>
-<!-- 15 -->
-<g id="node24" class="node">
-<title>15</title>
-<ellipse fill="none" stroke="black" cx="141" cy="-231.34" rx="31" ry="31"/>
-<text text-anchor="middle" x="141" y="-234.94" font-family="Times,serif" font-size="13.00">f5</text>
-<text text-anchor="middle" x="141" y="-220.94" font-family="Times,serif" font-size="13.00">(15)</text>
-</g>
-<!-- 13&#45;&gt;15 -->
-<g id="edge23" class="edge">
-<title>13&#45;&gt;15</title>
-<path fill="none" stroke="black" d="M171.12,-311.11C166.08,-297.92 160.03,-282.1 154.68,-268.11"/>
-<polygon fill="black" stroke="black" points="157.26,-267.02 151.79,-260.55 152.03,-269.02 157.26,-267.02"/>
-<text text-anchor="middle" x="175.5" y="-283.45" font-family="Times,serif" font-size="10.00">f5=2</text>
-</g>
-<!-- 14 -->
-<g id="node25" class="node">
-<title>14</title>
-<polygon fill="none" stroke="black" points="234,-253.34 190,-253.34 190,-209.34 234,-209.34 234,-253.34"/>
-<text text-anchor="middle" x="212" y="-234.94" font-family="Times,serif" font-size="13.00">1</text>
-<text text-anchor="middle" x="212" y="-220.94" font-family="Times,serif" font-size="13.00">(14)</text>
-</g>
-<!-- 13&#45;&gt;14 -->
-<g id="edge24" class="edge">
-<title>13&#45;&gt;14</title>
-<path fill="none" stroke="black" d="M190.2,-310.26C194.44,-295.09 199.61,-276.62 203.85,-261.47"/>
-<polygon fill="black" stroke="black" points="206.64,-261.9 206.1,-253.44 201.25,-260.39 206.64,-261.9"/>
-<text text-anchor="middle" x="210.5" y="-283.45" font-family="Times,serif" font-size="10.00">f5=3</text>
-</g>
-<!-- 29 -->
-<g id="node30" class="node">
-<title>29</title>
-<polygon fill="none" stroke="black" points="538,-253.34 494,-253.34 494,-209.34 538,-209.34 538,-253.34"/>
-<text text-anchor="middle" x="516" y="-234.94" font-family="Times,serif" font-size="13.00">0</text>
-<text text-anchor="middle" x="516" y="-220.94" font-family="Times,serif" font-size="13.00">(29)</text>
-</g>
-<!-- 28&#45;&gt;29 -->
-<g id="edge29" class="edge">
-<title>28&#45;&gt;29</title>
-<path fill="none" stroke="black" d="M553.1,-317.31C546.63,-309.56 539.91,-300.53 535,-291.45 529.9,-282.03 525.89,-271.01 522.9,-261.05"/>
-<polygon fill="black" stroke="black" points="525.59,-260.28 520.72,-253.35 520.21,-261.81 525.59,-260.28"/>
-<text text-anchor="middle" x="553.5" y="-283.45" font-family="Times,serif" font-size="10.00">f6=111</text>
-</g>
-<!-- 30 -->
-<g id="node31" class="node">
-<title>30</title>
-<polygon fill="none" stroke="black" points="600,-253.34 556,-253.34 556,-209.34 600,-209.34 600,-253.34"/>
-<text text-anchor="middle" x="578" y="-234.94" font-family="Times,serif" font-size="13.00">1</text>
-<text text-anchor="middle" x="578" y="-220.94" font-family="Times,serif" font-size="13.00">(30)</text>
-</g>
-<!-- 28&#45;&gt;30 -->
-<g id="edge30" class="edge">
-<title>28&#45;&gt;30</title>
-<path fill="none" stroke="black" d="M575.12,-309.41C575.68,-294.5 576.35,-276.57 576.9,-261.76"/>
-<polygon fill="black" stroke="black" points="579.71,-261.54 577.21,-253.44 574.11,-261.33 579.71,-261.54"/>
-<text text-anchor="middle" x="595.5" y="-283.45" font-family="Times,serif" font-size="10.00">f6=110</text>
-</g>
-<!-- 32 -->
-<g id="node32" class="node">
-<title>32</title>
-<polygon fill="none" stroke="black" points="671,-253.34 627,-253.34 627,-209.34 671,-209.34 671,-253.34"/>
-<text text-anchor="middle" x="649" y="-234.94" font-family="Times,serif" font-size="13.00">0</text>
-<text text-anchor="middle" x="649" y="-220.94" font-family="Times,serif" font-size="13.00">(32)</text>
-</g>
-<!-- 31&#45;&gt;32 -->
-<g id="edge31" class="edge">
-<title>31&#45;&gt;32</title>
-<path fill="none" stroke="black" d="M651.2,-309.56C650.72,-303.56 650.29,-297.31 650,-291.45 649.51,-281.7 649.25,-271.03 649.11,-261.48"/>
-<polygon fill="black" stroke="black" points="651.91,-261.39 649.02,-253.42 646.31,-261.45 651.91,-261.39"/>
-<text text-anchor="middle" x="671.5" y="-283.45" font-family="Times,serif" font-size="10.00">f13=345</text>
-</g>
-<!-- 33 -->
-<g id="node33" class="node">
-<title>33</title>
-<polygon fill="none" stroke="black" points="737,-253.34 693,-253.34 693,-209.34 737,-209.34 737,-253.34"/>
-<text text-anchor="middle" x="715" y="-234.94" font-family="Times,serif" font-size="13.00">1</text>
-<text text-anchor="middle" x="715" y="-220.94" font-family="Times,serif" font-size="13.00">(33)</text>
-</g>
-<!-- 31&#45;&gt;33 -->
-<g id="edge32" class="edge">
-<title>31&#45;&gt;33</title>
-<path fill="none" stroke="black" d="M674.66,-317.18C681.12,-309.42 687.88,-300.42 693,-291.45 698.35,-282.07 702.85,-271.14 706.35,-261.25"/>
-<polygon fill="black" stroke="black" points="709.03,-262.08 708.94,-253.6 703.72,-260.28 709.03,-262.08"/>
-<text text-anchor="middle" x="719.5" y="-283.45" font-family="Times,serif" font-size="10.00">f13=344</text>
-</g>
-<!-- 16 -->
-<g id="node26" class="node">
-<title>16</title>
-<ellipse fill="none" stroke="black" cx="105" cy="-122.11" rx="31" ry="31"/>
-<text text-anchor="middle" x="105" y="-125.71" font-family="Times,serif" font-size="13.00">f13</text>
-<text text-anchor="middle" x="105" y="-111.71" font-family="Times,serif" font-size="13.00">(16)</text>
-</g>
-<!-- 15&#45;&gt;16 -->
-<g id="edge25" class="edge">
-<title>15&#45;&gt;16</title>
-<path fill="none" stroke="black" d="M130.04,-201.95C127.65,-195.49 125.18,-188.64 123,-182.23 120.57,-175.06 118.12,-167.36 115.83,-159.96"/>
-<polygon fill="black" stroke="black" points="118.44,-158.93 113.43,-152.09 113.09,-160.56 118.44,-158.93"/>
-<text text-anchor="middle" x="138.5" y="-174.23" font-family="Times,serif" font-size="10.00">f5=15</text>
-</g>
-<!-- 19 -->
-<g id="node27" class="node">
-<title>19</title>
-<polygon fill="none" stroke="black" points="198,-144.11 154,-144.11 154,-100.11 198,-100.11 198,-144.11"/>
-<text text-anchor="middle" x="176" y="-125.71" font-family="Times,serif" font-size="13.00">0</text>
-<text text-anchor="middle" x="176" y="-111.71" font-family="Times,serif" font-size="13.00">(19)</text>
-</g>
-<!-- 15&#45;&gt;19 -->
-<g id="edge26" class="edge">
-<title>15&#45;&gt;19</title>
-<path fill="none" stroke="black" d="M150.38,-201.6C155.38,-186.28 161.52,-167.48 166.53,-152.12"/>
-<polygon fill="black" stroke="black" points="169.22,-152.91 169.04,-144.44 163.89,-151.17 169.22,-152.91"/>
-<text text-anchor="middle" x="175.5" y="-174.23" font-family="Times,serif" font-size="10.00">f5=14</text>
-</g>
-<!-- 17 -->
-<g id="node28" class="node">
-<title>17</title>
-<polygon fill="none" stroke="black" points="94,-44 50,-44 50,0 94,0 94,-44"/>
-<text text-anchor="middle" x="72" y="-25.6" font-family="Times,serif" font-size="13.00">0</text>
-<text text-anchor="middle" x="72" y="-11.6" font-family="Times,serif" font-size="13.00">(17)</text>
-</g>
-<!-- 16&#45;&gt;17 -->
-<g id="edge27" class="edge">
-<title>16&#45;&gt;17</title>
-<path fill="none" stroke="black" d="M85.49,-97.53C80.45,-90.12 75.7,-81.62 73,-73 70.99,-66.59 70.11,-59.49 69.85,-52.7"/>
-<polygon fill="black" stroke="black" points="72.65,-52.34 69.82,-44.34 67.05,-52.35 72.65,-52.34"/>
-<text text-anchor="middle" x="94.5" y="-65" font-family="Times,serif" font-size="10.00">f13=199</text>
-</g>
-<!-- 18 -->
-<g id="node29" class="node">
-<title>18</title>
-<polygon fill="none" stroke="black" points="160,-44 116,-44 116,0 160,0 160,-44"/>
-<text text-anchor="middle" x="138" y="-25.6" font-family="Times,serif" font-size="13.00">1</text>
-<text text-anchor="middle" x="138" y="-11.6" font-family="Times,serif" font-size="13.00">(18)</text>
-</g>
-<!-- 16&#45;&gt;18 -->
-<g id="edge28" class="edge">
-<title>16&#45;&gt;18</title>
-<path fill="none" stroke="black" d="M114.63,-92.47C118.95,-79.64 124.03,-64.53 128.35,-51.69"/>
-<polygon fill="black" stroke="black" points="131.03,-52.51 130.93,-44.03 125.72,-50.72 131.03,-52.51"/>
-<text text-anchor="middle" x="146.5" y="-65" font-family="Times,serif" font-size="10.00">f13=198</text>
-</g>
-</g>
-</svg>
diff --git a/pages/application/layout_application.py b/pages/application/layout_application.py
index 28c6fb6..4972774 100644
--- a/pages/application/layout_application.py
+++ b/pages/application/layout_application.py
@@ -1,7 +1,6 @@
-from dash import dcc, html, Input, Output, callback
+from dash import dcc, html, Input, Output
 import dash
 import dash_bootstrap_components as dbc
-import visdcc
 import pandas as pd
 import numpy as np
 from os import listdir
@@ -9,68 +8,23 @@ from os.path import isfile, join
 import importlib
 
 from pages.application.utils_data import extract_data
-
-def create_tab_application(data):
-    model = Model(data)
-    view = View(model)
-
-    @callback(
-        Output('ml_datasets_choice', 'options'),
-        Input('ml_model_choice', 'value')
-    )
-    def update_ml_type(value):
-        model.update_ml_model(value)
-        view.update_ml_model()
-        return model.datasets
-
-    @callback(
-        Output('ml_instances_choice', 'options'),
-        Output('model_layout', 'children'),
-        Input('ml_datasets_choice', 'value'),
-        Input('ml_instances_choice', 'value')
-    )
-    def update_dataset(value_dataset, value_instance):
-        if model.ml_model!='':
-            ctx = dash.callback_context
-
-            if ctx.triggered:
-                dropdown = button_id = ctx.triggered[0]['prop_id'].split('.')[0]
-                if dropdown == 'ml_datasets_choice':
-                    model.update_dataset(value_dataset)
-                    model_layout = view.update_dataset()
-                    return model.instances, model_layout
-                   
-                else : 
-                    model.update_instance(value_instance)
-                    model_layout = view.update_instance()
-                    return model.instances, model_layout
-        else :
-            return [], html.Div(children=[])
-
-    return view.create_tab()
-
+from pages.application.DecisionTree.DecisionTreeComponent import DecisionTreeComponent
 
 SIDEBAR_STYLE = {
-    "width": "20rem",
-    "margin-bottom": "5rem",
-    "background-color": "#f8f9fa",
 }
 
 CONTENT_STYLE = {
-    "padding": "2rem 1rem",
 }
 
-
 class Model():
 
     def __init__(self, data):
 
-        names_models, dict_data_models, dict_data_instances, dict_components, dict_expl = extract_data(data)
+        names_models, dict_data_models, dict_data_instances, dict_components = extract_data(data)
 
         self.dict_data_models = dict_data_models
         self.dict_data_instances = dict_data_instances
         self.dict_components = dict_components
-        self.dict_expl = dict_expl
 
         self.ml_models = names_models
         self.ml_model = ''
@@ -83,8 +37,6 @@ class Model():
 
         self.component_class = ''
 
-        self.explicability_algorithm = ''
-
     def update_ml_model(self, ml_model_update):
         self.ml_model = ml_model_update
         
@@ -92,8 +44,6 @@ class Model():
 
         self.component_class = self.dict_components[self.ml_model]
 
-        self.explicability_algorithm = self.dict_expl[self.ml_model]
-
     def update_dataset(self, dataset_update):
         self.dataset = dataset_update
 
@@ -111,41 +61,28 @@ class View():
         self.datasets_menu_models = dcc.Dropdown(self.model.datasets,  id='ml_datasets_choice')
         self.instances_menu = dcc.Dropdown(self.model.instances, id='ml_instances_choice')
 
-        self.sidebar = html.Div([
-                html.Label("Choose the Machine Learning algorithm :"),
-                html.Br(),
-                self.ml_menu_models,
-                html.Hr(),
-                html.Label("Choose the dataset : "),
-                html.Br(),
-                self.datasets_menu_models, 
-                html.Hr(),
-                html.Label("Choose the instance to explain : "),
-                html.Br(),
-                self.instances_menu],
-                style=SIDEBAR_STYLE)
-
         self.component = ''
-        self.model_layout = html.Div(
-                id="model_layout", 
-                style= CONTENT_STYLE)
-
-    def create_tab(self):
-        return dcc.Tab(label='Application on Explainable AI', children=[
-        html.Div(children=[
-            self.sidebar,
-            self.model_layout
-            ])
-    ])
-
-    def update_ml_model(self):
-        cls = getattr(importlib.import_module("pages.application.DecisionTree."+self.model.component_class), self.model.component_class)
-        self.component = cls()
+        self.sidebar = dbc.Col([
+                                    html.Label("Choose the Machine Learning algorithm :"),
+                                    html.Br(),
+                                    self.ml_menu_models,
+                                    html.Hr(),
+                                    html.Label("Choose the dataset : "),
+                                    html.Br(),
+                                    self.datasets_menu_models, 
+                                    html.Hr(),
+                                    html.Label("Choose the instance to explain : "),
+                                    html.Br(),
+                                    self.instances_menu], width=2)
+        self.layout = dbc.Row([self.sidebar, 
+                                dbc.Col(html.Div(id = "graph", children=" "), width=5), 
+                                dbc.Col(html.Div(id = "explanation", children=" "), width=3)])
+        self.tab = dcc.Tab(label='Application on Explainable AI', children=self.layout)
 
     def update_dataset(self):
-        self.component.update(self.model.dataset)
-        return html.Div(children=[self.component.network])
+        class_ =  globals()[self.model.component_class]
+        self.component = class_(self.model.dataset)
+
 
     def update_instance(self):
-        self.component.update_with_explicability(self.model.instance)
-        return html.Div(children=[self.component.network])
\ No newline at end of file
+        self.component.update_with_explicability(self.model.dataset, self.model.instance)
\ No newline at end of file
diff --git a/pages/application/utils_data.py b/pages/application/utils_data.py
index f2a814c..7b7a5fe 100644
--- a/pages/application/utils_data.py
+++ b/pages/application/utils_data.py
@@ -9,21 +9,19 @@ from os.path import isfile, join
 import importlib
 
 def extract_data(data):
-    names_models = [data['data'][i]['ml_type'] for i in range (len(data))]
+    names_models = [data[i]['ml_type'] for i in range (len(data))]
     dict_data_models = {}
     dict_data_instances = {}
     dict_components = {}
-    dict_expl = {}
     
     for i in range (len(data)) :
-        ml_type = data['data'][i]['ml_type']
-        dict_components[ml_type] = data['data'][i]['component']
-        dict_expl[ml_type] = data['data'][i]['explicability_algorithm']
-        dict_data_models[ml_type] = [f for f in listdir(data['data'][i]['trained_models'])]
-        dict_data_instances = {}
+        ml_type = data[i]['ml_type']
+        dict_components[ml_type] = data[i]['component']
+        dict_data_models[ml_type] = [f for f in listdir(data[i]['trained_models'])]
+        dict_dataset_instances = {}
         for j in range (len(dict_data_models[ml_type])) :
             dataset = dict_data_models[ml_type][j]
-            dict_data_instances[dataset] = [f for f in listdir(data['data'][i]['instances']+dataset) if isfile(join(data['data'][i]['instances']+dataset, f))]
-        dict_data_instances[ml_type] = dict_data_instances
+            dict_dataset_instances[dataset] = [f for f in listdir(data[i]['instances']+dataset) if isfile(join(data[i]['instances']+dataset, f))]
+        dict_data_instances[ml_type] = dict_dataset_instances
 
-    return names_models, dict_data_models, dict_data_instances, dict_components, dict_expl
\ No newline at end of file
+    return names_models, dict_data_models, dict_data_instances, dict_components
\ No newline at end of file
diff --git a/pages/layout.py b/pages/layout.py
deleted file mode 100644
index 41742ca..0000000
--- a/pages/layout.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from dash import dcc
-from dash import html
-import visdcc
-import pandas as pd
-import numpy as np
-
-from pages.application.layout_application import create_tab_application
-
-
-def create_tabs(data):
-    tabs = dcc.Tabs([
-        dcc.Tab(label='Course on Explainable AI', children=[]),
-        create_tab_application(data),
-    ])
-    return tabs
-
-def create_layout(data):
-    return html.Div([
-        html.H1('FXToolKit'),
-        create_tabs(data) ])
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 860116d..9e95cab 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,4 +8,6 @@ numpy>=1.16.2
 pandas>=0.24.2
 scikit-learn>=0.20.3
 scipy>=1.2.1
-visdcc
+dash_bootstrap_components
+dash_interactive_graphviz
+importlib
-- 
GitLab