1+ import os
12import json
23import time
34import io
4647 More details on **[GitHub]({ GIT_REPO_URL } )**.
4748 '''
4849
50+ FOOTER = f'''
51+ If you find this app useful, please consider leaving a :star: on **[GitHub]({ GIT_REPO_URL } )**.
52+ '''
53+
54+ ICON_PATH = 'app/logo_icon.png'
55+
56+ LOGO_PATH = 'app/logo_hori.png'
57+
58+ APP_TITLE = 'TDA Mapper App'
59+
4960# V_* are reusable values for widgets
5061
5162V_LENS_IDENTITY = 'Identity'
@@ -224,51 +235,16 @@ def get_data_summary(df_X, df_y):
224235 return df_summary
225236
226237
227- def graph_download_button (mapper_graph ):
228- mapper_adj = {} if mapper_graph is None else adjacency_data (mapper_graph )
229- mapper_json = json .dumps (mapper_adj , default = int )
230- st .download_button (
231- '📥 Download Mapper Graph' ,
232- data = get_gzip_bytes (mapper_json ),
233- disabled = nx .is_empty (mapper_graph ),
234- use_container_width = True ,
235- file_name = f'mapper_graph_{ int (time .time ())} .json.gzip' )
236-
237-
238- def set_headings ():
239- page_title = 'TDA Mapper App'
240- default_page_icon = '🔮'
241- default_heading = '🔮 TDA Mapper App'
242- logo_icon_path = 'app/logo_icon.png'
243- logo_hori_path = 'app/logo_hori.png'
244- try :
245- with open (logo_icon_path , 'r' , encoding = 'utf-8' ) as _ :
246- page_icon = logo_icon_path
247- except FileNotFoundError as _ :
248- page_icon = default_page_icon
249- st .set_page_config (
250- page_icon = page_icon ,
251- page_title = page_title ,
252- menu_items = {
253- 'Report a bug' : REPORT_BUG ,
254- 'About' : ABOUT })
255- try :
256- with open (logo_hori_path , 'r' , encoding = 'utf-8' ) as _ :
257- with st .sidebar :
258- st .image (logo_hori_path , use_column_width = True )
259- st .image (logo_hori_path , use_column_width = True )
260- except FileNotFoundError as err :
261- print (err )
262- st .sidebar .header (default_heading )
263- st .header (default_heading )
264- finally :
265- st .sidebar .markdown ('#' )
266- st .sidebar .markdown (DESCRIPTION )
267- st .sidebar .markdown ('#' )
268- st .markdown ('#' )
238+ def get_graph_caption (mapper_graph ):
239+ nodes_num = 0
240+ edges_num = 0
241+ if mapper_graph is not None :
242+ nodes_num = mapper_graph .number_of_nodes ()
243+ edges_num = mapper_graph .number_of_edges ()
244+ return f'{ nodes_num } nodes, { edges_num } edges'
269245
270246
271- def graph_histogram (mapper_graph ):
247+ def get_graph_histogram (mapper_graph ):
272248 ccs = nx .connected_components (mapper_graph )
273249 size = nx .get_node_attributes (mapper_graph , ATTR_SIZE )
274250 node_cc , node_size = {}, {}
@@ -283,18 +259,15 @@ def graph_histogram(mapper_graph):
283259 node_size_max = u_size
284260 if cc_len > node_cc_max :
285261 node_cc_max = cc_len
286-
287262 arr_size = np .array ([node_size [u ]/ node_size_max for u in mapper_graph .nodes ()])
288263 arr_cc = np .array ([node_cc [u ]/ node_cc_max for u in mapper_graph .nodes ()])
289-
290264 df = pd .DataFrame (dict (
291265 series = np .concatenate ((
292266 ['node size (rel.)' ] * len (arr_size ),
293267 ['conn. comp. size (rel.)' ] * len (arr_cc ))),
294268 data = np .concatenate ((
295269 arr_size ,
296270 arr_cc ))))
297-
298271 fig = px .histogram (
299272 df ,
300273 nbins = 20 ,
@@ -323,24 +296,48 @@ def graph_histogram(mapper_graph):
323296 return fig
324297
325298
326- def get_graph_caption (mapper_graph ):
327- nodes_num = 0
328- edges_num = 0
329- if mapper_graph is not None :
330- nodes_num = mapper_graph .number_of_nodes ()
331- edges_num = mapper_graph .number_of_edges ()
332- return f'{ nodes_num } nodes, { edges_num } edges'
299+ def graph_download_button (mapper_graph ):
300+ mapper_adj = {} if mapper_graph is None else adjacency_data (mapper_graph )
301+ mapper_json = json .dumps (mapper_adj , default = int )
302+ return st .download_button (
303+ '📥 Download Mapper Graph' ,
304+ data = get_gzip_bytes (mapper_json ),
305+ disabled = nx .is_empty (mapper_graph ),
306+ use_container_width = True ,
307+ file_name = f'mapper_graph_{ int (time .time ())} .json.gzip' )
333308
334309
335- def wrap_callback (cont , msg , func ):
336- def _func (* args , ** kwargs ):
337- with cont :
338- with st .spinner (msg ):
339- func (* args , ** kwargs )
340- return _func
310+ def set_page_config ():
311+ icon = ICON_PATH if os .path .exists (ICON_PATH ) else '🔮'
312+ st .set_page_config (
313+ page_icon = icon ,
314+ page_title = APP_TITLE ,
315+ menu_items = {
316+ 'Report a bug' : REPORT_BUG ,
317+ 'About' : ABOUT })
318+
341319
320+ def set_sidebar_headings ():
321+ with st .sidebar :
322+ if os .path .exists (LOGO_PATH ):
323+ st .image (LOGO_PATH , width = 200 )
324+ else :
325+ st .header (f'🔮 { APP_TITLE } ' )
326+ st .markdown ('#' )
327+ st .markdown (DESCRIPTION )
328+ st .markdown ('#' )
329+
330+
331+ def set_main_headings ():
332+ st .subheader ('' )
333+ if os .path .exists (LOGO_PATH ):
334+ st .image (LOGO_PATH , width = 400 )
335+ else :
336+ st .header (f'🔮 { APP_TITLE } ' )
337+ st .markdown ('#' )
342338
343- def _load_data (data_source ):
339+
340+ def _update_data (data_source ):
344341 X , y = pd .DataFrame (), pd .DataFrame ()
345342 if isinstance (data_source , io .BytesIO ):
346343 X , y = pd .read_csv (data_source ), pd .DataFrame ()
@@ -359,7 +356,7 @@ def _load_data(data_source):
359356
360357
361358def data_section ():
362- st .subheader ('📊 Data' )
359+ st .subheader ('📊 Data' , anchor = False )
363360 col_0 , col_1 = st .columns ([2 , 4 ])
364361 col_2 , col_3 = st .columns ([2 , 4 ])
365362 with col_0 :
@@ -380,7 +377,7 @@ def data_section():
380377 '⏳ Loading Data...' ,
381378 '📦 Load' ,
382379 use_container_width = True ,
383- on_click = _load_data ,
380+ on_click = _update_data ,
384381 args = (data_source ,))
385382 df_X = st .session_state [S_RESULTS ].df_X
386383 df_y = st .session_state [S_RESULTS ].df_y
@@ -391,8 +388,25 @@ def data_section():
391388 st .caption (get_data_caption (df_X , df_y ))
392389
393390
391+ def _update_mapper (X , lens , cover , clustering ):
392+ mapper_algo = MapperAlgorithm (
393+ cover = cover ,
394+ clustering = FailSafeClustering (
395+ clustering = clustering ,
396+ verbose = False ))
397+ mapper_graph = mapper_algo .fit_transform (X , lens )
398+ st .session_state [S_RESULTS ].set_mapper (mapper_graph )
399+ st .toast ('Successfully Computed Mapper' , icon = '✅' )
400+ auto_rendering = st .session_state [S_RESULTS ].auto_rendering
401+ if auto_rendering is False :
402+ st .toast ('Automatic Rendering Disabled: Graph Too Large' , icon = '⚠️' )
403+
404+
394405def settings_tab (X ):
395- tab_0 , tab_1 , tab_2 = st .tabs (['🔎 Lens' , '🌐 Cover' , '🧮 Clustering' ])
406+ tab_0 , tab_1 , tab_2 = st .tabs ([
407+ '🔎 Lens' ,
408+ '🌐 Cover' ,
409+ '🧮 Clustering' ])
396410 h = 300
397411 b = False
398412 with tab_0 :
@@ -449,22 +463,8 @@ def settings_tab(X):
449463 return lens , cover , clustering
450464
451465
452- def _update_mapper (X , lens , cover , clustering ):
453- mapper_algo = MapperAlgorithm (
454- cover = cover ,
455- clustering = FailSafeClustering (
456- clustering = clustering ,
457- verbose = False ))
458- mapper_graph = mapper_algo .fit_transform (X , lens )
459- st .session_state [S_RESULTS ].set_mapper (mapper_graph )
460- st .toast ('Successfully Computed Mapper' , icon = '✅' )
461- auto_rendering = st .session_state [S_RESULTS ].auto_rendering
462- if auto_rendering is False :
463- st .toast ('Automatic Rendering Disabled: Graph Too Large' , icon = '⚠️' )
464-
465-
466466def settings_section ():
467- st .subheader ('⚙️ Mapper Settings' )
467+ st .subheader ('⚙️ Mapper Settings' , anchor = False )
468468 X = st .session_state [S_RESULTS ].X
469469 col_0 , col_1 = st .columns ([2 , 4 ])
470470 col_2 , col_3 = st .columns ([2 , 4 ])
@@ -484,7 +484,7 @@ def settings_section():
484484
485485 with col_1 :
486486 with st .container (border = True ):
487- fig_hist = graph_histogram (mapper_graph )
487+ fig_hist = get_graph_histogram (mapper_graph )
488488 st .plotly_chart (
489489 fig_hist ,
490490 use_container_width = True ,
@@ -508,7 +508,7 @@ def _update_fig(seed, colors):
508508
509509
510510def rendering_section ():
511- st .subheader ('🔮 Mapper Graph' )
511+ st .subheader ('🔮 Mapper Graph' , anchor = False )
512512 df_summary = st .session_state [S_RESULTS ].df_summary
513513 df_X = st .session_state [S_RESULTS ].df_X
514514 df_y = st .session_state [S_RESULTS ].df_y
@@ -570,23 +570,19 @@ def rendering_section():
570570 use_container_width = True )
571571
572572
573-
574-
575-
576-
577573def main ():
578- set_headings ()
574+ set_page_config ()
575+ set_main_headings ()
576+ set_sidebar_headings ()
579577 if S_RESULTS not in st .session_state :
580578 st .session_state [S_RESULTS ] = Results ()
581579 data_section ()
582580 st .markdown ('#' )
583581 settings_section ()
584582 st .markdown ('#' )
585583 rendering_section ()
586- st .markdown (f'''
587- ---
588- If you find this app useful, please consider leaving a :star: on **[GitHub]({ GIT_REPO_URL } )**.
589- ''' )
584+ st .divider ()
585+ st .markdown (FOOTER )
590586
591587
592588main ()
0 commit comments