def generate_mst_almst_comparison(log_returns_df, distance_matrix_type='angular', jupyter=False): """ This method returns a Dash server ready to be run. :param log_returns_df: (pd.Dataframe) An input dataframe of log returns with stock names as columns. :param distance_matrix_type: (str) A valid sub type of a distance matrix, namely 'angular', 'abs_angular', 'squared_angular'. :param jupyter: (bool) True if the user would like to run inside jupyter notebook. False otherwise. :return: (Dash) Returns the Dash app object, which can be run using run_server. Returns a Jupyter Dash object if the parameter jupyter is set to True. """ distance_matrix = create_input_matrix(log_returns_df, distance_matrix_type) # Create ALMST and MST objects almst = ALMST(distance_matrix, 'distance') mst = MST(distance_matrix, 'distance') # If Jupyter is true, create a Jupyter compatible DashGraph object if jupyter: dash_graph = DualDashGraph(almst, mst, 'jupyter notebook') else: dash_graph = DualDashGraph(almst, mst) # Retrieve the server server = dash_graph.get_server() return server
def setUp(self): """ Import sample data and create DashGraph object for testing """ # Set project path to current directory. project_path = os.path.dirname(__file__) # Add new data path to match stock_prices.csv data. data_path = project_path + '/test_data/stock_prices_2.csv' log_return_dataframe = pd.read_csv(data_path, index_col=False) # Calculate correlation and distances correlation_matrix = log_return_dataframe.corr(method='pearson') # Correlation matrix copy for alternative settings correlation_matrix2 = correlation_matrix.copy() # Creates Graph class objects from matrices self.graph = MST(correlation_matrix, "correlation") # Creates Graph class copy to test for alternative settings self.graph2 = MST(correlation_matrix2, "correlation") # Graph 3 for JupyterDash testing self.graph3 = MST(correlation_matrix, "correlation") # Adding industry groups colour assignment industry = { "tech": ['Apple', 'Amazon', 'Facebook'], "utilities": ['Microsoft', 'Netflix'], "automobiles": ['Tesla'] } self.graph.set_node_groups(industry) # Adding market cap market_caps = [2000, 2500, 3000, 1000, 5000, 3500] self.graph.set_node_size(market_caps) # Create DashGraph object self.dash_graph = DashGraph(self.graph) # Create DashGraph object for alternative settings self.dash_graph2 = DashGraph(self.graph2) # Test for additional colours on graph 3 for i in range(17): industry['category{}'.format(i)] = [i] self.graph3.set_node_groups(industry) # DashGraph for Jupyter Notebook self.dash_graph3 = DashGraph(self.graph3, "jupyter notebook")
def generate_mst_server(log_returns_df, mst_algorithm='kruskal', distance_matrix_type='angular', jupyter=False, colours=None, sizes=None): """ This method returns a Dash server ready to be run. :param log_returns_df: (pd.Dataframe) An input dataframe of log returns with stock names as columns. :param mst_algorithm: (str) A valid MST type such as 'kruskal', 'prim', or 'boruvka'. :param distance_matrix_type: (str) A valid sub type of a distance matrix, namely 'angular', 'abs_angular', 'squared_angular'. :param jupyter: (bool) True if the user would like to run inside jupyter notebook. False otherwise. :param colours: (Dict) A dictionary of key string for category name and value of a list of indexes corresponding to the node indexes inputted in the initial dataframe. :param sizes: (List) A list of numbers, where the positions correspond to the node indexes inputted in the initial dataframe. :return: (Dash) Returns the Dash app object, which can be run using run_server. Returns a Jupyter Dash object if the parameter jupyter is set to True. """ distance_matrix = create_input_matrix(log_returns_df, distance_matrix_type) # Create MST object graph = MST(distance_matrix, 'distance', mst_algorithm) # If colours are inputted, call set the node colours if colours: graph.set_node_groups(colours) # If sizes are inputted, set the node sizes if sizes: graph.set_node_size(sizes) # If Jupyter is true, create a Jupyter compatible DashGraph object if jupyter: dash_graph = DashGraph(graph, 'jupyter notebook') else: dash_graph = DashGraph(graph) # Retrieve the server server = dash_graph.get_server() return server
# risk_estimators = ml.portfolio_optimization.RiskEstimators() # tn_relation = transitionDataMatrixWeeks_directFollow_standardised[w].shape[0] / transitionDataMatrixWeeks_directFollow_standardised[w].shape[1] # The bandwidth of the KDE kernel # kde_bwidth = 0.01 # Finding the Вe-noised Сovariance matrix # denoised_matrix_byLib = risk_estimators.denoise_covariance(matrix, tn_relation, kde_bwidth) # denoised_matrix_byLib = pd.DataFrame(denoised_matrix_byLib, index=matrix.index, columns=matrix.columns) denoise_method='target_shrink', detoned_matrix_byLib = matrix #risk_estimators.denoise_covariance(matrix, tn_relation, kde_bwidth=kde_bwidth, detone=True) # detoned_matrix_byLib = matrix #no denoised and detoned detoned_matrix_byLib = pd.DataFrame(detoned_matrix_byLib, index=matrix.index, columns=matrix.columns) distance_matrix = (0.5 * (1 - detoned_matrix_byLib)).apply(np.sqrt) graphBuild = MST(distance_matrix, 'distance') # graphBuild = nx.from_pandas_adjacency(distance_matrix) graph_all_weeks.append(graphBuild) graph_all_weeks_cleaned = [] for w in range(1, 12): print('Week ' + str(w) + '...') matrix = corrList_cleaned[w] # risk_estimators = ml.portfolio_optimization.RiskEstimators() # tn_relation = transitionDataMatrixWeeks_directFollow_standardised[w].T.shape[0] / transitionDataMatrixWeeks_directFollow_standardised[w].T.shape[1] # The bandwidth of the KDE kernel # kde_bwidth = 0.01 # Finding the Вe-noised Сovariance matrix # denoised_matrix_byLib = risk_estimators.denoise_covariance(matrix, tn_relation, kde_bwidth) # denoised_matrix_byLib = pd.DataFrame(denoised_matrix_byLib, index=matrix.index, columns=matrix.columns)
class TestDashGraph(unittest.TestCase): # pylint: disable=protected-access, too-many-public-methods """ Tests for the different DashGraph object functionalities """ def setUp(self): """ Import sample data and create DashGraph object for testing """ # Set project path to current directory. project_path = os.path.dirname(__file__) # Add new data path to match stock_prices.csv data. data_path = project_path + '/test_data/stock_prices_2.csv' log_return_dataframe = pd.read_csv(data_path, index_col=False) # Calculate correlation and distances correlation_matrix = log_return_dataframe.corr(method='pearson') # Correlation matrix copy for alternative settings correlation_matrix2 = correlation_matrix.copy() # Creates Graph class objects from matrices self.graph = MST(correlation_matrix, "correlation") # Creates Graph class copy to test for alternative settings self.graph2 = MST(correlation_matrix2, "correlation") # Graph 3 for JupyterDash testing self.graph3 = MST(correlation_matrix, "correlation") # Adding industry groups colour assignment industry = { "tech": ['Apple', 'Amazon', 'Facebook'], "utilities": ['Microsoft', 'Netflix'], "automobiles": ['Tesla'] } self.graph.set_node_groups(industry) # Adding market cap market_caps = [2000, 2500, 3000, 1000, 5000, 3500] self.graph.set_node_size(market_caps) # Create DashGraph object self.dash_graph = DashGraph(self.graph) # Create DashGraph object for alternative settings self.dash_graph2 = DashGraph(self.graph2) # Test for additional colours on graph 3 for i in range(17): industry['category{}'.format(i)] = [i] self.graph3.set_node_groups(industry) # DashGraph for Jupyter Notebook self.dash_graph3 = DashGraph(self.graph3, "jupyter notebook") def test_additional_colours(self): """ If more than 19 groups are added, test random colours are assigned """ colours_groups = self.dash_graph3.colour_groups self.assertEqual(len(colours_groups), 20) def test_jupyter_settings(self): """ Tests JupyterDash app is created instead of Dash app """ expected_elements = [ 'dropdown-layout', 'dropdown-stat', 'json-output', 'rounding_decimals', 'card-content', 'cytoscape', 'positioned-toast' ] app3 = self.dash_graph3.get_server() self.assertIsInstance(app3, JupyterDash) self.assertEqual(app3.serve_layout().status_code, 200) for i, item in enumerate(app3.layout): self.assertEqual(expected_elements[i], item) app2 = self.dash_graph2.get_server() self.assertIsInstance(app2, dash.Dash) self.assertEqual(app2.serve_layout().status_code, 200) for i, item in enumerate(app2.layout): self.assertEqual(expected_elements[i], item) def test_colours_unassigned(self): """ Tests colour groups are unassigned when no colours are set """ colour_groups_attribute = self.dash_graph2.colour_groups self.assertEqual(colour_groups_attribute, {}) def test_size_unassigned(self): """ Tests sizes are unassigned when no sizes are set """ stylesheet_length = len(self.dash_graph2.stylesheet) default_stylesheet_length = len( self.dash_graph2._get_default_stylesheet()) self.assertEqual(stylesheet_length, default_stylesheet_length) def test_assign_colours_to_groups(self): """ Tests correct assignment of colours to industry groups """ colour_groups_map = self.dash_graph.colour_groups expected_colour_groups = { "tech": "#d0b7d5", "utilities": "#a0b3dc", "automobiles": "#90e190" } self.assertEqual(colour_groups_map, expected_colour_groups) def test_style_colours(self): """ Checks style sheet has been appended with colour styles """ current_stylesheet = len(self.dash_graph.stylesheet) self.dash_graph._style_colours() appended_stylesheet = len(self.dash_graph.stylesheet) self.assertEqual(appended_stylesheet, current_stylesheet + 3) def test_get_node_group(self): """ Checks correct industry group returned when index inputted """ node1 = self.dash_graph._get_node_group('Apple') self.assertEqual(node1, "tech") node2 = self.dash_graph._get_node_group('Amazon') self.assertEqual(node2, "tech") node3 = self.dash_graph._get_node_group('Facebook') self.assertEqual(node3, "tech") node4 = self.dash_graph._get_node_group('Microsoft') self.assertEqual(node4, "utilities") node6 = self.dash_graph._get_node_group('Tesla') self.assertEqual(node6, "automobiles") # Test for when object not assigned to group node_nan = self.dash_graph._get_node_group('invalid name') self.assertEqual(node_nan, "default") def test_get_node_size(self): """ Checks correct market cap node size returned when index inputted """ node1_size = self.dash_graph._get_node_size(0) self.assertEqual(node1_size, 2000) node2_size = self.dash_graph._get_node_size(1) self.assertEqual(node2_size, 2500) node3_size = self.dash_graph._get_node_size(2) self.assertEqual(node3_size, 3000) node8_size = self.dash_graph._get_node_size(5) self.assertEqual(node8_size, 3500) # No sizes have been assigned node_nan = self.dash_graph2._get_node_size(3) self.assertEqual(node_nan, 0) def test_assign_sizes(self): """ Checks style sheet has been appended with node sizes """ current_stylesheet = len(self.dash_graph.stylesheet) self.dash_graph._assign_sizes() appended_stylsheet = len(self.dash_graph.stylesheet) self.assertEqual(appended_stylsheet, current_stylesheet + 1) def test_get_server(self): """ Tests get_server returns a Dash app object """ app = self.dash_graph.get_server() self.assertIsInstance(app, dash.Dash) def test_generate_layout(self): """ Tests generate_layout returns Dash Bootstrap Container """ layout = self.dash_graph._generate_layout() self.assertIsInstance(layout, dbc.Container) def test_set_cyto_graph(self): """ Tests generate_layout returns Dash Bootstrap Container """ self.dash_graph._set_cyto_graph() self.assertIsInstance(self.dash_graph.cyto_graph, cyto.Cytoscape) def test_update_cytoscape_layout(self): """ Tests correct layout is returned """ layout = self.dash_graph._update_cytoscape_layout("cola") self.assertEqual(layout['name'], "cola") def test_update_stat_json(self): """ Checks if Statistics panel returned expected outputs """ # Test for graph summary summary = self.dash_graph._update_stat_json("graph_summary") self.check_summary_values(json.loads(summary)) # Test for average_degree_connectivity average_degree_connectivity = self.dash_graph._update_stat_json( "average_degree_connectivity") expected_average_degree = {'1': 5.0, '5': 1.0} self.assertEqual(json.loads(average_degree_connectivity), expected_average_degree) # Test for average_neighbor_degree average_neighbor_degree = self.dash_graph._update_stat_json( "average_neighbor_degree") expected_average_neighbor = { 'Apple': 5.0, 'Amazon': 5.0, 'Facebook': 5.0, 'Microsoft': 5.0, 'Netflix': 5.0, 'Tesla': 1.0 } self.assertEqual(json.loads(average_neighbor_degree), expected_average_neighbor) # Test for betweenness_centrality betweenness_centrality = self.dash_graph._update_stat_json( "betweenness_centrality") expected_betweenness = { 'Apple': 0.0, 'Amazon': 0.0, 'Facebook': 0.0, 'Microsoft': 0.0, 'Netflix': 0.0, 'Tesla': 1.0 } self.assertEqual(json.loads(betweenness_centrality), expected_betweenness) def test_get_graph_summary(self): """ Tests if graph summary values are returned correctly """ summary = self.dash_graph.get_graph_summary() self.check_summary_values(summary) def check_summary_values(self, summary): """ Checks graph summary dictionary """ self.assertEqual(summary['nodes'], 6) self.assertEqual(summary['edges'], 5) self.assertEqual(summary['smallest_edge'], 0.4706) self.assertEqual(summary['largest_edge'], 0.6965) self.assertEqual(summary['normalised_tree_length'], 0.5525800000000001) self.assertEqual(summary['average_node_connectivity'], 1.0) self.assertEqual(summary['average_shortest_path'], 1.6666666666666667) def test_round_decimals(self): """ Tests decimals are rounded to desired decimal place when round_decimals method is called """ elements = self.dash_graph._round_decimals(None) self.assertIsInstance(elements, (list, )) self.dash_graph._round_decimals(1) for weight in self.dash_graph.weights: decimal_places = len(str(weight).split('.')[1]) self.assertEqual(decimal_places, 1) def test_get_toast(self): """ Tests Div containing Toast (Bootstrap component) is returned """ toast_div = self.dash_graph._get_toast() self.assertIsInstance(toast_div, html.Div) toast = toast_div.children[0] self.assertIsInstance(toast, dbc.Toast) def test_get_default_controls(self): """ Tests Dash Bootstrap component is returned """ controls = self.dash_graph._get_default_controls() self.assertIsInstance(controls, dbc.Card) def test_get_default_stylesheet(self): """ Tests correct stylesheet dictionary is returned """ stylesheet = self.dash_graph._get_default_stylesheet() self.assertEqual(len(stylesheet), 4)
def setUp(self): """ Import sample data and create DualDashGraph object for testing. """ # Set project path to current directory. project_path = os.path.dirname(__file__) # Add new data path to match stock_prices.csv data. data_path = project_path + '/test_data/stock_prices.csv' log_return_dataframe = pd.read_csv(data_path, index_col=0) log_return_dataframe = log_return_dataframe.pct_change() other_data_path = project_path + '/test_data/stock_prices_2.csv' other_log_return = pd.read_csv(other_data_path, index_col=0) other_log_return = other_log_return.pct_change() # Calculate correlation and distances. correlation_matrix = log_return_dataframe.corr(method='pearson') distance_matrix = get_distance_matrix(correlation_matrix) other_correlation_matrix = other_log_return.corr(method='pearson') other_distance_matrix = get_distance_matrix(other_correlation_matrix) distance_matrix2 = distance_matrix.copy() # Creates Graph class objects from matrices. self.mst_graph = MST(distance_matrix, "distance") # Creates Graph class copy to test for alternative settings. mst_graph2 = MST(distance_matrix2, "distance") # Graph 3 for JupyterDash testing. self.mst_graph3 = MST(distance_matrix, "distance") # Creates Graph class objects from matrices. self.almst_graph = ALMST(distance_matrix, "distance") # Creates Graph class copy to test for alternative settings almst_graph2 = ALMST(distance_matrix2, "distance") # Graph 3 for JupyterDash testing almst_graph3 = ALMST(distance_matrix, "distance") # Graph 4 with adifferent dataset self.almst_graph4 = ALMST(other_distance_matrix, "distance") # Adding industry groups colour assignment self.industry = {"stocks": ['EEM', 'EWG', 'EWJ', 'EFA', 'EWQ', 'EWU', 'XLB', 'XLE', 'XLF', 'XLK', 'XLU', 'EPP', 'FXI', 'VGK', 'VPL', 'SPY', 'CSJ'], "bonds": ['TIP', 'IEF', 'LQD', 'TLT', 'BND', 'CSJ', 'DIA']} self.mst_graph.set_node_groups(self.industry) self.almst_graph.set_node_groups(self.industry) # Adding market cap self.market_cap = [2000, 2500, 3000, 1000, 5000, 3500, 2000, 2500, 3000, 1000, 5000, 3500, 2000, 2500, 3000, 1000, 5000, 3500, 2000, 2500, 3000, 1000, 5000] self.mst_graph.set_node_size(self.market_cap) self.almst_graph.set_node_size(self.market_cap) # Create DashGraph object self.dash_graph = DualDashGraph(self.mst_graph, self.almst_graph) # Create DashGraph object for alternative settings self.dash_graph2 = DualDashGraph(mst_graph2, almst_graph2) # Test for additional colours on graph 3 for i in range(17): self.industry['category{}'.format(i)] = [i] self.mst_graph3.set_node_groups(self.industry) almst_graph3.set_node_groups(self.industry) # DashGraph for Jupyter Notebook self.dash_graph3 = DualDashGraph(self.mst_graph3, almst_graph3, "jupyter notebook")
class TestDualDashGraph(unittest.TestCase): # pylint: disable=protected-access, too-many-public-methods """ Tests for the different DualDashGraph object functionalities and the ALMST functionality. """ def setUp(self): """ Import sample data and create DualDashGraph object for testing. """ # Set project path to current directory. project_path = os.path.dirname(__file__) # Add new data path to match stock_prices.csv data. data_path = project_path + '/test_data/stock_prices.csv' log_return_dataframe = pd.read_csv(data_path, index_col=0) log_return_dataframe = log_return_dataframe.pct_change() other_data_path = project_path + '/test_data/stock_prices_2.csv' other_log_return = pd.read_csv(other_data_path, index_col=0) other_log_return = other_log_return.pct_change() # Calculate correlation and distances. correlation_matrix = log_return_dataframe.corr(method='pearson') distance_matrix = get_distance_matrix(correlation_matrix) other_correlation_matrix = other_log_return.corr(method='pearson') other_distance_matrix = get_distance_matrix(other_correlation_matrix) distance_matrix2 = distance_matrix.copy() # Creates Graph class objects from matrices. self.mst_graph = MST(distance_matrix, "distance") # Creates Graph class copy to test for alternative settings. mst_graph2 = MST(distance_matrix2, "distance") # Graph 3 for JupyterDash testing. self.mst_graph3 = MST(distance_matrix, "distance") # Creates Graph class objects from matrices. self.almst_graph = ALMST(distance_matrix, "distance") # Creates Graph class copy to test for alternative settings almst_graph2 = ALMST(distance_matrix2, "distance") # Graph 3 for JupyterDash testing almst_graph3 = ALMST(distance_matrix, "distance") # Graph 4 with adifferent dataset self.almst_graph4 = ALMST(other_distance_matrix, "distance") # Adding industry groups colour assignment self.industry = {"stocks": ['EEM', 'EWG', 'EWJ', 'EFA', 'EWQ', 'EWU', 'XLB', 'XLE', 'XLF', 'XLK', 'XLU', 'EPP', 'FXI', 'VGK', 'VPL', 'SPY', 'CSJ'], "bonds": ['TIP', 'IEF', 'LQD', 'TLT', 'BND', 'CSJ', 'DIA']} self.mst_graph.set_node_groups(self.industry) self.almst_graph.set_node_groups(self.industry) # Adding market cap self.market_cap = [2000, 2500, 3000, 1000, 5000, 3500, 2000, 2500, 3000, 1000, 5000, 3500, 2000, 2500, 3000, 1000, 5000, 3500, 2000, 2500, 3000, 1000, 5000] self.mst_graph.set_node_size(self.market_cap) self.almst_graph.set_node_size(self.market_cap) # Create DashGraph object self.dash_graph = DualDashGraph(self.mst_graph, self.almst_graph) # Create DashGraph object for alternative settings self.dash_graph2 = DualDashGraph(mst_graph2, almst_graph2) # Test for additional colours on graph 3 for i in range(17): self.industry['category{}'.format(i)] = [i] self.mst_graph3.set_node_groups(self.industry) almst_graph3.set_node_groups(self.industry) # DashGraph for Jupyter Notebook self.dash_graph3 = DualDashGraph(self.mst_graph3, almst_graph3, "jupyter notebook") def test_select_other_graph_node(self): """ Tests _select_other_graph_node """ # Creating elements to test function elements_in = [{"data": {"id": "element1"}}, {"data": {"id": "element2"}}] data = {"data": {"id": "element2"}} # Passing valid parameters elements_out = self.dash_graph._select_other_graph_node(data, elements_in) # Passing empty data variable elements_same = self.dash_graph._select_other_graph_node(None, elements_in) # Testing valid output self.assertTrue(not elements_out[0]['selected']) self.assertTrue(elements_out[1]['selected']) # Testing same output self.assertTrue(elements_same == elements_in) def test_generate_comparison_layout(self): """ Tests _generate_comparison_layout returns html.Div """ comparison_layout = self.dash_graph._generate_comparison_layout(self.mst_graph, self.almst_graph) self.assertIsInstance(comparison_layout, html.Div) def test_get_default_stylesheet(self): """ Tests correct stylesheet dictionary is returned """ stylesheet = self.dash_graph._get_default_stylesheet(self.dash_graph.one_components[0]) self.assertEqual(len(stylesheet), 7) def test_set_cyto_graph(self): """ Tests generate_layout returns Dash Bootstrap Container """ self.dash_graph._set_cyto_graph() self.assertIsInstance(self.dash_graph.cyto_one, cyto.Cytoscape) self.assertIsInstance(self.dash_graph.cyto_two, cyto.Cytoscape) def test_get_server(self): """ Tests get_server returns a Dash app object """ app = self.dash_graph.get_server() self.assertIsInstance(app, dash.Dash) def test_jupyter_settings(self): """ Tests JupyterDash app is created instead of Dash app """ expected_elements = ['cytoscape', 'cytoscape_two'] app3 = self.dash_graph3.get_server() self.assertIsInstance(app3, JupyterDash) self.assertEqual(app3.serve_layout().status_code, 200) for i, item in enumerate(app3.layout): self.assertEqual(expected_elements[i], item) app2 = self.dash_graph2.get_server() self.assertIsInstance(app2, dash.Dash) self.assertEqual(app2.serve_layout().status_code, 200) for i, item in enumerate(app2.layout): self.assertEqual(expected_elements[i], item) def test_calculate_average_distance(self): """ Test for the calculate_average_distance method. """ # Create a 3 by 3 matrix for testing. dist_array = [[0, 0.2, 0.3], [0.2, 0, 0.4], [0.3, 0.4, 0]] distance_df = pd.DataFrame(dist_array) # Create clusters clusters = [[0, 1], [2]] # Set cluster index corresponding to the clusters. c_x = 0 c_y = 1 average, node_1, node_2 = ALMST._calculate_average_distance(distance_df, clusters, c_x, c_y) self.assertEqual(node_2, 2) self.assertEqual(node_1, 0) average_distance = (0.3 + 0.4)/2 self.assertEqual(average, average_distance) def test_differnet_graphs_error(self): """ Tests that when given two graphs with different set of nodes it results in an error. """ self.assertRaises(ValueError, DualDashGraph, graph_one=self.mst_graph3, graph_two=self.almst_graph4)
# sns.heatmap(detoned_matrix_byLib, annot=False, center=0, yticklabels=False, xticklabels=False) # plt.title('Correlation heatmap week ' + str(w)) # plt.show() from mlfinlab.networks.mst import MST custom_matrix = ml.codependence.get_distance_matrix(detoned_matrix_byLib, distance_metric='angular') reLabelIndex = custom_matrix.reset_index().loc[:, ['user']] custom_matrix = custom_matrix.reset_index(drop=True) custom_matrix.columns = np.arange(len(custom_matrix.columns)) # sns.heatmap(custom_matrix, annot=False, center=0, yticklabels=False, xticklabels=False) # plt.title('Distance Correlation heatmap week ' + str(w)) # plt.show() graph = MST(custom_matrix, 'custom') studentCohorts = { "excellent": reLabelIndex.loc[reLabelIndex['user'].isin(ex3_excellent.index)].index, "weak": reLabelIndex.loc[reLabelIndex['user'].isin(ex3_weak.index)].index } graph.set_node_groups(studentCohorts) graph.get_graph_plot() from mlfinlab.networks.dash_graph import DashGraph dash_graph = DashGraph(graph) server = dash_graph.get_server() # Run server server.run_server()