none commited on
Commit
789e5bc
1 Parent(s): ffd3002

Finshing writing, change color palette

Browse files
Files changed (1) hide show
  1. streamlit_viz.py +116 -65
streamlit_viz.py CHANGED
@@ -59,90 +59,54 @@ FEATS = [
59
  # Generated from
60
  # mokole.com/palette.html
61
  COLORS = [
 
62
  '#808080',
63
  '#2f4f4f',
64
  '#556b2f',
65
  '#8b4513',
66
- '#6b8e23',
67
- '#2e8b57',
68
  '#800000',
69
- '#191970',
70
- '#006400',
 
71
  '#b8860b',
 
72
  '#4682b4',
73
  '#d2691e',
74
  '#9acd32',
75
- '#20b2aa',
76
  '#cd5c5c',
77
  '#00008b',
78
  '#32cd32',
79
  '#8fbc8f',
80
- '#800080',
81
  '#b03060',
82
  '#d2b48c',
83
- '#ff4500',
84
  '#ffa500',
 
85
  '#ffff00',
86
- '#c71585',
87
  '#0000cd',
88
  '#00ff00',
 
89
  '#00ff7f',
 
90
  '#dc143c',
91
  '#00ffff',
92
  '#00bfff',
93
  '#f4a460',
94
- '#9370db',
95
- '#a020f0',
96
  '#adff2f',
97
  '#ff6347',
98
  '#da70d6',
99
- '#b0c4de',
100
  '#ff00ff',
101
  '#f0e68c',
102
  '#6495ed',
103
  '#dda0dd',
104
- '#afeeee',
105
  '#98fb98',
106
  '#7fffd4',
107
- '#ffb6c1',
108
- ]
109
 
110
- #COLORS = [
111
- # 'aliceblue','aqua','aquamarine','azure',
112
- # 'bisque','black','blanchedalmond','blue',
113
- # 'blueviolet','brown','burlywood','cadetblue',
114
- # 'chartreuse','chocolate','coral','cornflowerblue',
115
- # 'cornsilk','crimson','cyan','darkblue','darkcyan',
116
- # 'darkgoldenrod','darkgray','darkgreen',
117
- # 'darkkhaki','darkmagenta','darkolivegreen','darkorange',
118
- # 'darkorchid','darkred','darksalmon','darkseagreen',
119
- # 'darkslateblue','darkslategray',
120
- # 'darkturquoise','darkviolet','deeppink','deepskyblue',
121
- # 'dimgray','dodgerblue',
122
- # 'forestgreen','fuchsia','gainsboro',
123
- # 'gold','goldenrod','gray','green',
124
- # 'greenyellow','honeydew','hotpink','indianred','indigo',
125
- # 'ivory','khaki','lavender','lavenderblush','lawngreen',
126
- # 'lemonchiffon','lightblue','lightcoral','lightcyan',
127
- # 'lightgoldenrodyellow','lightgray',
128
- # 'lightgreen','lightpink','lightsalmon','lightseagreen',
129
- # 'lightskyblue','lightslategray',
130
- # 'lightsteelblue','lightyellow','lime','limegreen',
131
- # 'linen','magenta','maroon','mediumaquamarine',
132
- # 'mediumblue','mediumorchid','mediumpurple',
133
- # 'mediumseagreen','mediumslateblue','mediumspringgreen',
134
- # 'mediumturquoise','mediumvioletred','midnightblue',
135
- # 'mintcream','mistyrose','moccasin','navy',
136
- # 'oldlace','olive','olivedrab','orange','orangered',
137
- # 'orchid','palegoldenrod','palegreen','paleturquoise',
138
- # 'palevioletred','papayawhip','peachpuff','peru','pink',
139
- # 'plum','powderblue','purple','red','rosybrown',
140
- # 'royalblue','saddlebrown','salmon','sandybrown',
141
- # 'seagreen','seashell','sienna','silver','skyblue',
142
- # 'slateblue','slategray','slategrey','snow','springgreen',
143
- # 'steelblue','tan','teal','thistle','tomato','turquoise',
144
- # 'violet','wheat','yellow','yellowgreen'
145
- #]
146
 
147
  def build_parents(tree, visit_order, node_id2plot_id):
148
  parents = [None]
@@ -236,32 +200,87 @@ def main():
236
  # make the plots
237
  graph_objs = [build_plot(tree) for tree in trees]
238
  figures = [go.Figure(graph_obj) for graph_obj in graph_objs]
239
- frames = [go.Frame(data=graph_obj) for graph_obj in graph_objs]
 
 
240
  # show them with streamlit
241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  st.markdown("""
243
- I trained a
244
- [Histogram-based Gradient Boosting Classification Tree](https://scikit-learn.org/stable/modules/ensemble.html#histogram-based-gradient-boosting)
245
- on some data.
 
246
  That algoritm looks at its mistakes and tries to avoid those mistakes the next time around.
247
 
248
  To do that, it starts off with a decision tree.
249
  From there, it looks at the points that tree got wrong and makes another decision tree that tries
250
  to get those points right.
251
- Then it looks at that second tree's mistakes and makes another tree that tries to fix those mistakes.
252
  And so on.
253
 
254
  My model ends up with 10 trees.
255
- I've plotted the progression of those trees as an animated series of tree maps.
256
- The boxes are color-coded by which feature the decision tree is using to make that split and I've labeled each one with the exact decision boundary of that split.
257
- It takes a second to get going after you hit "Play."
 
 
 
 
 
 
 
 
 
 
258
 
259
  I recommend expanding the plot by clicking the arrows in the top right corner since Streamlit makes the plot really small.
 
260
 
261
  """)
262
 
263
 
264
- st.markdown('## My Trees')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
 
266
  # Maybe just show a Plotly animated chart
267
  # https://plotly.com/python/animations/#using-a-slider-and-buttons
@@ -297,26 +316,54 @@ I recommend expanding the plot by clicking the arrows in the top right corner si
297
  # border color of the buttons
298
  'bordercolor': '#000',
299
 
300
- # Play button
 
 
301
  'buttons':[{
302
  'label':'Play',
303
  'method': 'animate',
304
  'args':[None, {
 
305
  'frame': {'duration':5000},
306
  'transition': {'duration': 2500},
307
- }],
308
- }
 
 
 
 
 
 
 
 
 
309
  ]
310
- }]
 
 
311
  )
312
  )
313
  st.plotly_chart(ani_fig)
314
 
315
  st.markdown("""
316
  This actually turned out to be a lot harder than I thought it would be.
 
 
 
 
 
 
 
 
 
 
 
317
  """)
318
 
319
- st.markdown('# Check out each tree!')
 
 
 
320
 
321
  # This works the way I want
322
  # but the plot is tiny
@@ -335,9 +382,13 @@ This actually turned out to be a lot harder than I thought it would be.
335
  value=0,
336
  step=1
337
  )
 
338
  st.plotly_chart(figures[idx])
339
- st.markdown(f'## Tree {idx}')
340
  st.dataframe(trees[idx])
 
 
 
 
341
 
342
  if __name__=='__main__':
343
  main()
 
59
  # Generated from
60
  # mokole.com/palette.html
61
  COLORS = [
62
+ '#000000',
63
  '#808080',
64
  '#2f4f4f',
65
  '#556b2f',
66
  '#8b4513',
67
+ '#228b22',
 
68
  '#800000',
69
+ '#808000',
70
+ '#3cb371',
71
+ '#663399',
72
  '#b8860b',
73
+ '#008b8b',
74
  '#4682b4',
75
  '#d2691e',
76
  '#9acd32',
 
77
  '#cd5c5c',
78
  '#00008b',
79
  '#32cd32',
80
  '#8fbc8f',
 
81
  '#b03060',
82
  '#d2b48c',
83
+ '#ff0000',
84
  '#ffa500',
85
+ '#ffd700',
86
  '#ffff00',
 
87
  '#0000cd',
88
  '#00ff00',
89
+ '#8a2be2',
90
  '#00ff7f',
91
+ '#4169e1',
92
  '#dc143c',
93
  '#00ffff',
94
  '#00bfff',
95
  '#f4a460',
 
 
96
  '#adff2f',
97
  '#ff6347',
98
  '#da70d6',
99
+ '#d8bfd8',
100
  '#ff00ff',
101
  '#f0e68c',
102
  '#6495ed',
103
  '#dda0dd',
104
+ '#b0e0e6',
105
  '#98fb98',
106
  '#7fffd4',
107
+ '#ff69b4',
 
108
 
109
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
  def build_parents(tree, visit_order, node_id2plot_id):
112
  parents = [None]
 
200
  # make the plots
201
  graph_objs = [build_plot(tree) for tree in trees]
202
  figures = [go.Figure(graph_obj) for graph_obj in graph_objs]
203
+ # each frame has to have a name
204
+ # https://community.plotly.com/t/animation-with-slider-not-moving-when-pressing-play/34763/2
205
+ frames = [go.Frame(data=graph_obj, name=str(i)) for i, graph_obj in enumerate(graph_objs)]
206
  # show them with streamlit
207
 
208
+ #st.markdown('# Thankfully, Visualizing Decision Trees is Hard')
209
+ st.markdown('# Thankfully, visualizing decision trees is hard')
210
+ st.markdown('## Setting the scene')
211
+ st.markdown("""
212
+ I make a lot of dashboards, which means I make a lot of the same plots over and over.
213
+ Desperate for some creative outlet, I wanted to make a new visualization—
214
+ something I'd never seen before.
215
+ Inspired by interactive visualizations like
216
+ [Tensorflow Playground](https://playground.tensorflow.org)
217
+ and
218
+ [GAN Lab](https://poloclub.github.io/ganlab),
219
+ I decided to wanted to watch some kind of gradient-boosted tree as it learned.
220
+ """)
221
+
222
+ st.markdown('## Some kind of gradient-boosted tree')
223
  st.markdown("""
224
+ I trained an ensemble of
225
+ [Histogram-based Gradient Boosting Decision Trees](https://scikit-learn.org/stable/modules/ensemble.html#histogram-based-gradient-boosting)
226
+ on some
227
+ [data](https://research.unsw.edu.au/projects/unsw-nb15-dataset).
228
  That algoritm looks at its mistakes and tries to avoid those mistakes the next time around.
229
 
230
  To do that, it starts off with a decision tree.
231
  From there, it looks at the points that tree got wrong and makes another decision tree that tries
232
  to get those points right.
233
+ Then it looks at that second tree's mistakes and makes a third tree that tries to fix those mistakes.
234
  And so on.
235
 
236
  My model ends up with 10 trees.
237
+ """)
238
+
239
+ st.markdown('## Behold')
240
+
241
+ st.markdown("""
242
+ I've plotted the progression of those 10 trees as an animated series of interactive Plotly tree maps.
243
+ The nodes are color-coded by which feature the decision tree used to make that split.
244
+
245
+ I've also labeled each node with the feature name and the decison boundary.
246
+ If you click on a node, Plotly will show the path to that node in a banner at the top of the plot so you can see how a point ends up in the node you clicked.
247
+
248
+ The numbers and letters in brackets like `[3.L]` refer to the parent node's position in a breadth-first traversal of the tree and whether the current node is a left or right child of that parent.
249
+ Plotly unforunately plots everything flipped for some reason, so all the `R` nodes are on the left and vice versa.
250
 
251
  I recommend expanding the plot by clicking the arrows in the top right corner since Streamlit makes the plot really small.
252
+ It takes a second to get going after you hit `Play`.
253
 
254
  """)
255
 
256
 
257
+
258
+ # Build the slider steps
259
+ slider_steps = []
260
+ for i in range(len(trees)):
261
+ slider_steps.append({
262
+ 'args': [
263
+ [i],
264
+ {
265
+ 'frame': {'duration': 300, 'redraw': True},
266
+ 'mode': 'immediate',
267
+ 'transition': {'duration': 300}
268
+ }
269
+ ],
270
+ 'label': i,
271
+ 'method': 'animate',
272
+ })
273
+
274
+ sliders_dict = {
275
+ 'active': 0,
276
+ 'currentvalue': {
277
+ 'font': {'size': 20},
278
+ 'prefix': 'Tree ',
279
+ 'visible': True
280
+ },
281
+ 'transition': {'duration': 300},
282
+ 'steps': slider_steps
283
+ }
284
 
285
  # Maybe just show a Plotly animated chart
286
  # https://plotly.com/python/animations/#using-a-slider-and-buttons
 
316
  # border color of the buttons
317
  'bordercolor': '#000',
318
 
319
+ # Play and Pause buttons
320
+ # trying to copy this exactly
321
+ # https://plotly.com/python/animations/#adding-control-buttons-to-animations
322
  'buttons':[{
323
  'label':'Play',
324
  'method': 'animate',
325
  'args':[None, {
326
+ 'fromcurrent': True,
327
  'frame': {'duration':5000},
328
  'transition': {'duration': 2500},
329
+ }],
330
+ },
331
+ {
332
+ 'label': 'Pause',
333
+ 'method': 'animate',
334
+ 'args':[[None], {
335
+ 'frame': {'duration': 0},
336
+ 'transition': {'duration': 0},
337
+ 'mode': 'immediate'
338
+ }]
339
+ }
340
  ]
341
+ }],
342
+ # add the slider to the layout
343
+ sliders=[sliders_dict]
344
  )
345
  )
346
  st.plotly_chart(ani_fig)
347
 
348
  st.markdown("""
349
  This actually turned out to be a lot harder than I thought it would be.
350
+ Plotly doesn't have many examples of how to create animations like this in Python.
351
+ [The only example I could find](https://plotly.com/python/animations/#using-a-slider-and-buttons)
352
+ was derided as an
353
+ ["old example [. . .] that is not the best one to learn how to define an animation with slider."](https://community.plotly.com/t/slider-not-updating-during-animation/37261)
354
+
355
+ That helpful poster didn't point out any other examples, so that one is still pretty much all I have to go on.
356
+
357
+ Later on,
358
+ [a different answer by the same poster](https://community.plotly.com/t/animation-with-slider-not-moving-when-pressing-play/34763)
359
+ got me out of a jam.
360
+ As far as I can tell, this poster `empet` is the only person in the world who understands Plotly's animations in Python.
361
  """)
362
 
363
+ st.markdown('## Check out the data!')
364
+ st.markdown("""
365
+ This plot is similar to the plot above, but the slider here coordinates with a table to show the data I extracted to plot each tree.
366
+ """)
367
 
368
  # This works the way I want
369
  # but the plot is tiny
 
382
  value=0,
383
  step=1
384
  )
385
+ st.markdown(f'### Tree {idx}')
386
  st.plotly_chart(figures[idx])
 
387
  st.dataframe(trees[idx])
388
+ st.markdown("""
389
+ This section is mostly just to warn you against making the same foolhardy decision to marry the innermost guts of SciKit-Learn to the sparsely documented world of Python Plotly animations.
390
+
391
+ """)
392
 
393
  if __name__=='__main__':
394
  main()