def test_approx_radius(self): adjacency = test_graph() n = adjacency.shape[0] spring = Spring(approx_radius=1.) layout = spring.fit_transform(adjacency) self.assertEqual((n, 2), layout.shape)
def test_errors(self): adjacency = test_graph() with self.assertRaises(ValueError): Spring(position_init='toto') Spring().fit(adjacency, position_init=np.ones(2, 2)) with self.assertRaises(TypeError): # noinspection PyTypeChecker Spring().fit(adjacency, position_init='toto')
def test_pos_init(self): adjacency = test_graph() n = adjacency.shape[0] spring = Spring(strength=0.1, position_init='spectral', tol=1e3) layout = spring.fit_transform(adjacency) self.assertEqual((n, 2), layout.shape) layout = spring.fit_transform(adjacency, position_init=layout) self.assertEqual((n, 2), layout.shape)
def test_shape(self): for adjacency in [test_graph(), test_digraph()]: n = adjacency.shape[0] spring = Spring() layout = spring.fit_transform(adjacency) self.assertEqual((n, 2), layout.shape) spring = Spring(n_components=3) layout = spring.fit_transform(adjacency) self.assertEqual((n, 3), layout.shape)
def test_undirected(self): adjacency = test_graph() n = adjacency.shape[0] method = Spring() embedding = method.fit_transform(adjacency) self.assertEqual(embedding.shape, (n, 2)) pred1 = method.predict(adjacency[0]) pred2 = method.predict(adjacency[0].toarray()) self.assertEqual(pred1.shape, (2, )) self.assertAlmostEqual(np.linalg.norm(pred1 - pred2), 0) pred1 = method.predict(adjacency) pred2 = method.predict(adjacency.toarray()) self.assertTupleEqual(pred1.shape, (n, 2)) self.assertAlmostEqual(np.linalg.norm(pred1 - pred2), 0)
def test_undirected(self): adjacency = test_graph() n = adjacency.shape[0] for method in self.methods: with self.assertRaises(ValueError): method.predict(adjacency[0]) embedding = method.fit_transform(adjacency) self.assertEqual(embedding.shape, (n, 2)) ref = embedding[0] pred1 = method.predict(adjacency[0]) pred2 = method.predict(adjacency[0].toarray()) self.assertEqual(pred1.shape, (2, )) self.assertAlmostEqual(np.linalg.norm(pred1 - pred2), 0) self.assertAlmostEqual(np.linalg.norm(pred1 - ref), 0) pred1 = method.predict(adjacency) pred2 = method.predict(adjacency.toarray()) self.assertTupleEqual(pred1.shape, (n, 2)) self.assertAlmostEqual(np.linalg.norm(pred1 - pred2), 0) self.assertAlmostEqual(np.linalg.norm(pred1 - embedding), 0) method = Spring() embedding = method.fit_transform(adjacency) self.assertEqual(embedding.shape, (n, 2)) pred1 = method.predict(adjacency[0]) pred2 = method.predict(adjacency[0].toarray()) self.assertEqual(pred1.shape, (2, )) self.assertAlmostEqual(np.linalg.norm(pred1 - pred2), 0) pred1 = method.predict(adjacency) pred2 = method.predict(adjacency.toarray()) self.assertTupleEqual(pred1.shape, (n, 2)) self.assertAlmostEqual(np.linalg.norm(pred1 - pred2), 0)
def svg_graph(adjacency: Optional[sparse.csr_matrix] = None, position: Optional[np.ndarray] = None, names: Optional[np.ndarray] = None, labels: Optional[Iterable] = None, scores: Optional[Iterable] = None, membership: Optional[sparse.csr_matrix] = None, seeds: Union[list, dict] = None, width: Optional[float] = 400, height: Optional[float] = 300, margin: float = 20, margin_text: float = 3, scale: float = 1, node_order: Optional[np.ndarray] = None, node_size: float = 7, node_size_min: float = 1, node_size_max: float = 20, display_node_weight: bool = False, node_weights: Optional[np.ndarray] = None, node_width: float = 1, node_width_max: float = 3, node_color: str = 'gray', display_edges: bool = True, edge_labels: Optional[list] = None, edge_width: float = 1, edge_width_min: float = 0.5, edge_width_max: float = 20, display_edge_weight: bool = True, edge_color: Optional[str] = None, label_colors: Optional[Iterable] = None, font_size: int = 12, directed: bool = False, filename: Optional[str] = None) -> str: """Return SVG image of a graph. Parameters ---------- adjacency : Adjacency matrix of the graph. position : Positions of the nodes. names : Names of the nodes. labels : Labels of the nodes (negative values mean no label). scores : Scores of the nodes (measure of importance). membership : Membership of the nodes (label distribution). seeds : Nodes to be highlighted (if dict, only keys are considered). width : Width of the image. height : Height of the image. margin : Margin of the image. margin_text : Margin between node and text. scale : Multiplicative factor on the dimensions of the image. node_order : Order in which nodes are displayed. node_size : Size of nodes. node_size_min : Minimum size of a node. node_size_max: Maximum size of a node. node_width : Width of node circle. node_width_max : Maximum width of node circle. node_color : Default color of nodes (svg color). display_node_weight : If ``True``, display node weights through node size. node_weights : Node weights (used only if **display_node_weight** is ``True``). display_edges : If ``True``, display edges. edge_labels : Labels of the edges, as a list of tuples (source, destination, label) edge_width : Width of edges. edge_width_min : Minimum width of edges. edge_width_max : Maximum width of edges. display_edge_weight : If ``True``, display edge weights through edge widths. edge_color : Default color of edges (svg color). label_colors: Colors of the labels (svg colors). font_size : Font size. directed : If ``True``, considers the graph as directed. filename : Filename for saving image (optional). Returns ------- image : str SVG image. Example ------- >>> from sknetwork.data import karate_club >>> graph = karate_club(True) >>> adjacency = graph.adjacency >>> position = graph.position >>> from sknetwork.visualization import svg_graph >>> image = svg_graph(adjacency, position) >>> image[1:4] 'svg' """ # check adjacency if adjacency is None: if position is None: raise ValueError("You must specify either adjacency or position.") else: n = position.shape[0] adjacency = sparse.csr_matrix((n, n)).astype(int) else: n = adjacency.shape[0] # node order if node_order is None: node_order = np.arange(n) # position if position is None: spring = Spring() position = spring.fit_transform(adjacency) # node colors node_colors = get_node_colors(n, labels, scores, membership, node_color, label_colors) # node sizes if node_weights is None: node_weights = adjacency.T.dot(np.ones(n)) node_sizes = get_node_sizes(node_weights, node_size, node_size_min, node_size_max, display_node_weight) # node widths node_widths = get_node_widths(n, seeds, node_width, node_width_max) # rescaling position, width, height = rescale(position, width, height, margin, node_size, node_size_max, display_node_weight) if names is not None: text_length = np.max(np.array([len(str(name)) for name in names])) width += text_length * font_size * .5 # scaling position *= scale height *= scale width *= scale svg = """<svg width="{}" height="{}" xmlns="http://www.w3.org/2000/svg">\n""".format(width, height) # edges if display_edges: adjacency_coo = sparse.coo_matrix(adjacency) if edge_color is None: if names is None: edge_color = 'black' else: edge_color = 'gray' if directed: svg += """<defs><marker id="arrow" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" >\n""" svg += """<path d="M0,0 L0,6 L9,3 z" fill="{}"/></marker></defs>\n""".format(edge_color) edge_colors, edge_order, edge_colors_residual = get_edge_colors(adjacency, edge_labels, edge_color, label_colors) edge_widths = get_edge_widths(adjacency_coo, edge_width, edge_width_min, edge_width_max, display_edge_weight) for ix in edge_order: i = adjacency_coo.row[ix] j = adjacency_coo.col[ix] color = edge_colors[ix] if directed: svg += svg_edge_directed(pos_1=position[i], pos_2=position[j], edge_width=edge_widths[ix], edge_color=color, node_size=node_sizes[j]) else: svg += svg_edge(pos_1=position[i], pos_2=position[j], edge_width=edge_widths[ix], edge_color=color) for i, j, color in edge_colors_residual: if directed: svg += svg_edge_directed(pos_1=position[i], pos_2=position[j], edge_width=edge_width, edge_color=color, node_size=node_sizes[j]) else: svg += svg_edge(pos_1=position[i], pos_2=position[j], edge_width=edge_width, edge_color=color) # nodes for i in node_order: if membership is None: svg += svg_node(position[i], node_sizes[i], node_colors[i], node_widths[i]) else: if membership[i].nnz == 1: index = membership[i].indices[0] svg += svg_node(position[i], node_sizes[i], node_colors[index], node_widths[i]) else: svg += svg_pie_chart_node(position[i], node_sizes[i], membership[i].todense(), node_colors, node_widths[i]) # text if names is not None: for i in range(n): svg += svg_text(position[i] + node_sizes[i] + (margin_text, 0), names[i], font_size) svg += """</svg>\n""" if filename is not None: with open(filename + '.svg', 'w') as f: f.write(svg) return svg
def svg_graph(adjacency: sparse.csr_matrix, position: Optional[np.ndarray] = None, names: Optional[np.ndarray] = None, labels: Optional[Union[dict, np.ndarray]] = None, scores: Optional[np.ndarray] = None, seeds: Union[list, dict] = None, width: float = 400, height: float = 300, margin: float = 20, margin_text: float = 3, scale: float = 1, node_order: Optional[np.ndarray] = None, node_size: float = 7, node_size_min: float = 1, node_size_max: float = 20, display_node_weight: bool = False, node_weights: Optional[np.ndarray] = None, node_width: float = 1, node_width_max: float = 3, node_color: str = 'gray', display_edges: bool = True, edge_width: float = 1, edge_width_min: float = 0.5, edge_width_max: float = 20, edge_weight: bool = True, edge_color: Optional[str] = None, font_size: int = 12, directed: bool = False, filename: Optional[str] = None) -> str: """Return SVG image of a graph. Parameters ---------- adjacency : Adjacency matrix of the graph. position : Positions of the nodes. names : Names of the nodes. labels : Labels of the nodes (negative values mean no label). scores : Scores of the nodes (measure of importance). seeds : Nodes to be highlighted (if dict, only keys are considered). width : Width of the image. height : Height of the image. margin : Margin of the image. margin_text : Margin between node and text. scale : Multiplicative factor on the dimensions of the image. node_order : Order in which nodes are displayed. node_size : Size of nodes. node_size_min : Minimum size of a node. node_size_max: Maximum size of a node. node_width : Width of node circle. node_width_max : Maximum width of node circle. node_color : Default color of nodes (svg color). display_node_weight : Display node weights by node size. node_weights : Node weights (used only if **display_node_weight** is ``True``). display_edges : If ``True``, display edges. edge_width : Width of edges. edge_width_min : Minimum width of edges. edge_width_max : Maximum width of edges. edge_weight : Display edge weights with edge widths. edge_color : Default color of edges (svg color). font_size : Font size. directed : If ``True``, considers the graph as directed. filename : Filename for saving image (optional). Returns ------- image : str SVG image. Example ------- >>> from sknetwork.data import karate_club >>> graph = karate_club(True) >>> adjacency = graph.adjacency >>> position = graph.position >>> from sknetwork.visualization import svg_graph >>> image = svg_graph(adjacency, position) >>> image[1:4] 'svg' """ n = adjacency.shape[0] # node order if node_order is None: node_order = np.arange(n) # position if position is None: spring = Spring() position = spring.fit_transform(adjacency) # colors colors = get_colors(n, labels, scores, node_color) if edge_color is None: if names is None: edge_color = 'black' else: edge_color = 'gray' # node sizes if node_weights is None: node_weights = adjacency.dot(np.ones(n)) node_sizes = get_node_sizes(node_weights, node_size, node_size_min, node_size_max, display_node_weight) # node widths node_widths = get_node_widths(n, seeds, node_width, node_width_max) # edge widths adjacency_ = sparse.coo_matrix(adjacency) edge_widths = get_edge_widths(adjacency_.data, edge_width, edge_width_min, edge_width_max, edge_weight) # rescaling position, width, height = rescale(position, width, height, margin, node_size_max, display_node_weight) if names is not None: text_length = np.max(np.array([len(str(name)) for name in names])) width += text_length * font_size * .5 # scaling position *= scale height *= scale width *= scale svg = """<svg width="{}" height="{}" xmlns="http://www.w3.org/2000/svg">""".format(width, height) if directed: svg += """<defs><marker id="arrow" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" >""" svg += """<path d="M0,0 L0,6 L9,3 z" fill="{}"/></marker></defs>""".format(edge_color) # edges if display_edges: n_edges = len(adjacency_.row) for ix in range(n_edges): i = adjacency_.row[ix] j = adjacency_.col[ix] if directed: svg += svg_edge_directed(pos_1=position[i], pos_2=position[j], stroke_width=edge_widths[ix], stroke_color=edge_color, node_size=node_sizes[j]) else: svg += svg_edge(pos_1=position[i], pos_2=position[j], stroke_width=edge_widths[ix], stroke_color=edge_color) # nodes for i in node_order: svg += svg_node(position[i], node_sizes[i], colors[i], node_widths[i]) # text if names is not None: for i in range(n): svg += svg_text(position[i] + node_sizes[i] + (margin_text, 0), names[i], font_size) svg += """</svg>""" if filename is not None: with open(filename + '.svg', 'w') as f: f.write(svg) return svg