scikit-network 0.33.3__cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of scikit-network might be problematic. Click here for more details.
- scikit_network-0.33.3.dist-info/METADATA +122 -0
- scikit_network-0.33.3.dist-info/RECORD +229 -0
- scikit_network-0.33.3.dist-info/WHEEL +6 -0
- scikit_network-0.33.3.dist-info/licenses/AUTHORS.rst +43 -0
- scikit_network-0.33.3.dist-info/licenses/LICENSE +34 -0
- scikit_network-0.33.3.dist-info/top_level.txt +1 -0
- scikit_network.libs/libgomp-d22c30c5.so.1.0.0 +0 -0
- sknetwork/__init__.py +21 -0
- sknetwork/base.py +67 -0
- sknetwork/classification/__init__.py +8 -0
- sknetwork/classification/base.py +142 -0
- sknetwork/classification/base_rank.py +133 -0
- sknetwork/classification/diffusion.py +134 -0
- sknetwork/classification/knn.py +139 -0
- sknetwork/classification/metrics.py +205 -0
- sknetwork/classification/pagerank.py +66 -0
- sknetwork/classification/propagation.py +152 -0
- sknetwork/classification/tests/__init__.py +1 -0
- sknetwork/classification/tests/test_API.py +30 -0
- sknetwork/classification/tests/test_diffusion.py +77 -0
- sknetwork/classification/tests/test_knn.py +23 -0
- sknetwork/classification/tests/test_metrics.py +53 -0
- sknetwork/classification/tests/test_pagerank.py +20 -0
- sknetwork/classification/tests/test_propagation.py +24 -0
- sknetwork/classification/vote.cpp +27587 -0
- sknetwork/classification/vote.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/classification/vote.pyx +56 -0
- sknetwork/clustering/__init__.py +8 -0
- sknetwork/clustering/base.py +172 -0
- sknetwork/clustering/kcenters.py +253 -0
- sknetwork/clustering/leiden.py +242 -0
- sknetwork/clustering/leiden_core.cpp +31578 -0
- sknetwork/clustering/leiden_core.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/clustering/leiden_core.pyx +124 -0
- sknetwork/clustering/louvain.py +286 -0
- sknetwork/clustering/louvain_core.cpp +31223 -0
- sknetwork/clustering/louvain_core.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/clustering/louvain_core.pyx +124 -0
- sknetwork/clustering/metrics.py +91 -0
- sknetwork/clustering/postprocess.py +66 -0
- sknetwork/clustering/propagation_clustering.py +104 -0
- sknetwork/clustering/tests/__init__.py +1 -0
- sknetwork/clustering/tests/test_API.py +38 -0
- sknetwork/clustering/tests/test_kcenters.py +60 -0
- sknetwork/clustering/tests/test_leiden.py +34 -0
- sknetwork/clustering/tests/test_louvain.py +135 -0
- sknetwork/clustering/tests/test_metrics.py +50 -0
- sknetwork/clustering/tests/test_postprocess.py +39 -0
- sknetwork/data/__init__.py +6 -0
- sknetwork/data/base.py +33 -0
- sknetwork/data/load.py +406 -0
- sknetwork/data/models.py +459 -0
- sknetwork/data/parse.py +644 -0
- sknetwork/data/test_graphs.py +84 -0
- sknetwork/data/tests/__init__.py +1 -0
- sknetwork/data/tests/test_API.py +30 -0
- sknetwork/data/tests/test_base.py +14 -0
- sknetwork/data/tests/test_load.py +95 -0
- sknetwork/data/tests/test_models.py +52 -0
- sknetwork/data/tests/test_parse.py +250 -0
- sknetwork/data/tests/test_test_graphs.py +29 -0
- sknetwork/data/tests/test_toy_graphs.py +68 -0
- sknetwork/data/timeout.py +38 -0
- sknetwork/data/toy_graphs.py +611 -0
- sknetwork/embedding/__init__.py +8 -0
- sknetwork/embedding/base.py +94 -0
- sknetwork/embedding/force_atlas.py +198 -0
- sknetwork/embedding/louvain_embedding.py +148 -0
- sknetwork/embedding/random_projection.py +135 -0
- sknetwork/embedding/spectral.py +141 -0
- sknetwork/embedding/spring.py +198 -0
- sknetwork/embedding/svd.py +359 -0
- sknetwork/embedding/tests/__init__.py +1 -0
- sknetwork/embedding/tests/test_API.py +49 -0
- sknetwork/embedding/tests/test_force_atlas.py +35 -0
- sknetwork/embedding/tests/test_louvain_embedding.py +33 -0
- sknetwork/embedding/tests/test_random_projection.py +28 -0
- sknetwork/embedding/tests/test_spectral.py +81 -0
- sknetwork/embedding/tests/test_spring.py +50 -0
- sknetwork/embedding/tests/test_svd.py +43 -0
- sknetwork/gnn/__init__.py +10 -0
- sknetwork/gnn/activation.py +117 -0
- sknetwork/gnn/base.py +181 -0
- sknetwork/gnn/base_activation.py +90 -0
- sknetwork/gnn/base_layer.py +109 -0
- sknetwork/gnn/gnn_classifier.py +305 -0
- sknetwork/gnn/layer.py +153 -0
- sknetwork/gnn/loss.py +180 -0
- sknetwork/gnn/neighbor_sampler.py +65 -0
- sknetwork/gnn/optimizer.py +164 -0
- sknetwork/gnn/tests/__init__.py +1 -0
- sknetwork/gnn/tests/test_activation.py +56 -0
- sknetwork/gnn/tests/test_base.py +75 -0
- sknetwork/gnn/tests/test_base_layer.py +37 -0
- sknetwork/gnn/tests/test_gnn_classifier.py +130 -0
- sknetwork/gnn/tests/test_layers.py +80 -0
- sknetwork/gnn/tests/test_loss.py +33 -0
- sknetwork/gnn/tests/test_neigh_sampler.py +23 -0
- sknetwork/gnn/tests/test_optimizer.py +43 -0
- sknetwork/gnn/tests/test_utils.py +41 -0
- sknetwork/gnn/utils.py +127 -0
- sknetwork/hierarchy/__init__.py +6 -0
- sknetwork/hierarchy/base.py +96 -0
- sknetwork/hierarchy/louvain_hierarchy.py +272 -0
- sknetwork/hierarchy/metrics.py +234 -0
- sknetwork/hierarchy/paris.cpp +37871 -0
- sknetwork/hierarchy/paris.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/hierarchy/paris.pyx +316 -0
- sknetwork/hierarchy/postprocess.py +350 -0
- sknetwork/hierarchy/tests/__init__.py +1 -0
- sknetwork/hierarchy/tests/test_API.py +24 -0
- sknetwork/hierarchy/tests/test_algos.py +34 -0
- sknetwork/hierarchy/tests/test_metrics.py +62 -0
- sknetwork/hierarchy/tests/test_postprocess.py +57 -0
- sknetwork/linalg/__init__.py +9 -0
- sknetwork/linalg/basics.py +37 -0
- sknetwork/linalg/diteration.cpp +27403 -0
- sknetwork/linalg/diteration.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/linalg/diteration.pyx +47 -0
- sknetwork/linalg/eig_solver.py +93 -0
- sknetwork/linalg/laplacian.py +15 -0
- sknetwork/linalg/normalizer.py +86 -0
- sknetwork/linalg/operators.py +225 -0
- sknetwork/linalg/polynome.py +76 -0
- sknetwork/linalg/ppr_solver.py +170 -0
- sknetwork/linalg/push.cpp +31075 -0
- sknetwork/linalg/push.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/linalg/push.pyx +71 -0
- sknetwork/linalg/sparse_lowrank.py +142 -0
- sknetwork/linalg/svd_solver.py +91 -0
- sknetwork/linalg/tests/__init__.py +1 -0
- sknetwork/linalg/tests/test_eig.py +44 -0
- sknetwork/linalg/tests/test_laplacian.py +18 -0
- sknetwork/linalg/tests/test_normalization.py +34 -0
- sknetwork/linalg/tests/test_operators.py +66 -0
- sknetwork/linalg/tests/test_polynome.py +38 -0
- sknetwork/linalg/tests/test_ppr.py +50 -0
- sknetwork/linalg/tests/test_sparse_lowrank.py +61 -0
- sknetwork/linalg/tests/test_svd.py +38 -0
- sknetwork/linkpred/__init__.py +2 -0
- sknetwork/linkpred/base.py +46 -0
- sknetwork/linkpred/nn.py +126 -0
- sknetwork/linkpred/tests/__init__.py +1 -0
- sknetwork/linkpred/tests/test_nn.py +27 -0
- sknetwork/log.py +19 -0
- sknetwork/path/__init__.py +5 -0
- sknetwork/path/dag.py +54 -0
- sknetwork/path/distances.py +98 -0
- sknetwork/path/search.py +31 -0
- sknetwork/path/shortest_path.py +61 -0
- sknetwork/path/tests/__init__.py +1 -0
- sknetwork/path/tests/test_dag.py +37 -0
- sknetwork/path/tests/test_distances.py +62 -0
- sknetwork/path/tests/test_search.py +40 -0
- sknetwork/path/tests/test_shortest_path.py +40 -0
- sknetwork/ranking/__init__.py +8 -0
- sknetwork/ranking/base.py +61 -0
- sknetwork/ranking/betweenness.cpp +9710 -0
- sknetwork/ranking/betweenness.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/ranking/betweenness.pyx +97 -0
- sknetwork/ranking/closeness.py +92 -0
- sknetwork/ranking/hits.py +94 -0
- sknetwork/ranking/katz.py +83 -0
- sknetwork/ranking/pagerank.py +110 -0
- sknetwork/ranking/postprocess.py +37 -0
- sknetwork/ranking/tests/__init__.py +1 -0
- sknetwork/ranking/tests/test_API.py +32 -0
- sknetwork/ranking/tests/test_betweenness.py +38 -0
- sknetwork/ranking/tests/test_closeness.py +30 -0
- sknetwork/ranking/tests/test_hits.py +20 -0
- sknetwork/ranking/tests/test_pagerank.py +62 -0
- sknetwork/ranking/tests/test_postprocess.py +26 -0
- sknetwork/regression/__init__.py +4 -0
- sknetwork/regression/base.py +61 -0
- sknetwork/regression/diffusion.py +210 -0
- sknetwork/regression/tests/__init__.py +1 -0
- sknetwork/regression/tests/test_API.py +32 -0
- sknetwork/regression/tests/test_diffusion.py +56 -0
- sknetwork/sknetwork.py +3 -0
- sknetwork/test_base.py +35 -0
- sknetwork/test_log.py +15 -0
- sknetwork/topology/__init__.py +8 -0
- sknetwork/topology/cliques.cpp +32568 -0
- sknetwork/topology/cliques.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/topology/cliques.pyx +149 -0
- sknetwork/topology/core.cpp +30654 -0
- sknetwork/topology/core.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/topology/core.pyx +90 -0
- sknetwork/topology/cycles.py +243 -0
- sknetwork/topology/minheap.cpp +27335 -0
- sknetwork/topology/minheap.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/topology/minheap.pxd +20 -0
- sknetwork/topology/minheap.pyx +109 -0
- sknetwork/topology/structure.py +194 -0
- sknetwork/topology/tests/__init__.py +1 -0
- sknetwork/topology/tests/test_cliques.py +28 -0
- sknetwork/topology/tests/test_core.py +19 -0
- sknetwork/topology/tests/test_cycles.py +65 -0
- sknetwork/topology/tests/test_structure.py +85 -0
- sknetwork/topology/tests/test_triangles.py +38 -0
- sknetwork/topology/tests/test_wl.py +72 -0
- sknetwork/topology/triangles.cpp +8897 -0
- sknetwork/topology/triangles.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/topology/triangles.pyx +151 -0
- sknetwork/topology/weisfeiler_lehman.py +133 -0
- sknetwork/topology/weisfeiler_lehman_core.cpp +27638 -0
- sknetwork/topology/weisfeiler_lehman_core.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/topology/weisfeiler_lehman_core.pyx +114 -0
- sknetwork/utils/__init__.py +7 -0
- sknetwork/utils/check.py +355 -0
- sknetwork/utils/format.py +221 -0
- sknetwork/utils/membership.py +82 -0
- sknetwork/utils/neighbors.py +115 -0
- sknetwork/utils/tests/__init__.py +1 -0
- sknetwork/utils/tests/test_check.py +190 -0
- sknetwork/utils/tests/test_format.py +63 -0
- sknetwork/utils/tests/test_membership.py +24 -0
- sknetwork/utils/tests/test_neighbors.py +41 -0
- sknetwork/utils/tests/test_tfidf.py +18 -0
- sknetwork/utils/tests/test_values.py +66 -0
- sknetwork/utils/tfidf.py +37 -0
- sknetwork/utils/values.py +76 -0
- sknetwork/visualization/__init__.py +4 -0
- sknetwork/visualization/colors.py +34 -0
- sknetwork/visualization/dendrograms.py +277 -0
- sknetwork/visualization/graphs.py +1039 -0
- sknetwork/visualization/tests/__init__.py +1 -0
- sknetwork/visualization/tests/test_dendrograms.py +53 -0
- sknetwork/visualization/tests/test_graphs.py +176 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created in April 2019
|
|
5
|
+
@author: Nathan de Lara <nathan.delara@polytechnique.org>
|
|
6
|
+
"""
|
|
7
|
+
import warnings
|
|
8
|
+
from typing import Optional, Union
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_values(shape: tuple, values: Union[np.ndarray, list, dict], default_value: float = -1) -> np.ndarray:
|
|
14
|
+
"""Get values as array."""
|
|
15
|
+
n = shape[0]
|
|
16
|
+
if isinstance(values, list):
|
|
17
|
+
values = np.array(values)
|
|
18
|
+
if isinstance(values, np.ndarray):
|
|
19
|
+
if len(values) != n:
|
|
20
|
+
raise ValueError('Dimensions mismatch between adjacency and values.')
|
|
21
|
+
else:
|
|
22
|
+
values = values.astype(float)
|
|
23
|
+
elif isinstance(values, dict):
|
|
24
|
+
keys, values_ = np.array(list(values.keys())), np.array(list(values.values()))
|
|
25
|
+
if np.min(values_) < 0:
|
|
26
|
+
warnings.warn(Warning("Negative values will not be taken into account."))
|
|
27
|
+
values = default_value * np.ones(n)
|
|
28
|
+
values[keys] = values_
|
|
29
|
+
else:
|
|
30
|
+
values = np.ones(n)
|
|
31
|
+
return values
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def stack_values(shape: tuple, values_row: Optional[Union[np.ndarray, list, dict]],
|
|
35
|
+
values_col: Optional[Union[np.ndarray, list, dict]] = None, default_value: float = -1) -> np.ndarray:
|
|
36
|
+
"""Process values for rows and columns and stack the results into a single vector."""
|
|
37
|
+
n_row, n_col = shape
|
|
38
|
+
if values_row is None and values_col is None:
|
|
39
|
+
values_row = np.ones(n_row)
|
|
40
|
+
values_col = default_value * np.ones(n_col)
|
|
41
|
+
elif values_row is None:
|
|
42
|
+
values_row = default_value * np.ones(n_row)
|
|
43
|
+
elif values_col is None:
|
|
44
|
+
values_col = default_value * np.ones(n_col)
|
|
45
|
+
values_row = get_values(shape, values_row, default_value)
|
|
46
|
+
values_col = get_values((n_col,), values_col, default_value)
|
|
47
|
+
return np.hstack((values_row, values_col))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def values2prob(n: int, values: np.ndarray = None) -> np.ndarray:
|
|
51
|
+
"""Transform seed values into probability vector.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
n : int
|
|
56
|
+
Number of nodes.
|
|
57
|
+
values :
|
|
58
|
+
If ``None``, the uniform distribution is used.
|
|
59
|
+
Otherwise, a non-negative, non-zero vector or a dictionary must be provided.
|
|
60
|
+
|
|
61
|
+
Returns
|
|
62
|
+
-------
|
|
63
|
+
probs: np.ndarray
|
|
64
|
+
A probability vector.
|
|
65
|
+
"""
|
|
66
|
+
if values is None:
|
|
67
|
+
return np.ones(n) / n
|
|
68
|
+
else:
|
|
69
|
+
values = get_values((n,), values)
|
|
70
|
+
probs = np.zeros_like(values, dtype=float)
|
|
71
|
+
ix = (values > 0)
|
|
72
|
+
probs[ix] = values[ix]
|
|
73
|
+
if probs.sum() > 0:
|
|
74
|
+
return probs / probs.sum()
|
|
75
|
+
else:
|
|
76
|
+
raise ValueError('At least one value must be positive.')
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on April 2020
|
|
5
|
+
@authors:
|
|
6
|
+
Thomas Bonald <bonald@enst.fr>
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
# standard SVG colors
|
|
12
|
+
STANDARD_COLORS = np.array(['blue', 'red', 'green', 'orange', 'purple', 'yellow', 'fuchsia', 'olive', 'aqua', 'brown'])
|
|
13
|
+
|
|
14
|
+
# 100 RGB colors of coolwarm color map.
|
|
15
|
+
COOLWARM_RGB = np.array([[58, 76, 192], [60, 79, 195], [64, 84, 199], [66, 88, 202], [70, 93, 207], [72, 96, 209],
|
|
16
|
+
[76, 102, 214], [80, 107, 218], [82, 110, 220], [86, 115, 224], [88, 118, 226], [92, 123, 229],
|
|
17
|
+
[96, 128, 232], [99, 131, 234], [103, 136, 237], [105, 139, 239], [109, 144, 241],
|
|
18
|
+
[112, 147, 243], [116, 151, 245], [120, 155, 247], [123, 158, 248], [127, 162, 250],
|
|
19
|
+
[130, 165, 251], [134, 169, 252], [138, 173, 253], [141, 175, 253], [145, 179, 254],
|
|
20
|
+
[148, 181, 254], [152, 185, 254], [155, 187, 254], [159, 190, 254], [163, 193, 254],
|
|
21
|
+
[166, 195, 253], [170, 198, 253], [172, 200, 252], [176, 203, 251], [180, 205, 250],
|
|
22
|
+
[183, 207, 249], [187, 209, 247], [189, 210, 246], [193, 212, 244], [197, 213, 242],
|
|
23
|
+
[199, 214, 240], [202, 216, 238], [205, 217, 236], [208, 218, 233], [210, 218, 231],
|
|
24
|
+
[214, 219, 228], [217, 220, 224], [219, 220, 222], [222, 219, 218], [224, 218, 215],
|
|
25
|
+
[227, 217, 211], [230, 215, 207], [231, 214, 204], [234, 211, 199], [236, 210, 196],
|
|
26
|
+
[237, 207, 192], [239, 206, 188], [241, 203, 184], [242, 200, 179], [243, 198, 176],
|
|
27
|
+
[244, 195, 171], [245, 193, 168], [246, 189, 164], [246, 186, 159], [246, 183, 156],
|
|
28
|
+
[247, 179, 151], [247, 177, 148], [247, 173, 143], [246, 169, 138], [246, 166, 135],
|
|
29
|
+
[245, 161, 130], [245, 158, 127], [244, 154, 123], [243, 150, 120], [242, 145, 115],
|
|
30
|
+
[240, 141, 111], [239, 137, 108], [237, 132, 103], [236, 128, 100], [234, 123, 96],
|
|
31
|
+
[231, 117, 92], [230, 114, 89], [227, 108, 84], [225, 104, 82], [222, 98, 78],
|
|
32
|
+
[220, 94, 75], [217, 88, 71], [214, 82, 67], [211, 77, 64], [207, 70, 61],
|
|
33
|
+
[205, 66, 58], [201, 59, 55], [197, 50, 51], [194, 45, 49], [190, 35, 45],
|
|
34
|
+
[187, 26, 43], [182, 13, 40], [179, 3, 38]])
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created in April 2020
|
|
5
|
+
@author: Thomas Bonald <bonald@enst.fr>
|
|
6
|
+
"""
|
|
7
|
+
from typing import Iterable, Optional
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
from sknetwork.hierarchy.postprocess import cut_straight
|
|
12
|
+
from sknetwork.visualization.colors import STANDARD_COLORS
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_index(dendrogram, reorder=True):
|
|
16
|
+
"""Index nodes for pretty dendrogram."""
|
|
17
|
+
n = dendrogram.shape[0] + 1
|
|
18
|
+
tree = {i: [i] for i in range(n)}
|
|
19
|
+
for t in range(n - 1):
|
|
20
|
+
i = int(dendrogram[t, 0])
|
|
21
|
+
j = int(dendrogram[t, 1])
|
|
22
|
+
left: list = tree.pop(i)
|
|
23
|
+
right: list = tree.pop(j)
|
|
24
|
+
if reorder and len(left) < len(right):
|
|
25
|
+
tree[n + t] = right + left
|
|
26
|
+
else:
|
|
27
|
+
tree[n + t] = left + right
|
|
28
|
+
return list(tree.values())[0]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def svg_dendrogram_top(dendrogram, names, width, height, margin, margin_text, scale, line_width, n_clusters,
|
|
32
|
+
color, colors, font_size, reorder, rotate_names):
|
|
33
|
+
"""Dendrogram as SVG image with root on top."""
|
|
34
|
+
|
|
35
|
+
# scaling
|
|
36
|
+
height *= scale
|
|
37
|
+
width *= scale
|
|
38
|
+
|
|
39
|
+
# positioning
|
|
40
|
+
labels = cut_straight(dendrogram, n_clusters, return_dendrogram=False)
|
|
41
|
+
index = get_index(dendrogram, reorder)
|
|
42
|
+
n = len(index)
|
|
43
|
+
unit_height = height / dendrogram[-1, 2]
|
|
44
|
+
unit_width = width / n
|
|
45
|
+
height_basis = margin + height
|
|
46
|
+
position = {index[i]: (margin + i * unit_width, height_basis) for i in range(n)}
|
|
47
|
+
label = {i: l for i, l in enumerate(labels)}
|
|
48
|
+
width += 2 * margin
|
|
49
|
+
height += 2 * margin
|
|
50
|
+
if names is not None:
|
|
51
|
+
text_length = np.max(np.array([len(str(name)) for name in names]))
|
|
52
|
+
height += text_length * font_size * .5 + margin_text
|
|
53
|
+
|
|
54
|
+
svg = """<svg width="{}" height="{}" xmlns="http://www.w3.org/2000/svg">""".format(width, height)
|
|
55
|
+
|
|
56
|
+
# text
|
|
57
|
+
if names is not None:
|
|
58
|
+
for i in range(n):
|
|
59
|
+
x, y = position[i]
|
|
60
|
+
x -= margin_text
|
|
61
|
+
y += margin_text
|
|
62
|
+
text = str(names[i]).replace('&', ' ')
|
|
63
|
+
if rotate_names:
|
|
64
|
+
svg += """<text x="{}" y="{}" transform="rotate(60, {}, {})" font-size="{}">{}</text>""" \
|
|
65
|
+
.format(x, y, x, y, font_size, text)
|
|
66
|
+
else:
|
|
67
|
+
y += margin_text
|
|
68
|
+
svg += """<text x="{}" y="{}" font-size="{}">{}</text>""" \
|
|
69
|
+
.format(x, y, font_size, text)
|
|
70
|
+
|
|
71
|
+
# tree
|
|
72
|
+
for t in range(n - 1):
|
|
73
|
+
i = int(dendrogram[t, 0])
|
|
74
|
+
j = int(dendrogram[t, 1])
|
|
75
|
+
x1, y1 = position.pop(i)
|
|
76
|
+
x2, y2 = position.pop(j)
|
|
77
|
+
l1 = label.pop(i)
|
|
78
|
+
l2 = label.pop(j)
|
|
79
|
+
if l1 == l2:
|
|
80
|
+
line_color = colors[l1 % len(colors)]
|
|
81
|
+
else:
|
|
82
|
+
line_color = color
|
|
83
|
+
x = .5 * (x1 + x2)
|
|
84
|
+
y = height_basis - dendrogram[t, 2] * unit_height
|
|
85
|
+
svg += """<path stroke-width="{}" stroke="{}" d="M {} {} {} {}" />"""\
|
|
86
|
+
.format(line_width, line_color, x1, y1, x1, y)
|
|
87
|
+
svg += """<path stroke-width="{}" stroke="{}" d="M {} {} {} {}" />"""\
|
|
88
|
+
.format(line_width, line_color, x2, y2, x2, y)
|
|
89
|
+
svg += """<path stroke-width="{}" stroke="{}" d="M {} {} {} {}" />"""\
|
|
90
|
+
.format(line_width, line_color, x1, y, x2, y)
|
|
91
|
+
position[n + t] = (x, y)
|
|
92
|
+
label[n + t] = l1
|
|
93
|
+
|
|
94
|
+
svg += '</svg>'
|
|
95
|
+
return svg
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def svg_dendrogram_left(dendrogram, names, width, height, margin, margin_text, scale, line_width, n_clusters,
|
|
99
|
+
color, colors, font_size, reorder):
|
|
100
|
+
"""Dendrogram as SVG image with root on left side."""
|
|
101
|
+
|
|
102
|
+
# scaling
|
|
103
|
+
height *= scale
|
|
104
|
+
width *= scale
|
|
105
|
+
|
|
106
|
+
# positioning
|
|
107
|
+
labels = cut_straight(dendrogram, n_clusters, return_dendrogram=False)
|
|
108
|
+
index = get_index(dendrogram, reorder)
|
|
109
|
+
n = len(index)
|
|
110
|
+
unit_height = height / n
|
|
111
|
+
unit_width = width / dendrogram[-1, 2]
|
|
112
|
+
width_basis = width + margin
|
|
113
|
+
position = {index[i]: (width_basis, margin + i * unit_height) for i in range(n)}
|
|
114
|
+
label = {i: l for i, l in enumerate(labels)}
|
|
115
|
+
width += 2 * margin
|
|
116
|
+
height += 2 * margin
|
|
117
|
+
if names is not None:
|
|
118
|
+
text_length = np.max(np.array([len(str(name)) for name in names]))
|
|
119
|
+
width += text_length * font_size * .5 + margin_text
|
|
120
|
+
|
|
121
|
+
svg = """<svg width="{}" height="{}" xmlns="http://www.w3.org/2000/svg">""".format(width, height)
|
|
122
|
+
|
|
123
|
+
# text
|
|
124
|
+
if names is not None:
|
|
125
|
+
for i in range(n):
|
|
126
|
+
x, y = position[i]
|
|
127
|
+
x += margin_text
|
|
128
|
+
y += unit_height / 3
|
|
129
|
+
text = str(names[i]).replace('&', ' ')
|
|
130
|
+
svg += """<text x="{}" y="{}" font-size="{}">{}</text>""" \
|
|
131
|
+
.format(x, y, font_size, text)
|
|
132
|
+
|
|
133
|
+
# tree
|
|
134
|
+
for t in range(n - 1):
|
|
135
|
+
i = int(dendrogram[t, 0])
|
|
136
|
+
j = int(dendrogram[t, 1])
|
|
137
|
+
x1, y1 = position.pop(i)
|
|
138
|
+
x2, y2 = position.pop(j)
|
|
139
|
+
l1 = label.pop(i)
|
|
140
|
+
l2 = label.pop(j)
|
|
141
|
+
if l1 == l2:
|
|
142
|
+
line_color = colors[l1 % len(colors)]
|
|
143
|
+
else:
|
|
144
|
+
line_color = color
|
|
145
|
+
y = .5 * (y1 + y2)
|
|
146
|
+
x = width_basis - dendrogram[t, 2] * unit_width
|
|
147
|
+
svg += """<path stroke-width="{}" stroke="{}" d="M {} {} {} {}" />"""\
|
|
148
|
+
.format(line_width, line_color, x1, y1, x, y1)
|
|
149
|
+
svg += """<path stroke-width="{}" stroke="{}" d="M {} {} {} {}" />"""\
|
|
150
|
+
.format(line_width, line_color, x2, y2, x, y2)
|
|
151
|
+
svg += """<path stroke-width="{}" stroke="{}" d="M {} {} {} {}" />"""\
|
|
152
|
+
.format(line_width, line_color, x, y1, x, y2)
|
|
153
|
+
position[n + t] = (x, y)
|
|
154
|
+
label[n + t] = l1
|
|
155
|
+
|
|
156
|
+
svg += '</svg>'
|
|
157
|
+
|
|
158
|
+
return svg
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def visualize_dendrogram(dendrogram: np.ndarray, names: Optional[np.ndarray] = None, rotate: bool = False,
|
|
162
|
+
width: float = 400, height: float = 300, margin: float = 10, margin_text: float = 5,
|
|
163
|
+
scale: float = 1, line_width: float = 2, n_clusters: int = 2, color: str = 'black',
|
|
164
|
+
colors: Optional[Iterable] = None, font_size: int = 12, reorder: bool = False,
|
|
165
|
+
rotate_names: bool = True, filename: Optional[str] = None):
|
|
166
|
+
"""Return the image of a dendrogram in SVG format.
|
|
167
|
+
|
|
168
|
+
Parameters
|
|
169
|
+
----------
|
|
170
|
+
dendrogram :
|
|
171
|
+
Dendrogram to display.
|
|
172
|
+
names :
|
|
173
|
+
Names of leaves.
|
|
174
|
+
rotate :
|
|
175
|
+
If ``True``, rotate the tree so that the root is on the left.
|
|
176
|
+
width :
|
|
177
|
+
Width of the image (margins excluded).
|
|
178
|
+
height :
|
|
179
|
+
Height of the image (margins excluded).
|
|
180
|
+
margin :
|
|
181
|
+
Margin.
|
|
182
|
+
margin_text :
|
|
183
|
+
Margin between leaves and their names, if any.
|
|
184
|
+
scale :
|
|
185
|
+
Scaling factor.
|
|
186
|
+
line_width :
|
|
187
|
+
Line width.
|
|
188
|
+
n_clusters :
|
|
189
|
+
Number of coloured clusters to display.
|
|
190
|
+
color :
|
|
191
|
+
Default SVG color for the dendrogram.
|
|
192
|
+
colors :
|
|
193
|
+
SVG colors of the clusters of the dendrogram (optional).
|
|
194
|
+
font_size :
|
|
195
|
+
Font size.
|
|
196
|
+
reorder :
|
|
197
|
+
If ``True``, reorder leaves so that left subtree has more leaves than right subtree.
|
|
198
|
+
rotate_names :
|
|
199
|
+
If ``True``, rotate names of leaves (only valid if **rotate** is ``False``).
|
|
200
|
+
filename :
|
|
201
|
+
Filename for saving image (optional).
|
|
202
|
+
|
|
203
|
+
Example
|
|
204
|
+
-------
|
|
205
|
+
>>> dendrogram = np.array([[0, 1, 1, 2], [2, 3, 2, 3]])
|
|
206
|
+
>>> from sknetwork.visualization import svg_dendrogram
|
|
207
|
+
>>> image = svg_dendrogram(dendrogram)
|
|
208
|
+
>>> image[1:4]
|
|
209
|
+
'svg'
|
|
210
|
+
"""
|
|
211
|
+
if colors is None:
|
|
212
|
+
colors = STANDARD_COLORS
|
|
213
|
+
elif isinstance(colors, dict):
|
|
214
|
+
colors = np.array(list(colors.values()))
|
|
215
|
+
elif isinstance(colors, list):
|
|
216
|
+
colors = np.array(colors)
|
|
217
|
+
|
|
218
|
+
if rotate:
|
|
219
|
+
svg = svg_dendrogram_left(dendrogram, names, width, height, margin, margin_text, scale, line_width, n_clusters,
|
|
220
|
+
color, colors, font_size, reorder)
|
|
221
|
+
else:
|
|
222
|
+
svg = svg_dendrogram_top(dendrogram, names, width, height, margin, margin_text, scale, line_width, n_clusters,
|
|
223
|
+
color, colors, font_size, reorder, rotate_names)
|
|
224
|
+
|
|
225
|
+
if filename is not None:
|
|
226
|
+
with open(filename + '.svg', 'w') as f:
|
|
227
|
+
f.write(svg)
|
|
228
|
+
|
|
229
|
+
return svg
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def svg_dendrogram(dendrogram: np.ndarray, names: Optional[np.ndarray] = None, rotate: bool = False, width: float = 400,
|
|
233
|
+
height: float = 300, margin: float = 10, margin_text: float = 5, scale: float = 1,
|
|
234
|
+
line_width: float = 2, n_clusters: int = 2, color: str = 'black', colors: Optional[Iterable] = None,
|
|
235
|
+
font_size: int = 12, reorder: bool = False, rotate_names: bool = True,
|
|
236
|
+
filename: Optional[str] = None):
|
|
237
|
+
"""Return the image of a dendrogram in SVG format.
|
|
238
|
+
|
|
239
|
+
Alias for visualize_dendrogram.
|
|
240
|
+
|
|
241
|
+
Parameters
|
|
242
|
+
----------
|
|
243
|
+
dendrogram :
|
|
244
|
+
Dendrogram to display.
|
|
245
|
+
names :
|
|
246
|
+
Names of leaves.
|
|
247
|
+
rotate :
|
|
248
|
+
If ``True``, rotate the tree so that the root is on the left.
|
|
249
|
+
width :
|
|
250
|
+
Width of the image (margins excluded).
|
|
251
|
+
height :
|
|
252
|
+
Height of the image (margins excluded).
|
|
253
|
+
margin :
|
|
254
|
+
Margin.
|
|
255
|
+
margin_text :
|
|
256
|
+
Margin between leaves and their names, if any.
|
|
257
|
+
scale :
|
|
258
|
+
Scaling factor.
|
|
259
|
+
line_width :
|
|
260
|
+
Line width.
|
|
261
|
+
n_clusters :
|
|
262
|
+
Number of coloured clusters to display.
|
|
263
|
+
color :
|
|
264
|
+
Default SVG color for the dendrogram.
|
|
265
|
+
colors :
|
|
266
|
+
SVG colors of the clusters of the dendrogram (optional).
|
|
267
|
+
font_size :
|
|
268
|
+
Font size.
|
|
269
|
+
reorder :
|
|
270
|
+
If ``True``, reorder leaves so that left subtree has more leaves than right subtree.
|
|
271
|
+
rotate_names :
|
|
272
|
+
If ``True``, rotate names of leaves (only valid if **rotate** is ``False``).
|
|
273
|
+
filename :
|
|
274
|
+
Filename for saving image (optional).
|
|
275
|
+
"""
|
|
276
|
+
return visualize_dendrogram(dendrogram, names, rotate, width, height, margin, margin_text, scale, line_width,
|
|
277
|
+
n_clusters, color, colors, font_size, reorder, rotate_names, filename)
|