scikit-network 0.31.0__cp311-cp311-win_amd64.whl → 0.32.1__cp311-cp311-win_amd64.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.31.0.dist-info → scikit_network-0.32.1.dist-info}/AUTHORS.rst +3 -0
- {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/METADATA +19 -3
- {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/RECORD +112 -105
- {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/WHEEL +1 -1
- sknetwork/__init__.py +1 -1
- sknetwork/classification/base.py +1 -1
- sknetwork/classification/base_rank.py +3 -3
- sknetwork/classification/diffusion.py +21 -13
- sknetwork/classification/knn.py +19 -13
- sknetwork/classification/metrics.py +1 -1
- sknetwork/classification/pagerank.py +12 -8
- sknetwork/classification/propagation.py +22 -15
- sknetwork/classification/tests/test_diffusion.py +10 -0
- sknetwork/classification/vote.cp311-win_amd64.pyd +0 -0
- sknetwork/classification/vote.cpp +14549 -8668
- sknetwork/clustering/__init__.py +3 -1
- sknetwork/clustering/base.py +1 -1
- sknetwork/clustering/kcenters.py +253 -0
- sknetwork/clustering/leiden.py +241 -0
- sknetwork/clustering/leiden_core.cp311-win_amd64.pyd +0 -0
- sknetwork/clustering/leiden_core.cpp +31564 -0
- sknetwork/clustering/leiden_core.pyx +124 -0
- sknetwork/clustering/louvain.py +118 -83
- sknetwork/clustering/louvain_core.cp311-win_amd64.pyd +0 -0
- sknetwork/clustering/louvain_core.cpp +21876 -16332
- sknetwork/clustering/louvain_core.pyx +86 -94
- sknetwork/clustering/postprocess.py +2 -2
- sknetwork/clustering/propagation_clustering.py +4 -4
- sknetwork/clustering/tests/test_API.py +7 -3
- sknetwork/clustering/tests/test_kcenters.py +92 -0
- sknetwork/clustering/tests/test_leiden.py +34 -0
- sknetwork/clustering/tests/test_louvain.py +2 -3
- sknetwork/data/load.py +2 -4
- sknetwork/data/parse.py +41 -20
- sknetwork/data/tests/test_parse.py +9 -12
- sknetwork/embedding/__init__.py +0 -1
- sknetwork/embedding/base.py +20 -19
- sknetwork/embedding/force_atlas.py +3 -2
- sknetwork/embedding/louvain_embedding.py +1 -1
- sknetwork/embedding/random_projection.py +5 -3
- sknetwork/embedding/spectral.py +0 -73
- sknetwork/embedding/tests/test_API.py +4 -28
- sknetwork/embedding/tests/test_louvain_embedding.py +4 -9
- sknetwork/embedding/tests/test_spectral.py +2 -5
- sknetwork/embedding/tests/test_svd.py +1 -1
- sknetwork/gnn/base_layer.py +3 -3
- sknetwork/gnn/gnn_classifier.py +40 -86
- sknetwork/gnn/layer.py +1 -1
- sknetwork/gnn/loss.py +1 -1
- sknetwork/gnn/optimizer.py +4 -3
- sknetwork/gnn/tests/test_base_layer.py +4 -4
- sknetwork/gnn/tests/test_gnn_classifier.py +12 -39
- sknetwork/gnn/utils.py +8 -8
- sknetwork/hierarchy/base.py +27 -0
- sknetwork/hierarchy/louvain_hierarchy.py +45 -41
- sknetwork/hierarchy/paris.cp311-win_amd64.pyd +0 -0
- sknetwork/hierarchy/paris.cpp +27521 -20771
- sknetwork/hierarchy/paris.pyx +7 -7
- sknetwork/hierarchy/postprocess.py +16 -16
- sknetwork/hierarchy/tests/test_algos.py +5 -0
- sknetwork/linalg/__init__.py +1 -1
- sknetwork/linalg/diteration.cp311-win_amd64.pyd +0 -0
- sknetwork/linalg/diteration.cpp +13916 -8050
- sknetwork/linalg/{normalization.py → normalizer.py} +17 -14
- sknetwork/linalg/operators.py +1 -1
- sknetwork/linalg/ppr_solver.py +1 -1
- sknetwork/linalg/push.cp311-win_amd64.pyd +0 -0
- sknetwork/linalg/push.cpp +23187 -16973
- sknetwork/linalg/tests/test_normalization.py +3 -7
- sknetwork/linalg/tests/test_operators.py +2 -6
- sknetwork/linalg/tests/test_ppr.py +1 -1
- sknetwork/linkpred/base.py +12 -1
- sknetwork/linkpred/nn.py +6 -6
- sknetwork/path/distances.py +11 -4
- sknetwork/path/shortest_path.py +1 -1
- sknetwork/path/tests/test_distances.py +7 -0
- sknetwork/path/tests/test_search.py +2 -2
- sknetwork/ranking/base.py +11 -6
- sknetwork/ranking/betweenness.cp311-win_amd64.pyd +0 -0
- sknetwork/ranking/betweenness.cpp +5256 -2190
- sknetwork/ranking/pagerank.py +13 -12
- sknetwork/ranking/tests/test_API.py +0 -2
- sknetwork/ranking/tests/test_betweenness.py +1 -1
- sknetwork/ranking/tests/test_pagerank.py +11 -5
- sknetwork/regression/base.py +18 -1
- sknetwork/regression/diffusion.py +24 -10
- sknetwork/regression/tests/test_diffusion.py +8 -0
- sknetwork/topology/__init__.py +3 -1
- sknetwork/topology/cliques.cp311-win_amd64.pyd +0 -0
- sknetwork/topology/cliques.cpp +23528 -16848
- sknetwork/topology/core.cp311-win_amd64.pyd +0 -0
- sknetwork/topology/core.cpp +22849 -16581
- sknetwork/topology/cycles.py +243 -0
- sknetwork/topology/minheap.cp311-win_amd64.pyd +0 -0
- sknetwork/topology/minheap.cpp +19495 -13469
- sknetwork/topology/structure.py +2 -42
- sknetwork/topology/tests/test_cycles.py +65 -0
- sknetwork/topology/tests/test_structure.py +2 -16
- sknetwork/topology/triangles.cp311-win_amd64.pyd +0 -0
- sknetwork/topology/triangles.cpp +5283 -1397
- sknetwork/topology/triangles.pyx +7 -4
- sknetwork/topology/weisfeiler_lehman_core.cp311-win_amd64.pyd +0 -0
- sknetwork/topology/weisfeiler_lehman_core.cpp +14781 -8915
- sknetwork/utils/format.py +1 -1
- sknetwork/utils/membership.py +2 -2
- sknetwork/visualization/__init__.py +2 -2
- sknetwork/visualization/dendrograms.py +55 -7
- sknetwork/visualization/graphs.py +261 -44
- sknetwork/visualization/tests/test_dendrograms.py +9 -9
- sknetwork/visualization/tests/test_graphs.py +63 -57
- sknetwork/embedding/louvain_hierarchy.py +0 -142
- sknetwork/embedding/tests/test_louvain_hierarchy.py +0 -19
- {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/LICENSE +0 -0
- {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/top_level.txt +0 -0
sknetwork/gnn/optimizer.py
CHANGED
|
@@ -130,9 +130,10 @@ class ADAM(BaseOptimizer):
|
|
|
130
130
|
layer.weight = \
|
|
131
131
|
layer.weight - (self.learning_rate * m_derivative_weight_corr) / (np.sqrt(v_derivative_weight_corr)
|
|
132
132
|
+ self.eps)
|
|
133
|
-
layer.
|
|
134
|
-
layer.bias
|
|
135
|
-
|
|
133
|
+
if layer.use_bias:
|
|
134
|
+
layer.bias = \
|
|
135
|
+
layer.bias - (self.learning_rate * m_derivative_bias_corr) / (np.sqrt(v_derivative_bias_corr)
|
|
136
|
+
+ self.eps)
|
|
136
137
|
|
|
137
138
|
|
|
138
139
|
def get_optimizer(optimizer: Union[BaseOptimizer, str] = 'Adam', learning_rate: float = 0.01) -> BaseOptimizer:
|
|
@@ -27,11 +27,11 @@ class TestBaseLayer(unittest.TestCase):
|
|
|
27
27
|
def test_base_layer_initialize_weights(self):
|
|
28
28
|
self.base_layer._initialize_weights(10)
|
|
29
29
|
self.assertTrue(self.base_layer.weight.shape == (10, len(self.labels)))
|
|
30
|
-
self.assertTrue(
|
|
30
|
+
self.assertTrue(self.base_layer.bias.shape == (1, len(self.labels)))
|
|
31
31
|
self.assertTrue(self.base_layer.weights_initialized)
|
|
32
32
|
|
|
33
33
|
def test_base_layer_repr(self):
|
|
34
34
|
self.assertTrue(self.base_layer.__repr__().startswith(" BaseLayer(layer_type: Conv, out_channels: 10"))
|
|
35
|
-
|
|
36
|
-
self.assertTrue('sample_size' in
|
|
37
|
-
self.assertTrue('sageconv' in
|
|
35
|
+
sage_layer = BaseLayer(layer_type='sageconv', out_channels=len(self.labels))
|
|
36
|
+
self.assertTrue('sample_size' in sage_layer.__repr__())
|
|
37
|
+
self.assertTrue('sageconv' in sage_layer.__repr__())
|
|
@@ -44,6 +44,14 @@ class TestGNNClassifier(unittest.TestCase):
|
|
|
44
44
|
self.assertTrue(len(y_pred) == self.n)
|
|
45
45
|
self.assertTrue(embedding.shape == (self.n, 2))
|
|
46
46
|
|
|
47
|
+
def test_gnn_classifier_no_bias(self):
|
|
48
|
+
gnn = GNNClassifier([3, 2], 'Conv', 'Softmax', use_bias=[True, False])
|
|
49
|
+
labels_pred = gnn.fit_predict(self.adjacency, self.features, self.labels)
|
|
50
|
+
embedding = gnn.embedding_
|
|
51
|
+
self.assertTrue(len(labels_pred) == self.n)
|
|
52
|
+
self.assertTrue(embedding.shape == (self.n, 2))
|
|
53
|
+
self.assertTrue(gnn.layers[1].bias is None)
|
|
54
|
+
|
|
47
55
|
def test_gnn_classifier_optimizer(self):
|
|
48
56
|
optimizers = ['GD', 'Adam']
|
|
49
57
|
for optimizer in optimizers:
|
|
@@ -88,23 +96,20 @@ class TestGNNClassifier(unittest.TestCase):
|
|
|
88
96
|
def test_gnn_classifier_early_stopping(self):
|
|
89
97
|
gnn = GNNClassifier(2, patience=2)
|
|
90
98
|
labels = {0: 0, 1: 1}
|
|
91
|
-
_ = gnn.fit_predict(self.adjacency, self.features, labels, n_epochs=100,
|
|
99
|
+
_ = gnn.fit_predict(self.adjacency, self.features, labels, n_epochs=100, validation=0.5,
|
|
92
100
|
random_state=42)
|
|
93
101
|
self.assertTrue(len(gnn.history_['val_accuracy']) < 100)
|
|
94
102
|
|
|
95
103
|
gnn = GNNClassifier(2, early_stopping=False)
|
|
96
|
-
_ = gnn.fit_predict(self.adjacency, self.features, labels, n_epochs=100,
|
|
104
|
+
_ = gnn.fit_predict(self.adjacency, self.features, labels, n_epochs=100, validation=0.5,
|
|
97
105
|
random_state=42)
|
|
98
106
|
self.assertTrue(len(gnn.history_['val_accuracy']) == 100)
|
|
99
107
|
|
|
100
108
|
def test_gnn_classifier_reinit(self):
|
|
101
109
|
gnn = GNNClassifier([4, 2])
|
|
102
|
-
gnn.fit(self.adjacency, self.features, self.labels
|
|
103
|
-
weights = [layer.weight for layer in gnn.layers]
|
|
104
|
-
biases = [layer.bias for layer in gnn.layers]
|
|
110
|
+
gnn.fit(self.adjacency, self.features, self.labels)
|
|
105
111
|
gnn.fit(self.adjacency, self.features, self.labels, n_epochs=1, reinit=True)
|
|
106
|
-
self.assertTrue(
|
|
107
|
-
self.assertTrue(all([np.all(bias != layer.bias) for bias, layer in zip(biases, gnn.layers)]))
|
|
112
|
+
self.assertTrue(gnn.embedding_.shape == (self.n, 2))
|
|
108
113
|
|
|
109
114
|
def test_gnn_classifier_sageconv(self):
|
|
110
115
|
gnn = GNNClassifier([4, 2], ['SAGEConv', 'SAGEConv'], sample_sizes=[5, 3])
|
|
@@ -119,38 +124,6 @@ class TestGNNClassifier(unittest.TestCase):
|
|
|
119
124
|
self.assertTrue(all(labels_pred == gnn.labels_))
|
|
120
125
|
self.assertTrue(all(labels_pred == labels_pred_))
|
|
121
126
|
|
|
122
|
-
# Predict same nodes
|
|
123
|
-
labels_pred_ = gnn.predict(self.adjacency, self.features)
|
|
124
|
-
self.assertTrue(all(labels_pred_ == gnn.labels_))
|
|
125
|
-
|
|
126
|
-
# Incorrect shapes
|
|
127
|
-
new_n = sparse.csr_matrix(np.random.randint(2, size=self.features.shape[1]))
|
|
128
|
-
new_feat = sparse.csr_matrix(np.random.randint(3, size=self.features.shape[1]))
|
|
129
|
-
with self.assertRaises(ValueError):
|
|
130
|
-
gnn.predict(new_n, self.features)
|
|
131
|
-
with self.assertRaises(ValueError):
|
|
132
|
-
gnn.predict(self.adjacency, new_feat)
|
|
133
|
-
|
|
134
|
-
new_feat = sparse.csr_matrix(np.random.rand(self.adjacency.shape[0], self.features.shape[1] - 1))
|
|
135
|
-
with self.assertRaises(ValueError):
|
|
136
|
-
gnn.predict(self.adjacency, new_feat)
|
|
137
|
-
|
|
138
|
-
# Predict new graph
|
|
139
|
-
n = 4
|
|
140
|
-
n_feat = self.features.shape[1]
|
|
141
|
-
adjacency = sparse.csr_matrix(np.random.randint(2, size=(n, n)))
|
|
142
|
-
features = sparse.csr_matrix(np.random.randint(2, size=(n, n_feat)))
|
|
143
|
-
labels_pred = gnn.predict(adjacency, features)
|
|
144
|
-
self.assertTrue(len(labels_pred) == n)
|
|
145
|
-
|
|
146
|
-
# No adj matrix
|
|
147
|
-
labels_pred = gnn.predict(None, features)
|
|
148
|
-
self.assertTrue(len(labels_pred) == features.shape[0])
|
|
149
|
-
|
|
150
|
-
# No feature matrix
|
|
151
|
-
with self.assertRaises(ValueError):
|
|
152
|
-
gnn.predict(new_n)
|
|
153
|
-
|
|
154
127
|
def test_gnn_classifier_predict_proba(self):
|
|
155
128
|
gnn = GNNClassifier([4, 2])
|
|
156
129
|
probs = gnn.fit_predict_proba(self.adjacency, self.features, self.labels)
|
sknetwork/gnn/utils.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# coding: utf-8
|
|
3
3
|
"""
|
|
4
|
-
Created
|
|
4
|
+
Created in April 2022
|
|
5
5
|
@author: Simon Delarue <sdelarue@enst.fr>
|
|
6
6
|
"""
|
|
7
|
-
from typing import Union
|
|
7
|
+
from typing import Iterable, Union
|
|
8
8
|
|
|
9
9
|
import numpy as np
|
|
10
10
|
|
|
@@ -22,7 +22,7 @@ def check_early_stopping(early_stopping: bool, val_mask: np.ndarray, patience: i
|
|
|
22
22
|
return early_stopping
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def check_normalizations(normalizations: Union[str,
|
|
25
|
+
def check_normalizations(normalizations: Union[str, Iterable]):
|
|
26
26
|
"""Check if normalization is known."""
|
|
27
27
|
available_norms = ['left', 'right', 'both']
|
|
28
28
|
if isinstance(normalizations, list):
|
|
@@ -69,10 +69,10 @@ def check_loss(layer: BaseLayer):
|
|
|
69
69
|
return layer.activation
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
def get_layers(dims: Union[int,
|
|
73
|
-
activations: Union[str, BaseActivation, list], use_bias: Union[bool,
|
|
74
|
-
normalizations: Union[str,
|
|
75
|
-
loss: Union[str, BaseLoss]) -> list:
|
|
72
|
+
def get_layers(dims: Union[int, Iterable], layer_types: Union[str, BaseLayer, Iterable],
|
|
73
|
+
activations: Union[str, BaseActivation, list], use_bias: Union[bool, Iterable],
|
|
74
|
+
normalizations: Union[str, Iterable], self_embeddings: Union[bool, Iterable],
|
|
75
|
+
sample_sizes: Union[int, Iterable], loss: Union[str, BaseLoss]) -> list:
|
|
76
76
|
"""Get the list of layers.
|
|
77
77
|
|
|
78
78
|
Parameters
|
|
@@ -101,7 +101,7 @@ def get_layers(dims: Union[int, list], layer_types: Union[str, BaseLayer, list],
|
|
|
101
101
|
"""
|
|
102
102
|
check_normalizations(normalizations)
|
|
103
103
|
|
|
104
|
-
if
|
|
104
|
+
if isinstance(dims, int):
|
|
105
105
|
dims = [dims]
|
|
106
106
|
n_layers = len(dims)
|
|
107
107
|
|
sknetwork/hierarchy/base.py
CHANGED
|
@@ -29,6 +29,33 @@ class BaseHierarchy(Algorithm, ABC):
|
|
|
29
29
|
def __init__(self):
|
|
30
30
|
self._init_vars()
|
|
31
31
|
|
|
32
|
+
def predict(self, columns: bool = False) -> np.ndarray:
|
|
33
|
+
"""Return the dendrogram predicted by the algorithm.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
columns : bool
|
|
38
|
+
If ``True``, return the prediction for columns.
|
|
39
|
+
|
|
40
|
+
Returns
|
|
41
|
+
-------
|
|
42
|
+
dendrogram : np.ndarray
|
|
43
|
+
Dendrogram.
|
|
44
|
+
"""
|
|
45
|
+
if columns:
|
|
46
|
+
return self.dendrogram_col_
|
|
47
|
+
return self.dendrogram_
|
|
48
|
+
|
|
49
|
+
def transform(self) -> np.ndarray:
|
|
50
|
+
"""Return the dendrogram predicted by the algorithm.
|
|
51
|
+
|
|
52
|
+
Returns
|
|
53
|
+
-------
|
|
54
|
+
dendrogram : np.ndarray
|
|
55
|
+
Dendrogram.
|
|
56
|
+
"""
|
|
57
|
+
return self.dendrogram_
|
|
58
|
+
|
|
32
59
|
def fit_predict(self, *args, **kwargs) -> np.ndarray:
|
|
33
60
|
"""Fit algorithm to data and return the dendrogram. Same parameters as the ``fit`` method.
|
|
34
61
|
|
|
@@ -22,34 +22,34 @@ class LouvainIteration(BaseHierarchy):
|
|
|
22
22
|
|
|
23
23
|
Parameters
|
|
24
24
|
----------
|
|
25
|
-
depth :
|
|
25
|
+
depth : int
|
|
26
26
|
Depth of the tree.
|
|
27
27
|
A negative value is interpreted as no limit (return a tree of maximum depth).
|
|
28
|
-
resolution :
|
|
28
|
+
resolution : float
|
|
29
29
|
Resolution parameter.
|
|
30
|
-
tol_optimization :
|
|
30
|
+
tol_optimization : float
|
|
31
31
|
Minimum increase in the objective function to enter a new optimization pass.
|
|
32
|
-
tol_aggregation :
|
|
32
|
+
tol_aggregation : float
|
|
33
33
|
Minimum increase in the objective function to enter a new aggregation pass.
|
|
34
|
-
n_aggregations :
|
|
34
|
+
n_aggregations : int
|
|
35
35
|
Maximum number of aggregations.
|
|
36
36
|
A negative value is interpreted as no limit.
|
|
37
|
-
shuffle_nodes :
|
|
38
|
-
|
|
39
|
-
random_state :
|
|
37
|
+
shuffle_nodes : bool
|
|
38
|
+
If ``True``, shuffle nodes before optimization.
|
|
39
|
+
random_state : int
|
|
40
40
|
Random number generator or random seed. If ``None``, numpy.random is used.
|
|
41
|
-
verbose :
|
|
41
|
+
verbose : bool
|
|
42
42
|
Verbose mode.
|
|
43
43
|
|
|
44
44
|
Attributes
|
|
45
45
|
----------
|
|
46
|
-
dendrogram_ :
|
|
46
|
+
dendrogram_ : np.ndarray
|
|
47
47
|
Dendrogram of the graph.
|
|
48
|
-
dendrogram_row_ :
|
|
48
|
+
dendrogram_row_ : np.ndarray
|
|
49
49
|
Dendrogram for the rows, for bipartite graphs.
|
|
50
|
-
dendrogram_col_ :
|
|
50
|
+
dendrogram_col_ : np.ndarray
|
|
51
51
|
Dendrogram for the columns, for bipartite graphs.
|
|
52
|
-
dendrogram_full_ :
|
|
52
|
+
dendrogram_full_ : np.ndarray
|
|
53
53
|
Dendrogram for both rows and columns, indexed in this order, for bipartite graphs.
|
|
54
54
|
|
|
55
55
|
Example
|
|
@@ -59,10 +59,10 @@ class LouvainIteration(BaseHierarchy):
|
|
|
59
59
|
>>> louvain = LouvainIteration()
|
|
60
60
|
>>> adjacency = house()
|
|
61
61
|
>>> louvain.fit_predict(adjacency)
|
|
62
|
-
array([[3., 2.,
|
|
63
|
-
[4., 1.,
|
|
64
|
-
[6., 0.,
|
|
65
|
-
[5., 7.,
|
|
62
|
+
array([[3., 2., 1., 2.],
|
|
63
|
+
[4., 1., 1., 2.],
|
|
64
|
+
[6., 0., 1., 3.],
|
|
65
|
+
[5., 7., 2., 5.]])
|
|
66
66
|
|
|
67
67
|
Notes
|
|
68
68
|
-----
|
|
@@ -71,6 +71,7 @@ class LouvainIteration(BaseHierarchy):
|
|
|
71
71
|
See Also
|
|
72
72
|
--------
|
|
73
73
|
scipy.cluster.hierarchy.dendrogram
|
|
74
|
+
sknetwork.clustering.Louvain
|
|
74
75
|
"""
|
|
75
76
|
|
|
76
77
|
def __init__(self, depth: int = 3, resolution: float = 1, tol_optimization: float = 1e-3,
|
|
@@ -91,11 +92,11 @@ class LouvainIteration(BaseHierarchy):
|
|
|
91
92
|
|
|
92
93
|
Parameters
|
|
93
94
|
----------
|
|
94
|
-
adjacency :
|
|
95
|
+
adjacency : sparse.csr_matrix, np.ndarray
|
|
95
96
|
Adjacency matrix of the graph.
|
|
96
|
-
depth :
|
|
97
|
+
depth : int
|
|
97
98
|
Depth of the recursion.
|
|
98
|
-
nodes :
|
|
99
|
+
nodes : np.ndarray
|
|
99
100
|
The indices of the current nodes in the original graph.
|
|
100
101
|
|
|
101
102
|
Returns
|
|
@@ -132,7 +133,7 @@ class LouvainIteration(BaseHierarchy):
|
|
|
132
133
|
|
|
133
134
|
Parameters
|
|
134
135
|
----------
|
|
135
|
-
input_matrix :
|
|
136
|
+
input_matrix : sparse.csr_matrix, np.ndarray
|
|
136
137
|
Adjacency matrix or biadjacency matrix of the graph.
|
|
137
138
|
|
|
138
139
|
Returns
|
|
@@ -145,7 +146,7 @@ class LouvainIteration(BaseHierarchy):
|
|
|
145
146
|
tree = self._recursive_louvain(adjacency, self.depth)
|
|
146
147
|
dendrogram, _ = get_dendrogram(tree)
|
|
147
148
|
dendrogram = np.array(dendrogram)
|
|
148
|
-
dendrogram[:, 2]
|
|
149
|
+
dendrogram[:, 2] += 1 - min(dendrogram[:, 2])
|
|
149
150
|
self.dendrogram_ = reorder_dendrogram(dendrogram)
|
|
150
151
|
if self.bipartite:
|
|
151
152
|
self._split_vars(input_matrix.shape)
|
|
@@ -155,30 +156,32 @@ class LouvainIteration(BaseHierarchy):
|
|
|
155
156
|
class LouvainHierarchy(BaseHierarchy):
|
|
156
157
|
"""Hierarchical clustering by Louvain (bottom-up).
|
|
157
158
|
|
|
159
|
+
Each level corresponds to an aggregation step of the Louvain algorithm.
|
|
160
|
+
|
|
158
161
|
Parameters
|
|
159
162
|
----------
|
|
160
|
-
resolution :
|
|
163
|
+
resolution : float
|
|
161
164
|
Resolution parameter.
|
|
162
|
-
tol_optimization :
|
|
165
|
+
tol_optimization : float
|
|
163
166
|
Minimum increase in the objective function to enter a new optimization pass.
|
|
164
|
-
tol_aggregation :
|
|
167
|
+
tol_aggregation : float
|
|
165
168
|
Minimum increase in the objective function to enter a new aggregation pass.
|
|
166
|
-
shuffle_nodes :
|
|
167
|
-
|
|
168
|
-
random_state :
|
|
169
|
+
shuffle_nodes : bool
|
|
170
|
+
If ``True``, shuffle nodes before optimization.
|
|
171
|
+
random_state : int
|
|
169
172
|
Random number generator or random seed. If ``None``, numpy.random is used.
|
|
170
|
-
verbose :
|
|
173
|
+
verbose : bool
|
|
171
174
|
Verbose mode.
|
|
172
175
|
|
|
173
176
|
Attributes
|
|
174
177
|
----------
|
|
175
|
-
dendrogram_ :
|
|
178
|
+
dendrogram_ : np.ndarray
|
|
176
179
|
Dendrogram of the graph.
|
|
177
|
-
dendrogram_row_ :
|
|
180
|
+
dendrogram_row_ : np.ndarray
|
|
178
181
|
Dendrogram for the rows, for bipartite graphs.
|
|
179
|
-
dendrogram_col_ :
|
|
182
|
+
dendrogram_col_ : np.ndarray
|
|
180
183
|
Dendrogram for the columns, for bipartite graphs.
|
|
181
|
-
dendrogram_full_ :
|
|
184
|
+
dendrogram_full_ : np.ndarray
|
|
182
185
|
Dendrogram for both rows and columns, indexed in this order, for bipartite graphs.
|
|
183
186
|
|
|
184
187
|
Example
|
|
@@ -188,10 +191,10 @@ class LouvainHierarchy(BaseHierarchy):
|
|
|
188
191
|
>>> louvain = LouvainHierarchy()
|
|
189
192
|
>>> adjacency = house()
|
|
190
193
|
>>> louvain.fit_predict(adjacency)
|
|
191
|
-
array([[3., 2.,
|
|
192
|
-
[4., 1.,
|
|
193
|
-
[6., 0.,
|
|
194
|
-
[5., 7.,
|
|
194
|
+
array([[3., 2., 1., 2.],
|
|
195
|
+
[4., 1., 1., 2.],
|
|
196
|
+
[6., 0., 1., 3.],
|
|
197
|
+
[5., 7., 2., 5.]])
|
|
195
198
|
|
|
196
199
|
Notes
|
|
197
200
|
-----
|
|
@@ -200,6 +203,7 @@ class LouvainHierarchy(BaseHierarchy):
|
|
|
200
203
|
See Also
|
|
201
204
|
--------
|
|
202
205
|
scipy.cluster.hierarchy.dendrogram
|
|
206
|
+
sknetwork.clustering.Louvain
|
|
203
207
|
"""
|
|
204
208
|
|
|
205
209
|
def __init__(self, resolution: float = 1, tol_optimization: float = 1e-3,
|
|
@@ -218,7 +222,7 @@ class LouvainHierarchy(BaseHierarchy):
|
|
|
218
222
|
|
|
219
223
|
Parameters
|
|
220
224
|
----------
|
|
221
|
-
adjacency :
|
|
225
|
+
adjacency : sparse.csr_matrix, np.ndarray
|
|
222
226
|
Adjacency matrix of the graph.
|
|
223
227
|
|
|
224
228
|
Returns
|
|
@@ -244,12 +248,12 @@ class LouvainHierarchy(BaseHierarchy):
|
|
|
244
248
|
|
|
245
249
|
Parameters
|
|
246
250
|
----------
|
|
247
|
-
input_matrix :
|
|
251
|
+
input_matrix : sparse.csr_matrix, np.ndarray
|
|
248
252
|
Adjacency matrix or biadjacency matrix of the graph.
|
|
249
253
|
|
|
250
254
|
Returns
|
|
251
255
|
-------
|
|
252
|
-
self: :class:`
|
|
256
|
+
self: :class:`LouvainHierarchy`
|
|
253
257
|
"""
|
|
254
258
|
self._init_vars()
|
|
255
259
|
input_matrix = check_format(input_matrix)
|
|
@@ -257,7 +261,7 @@ class LouvainHierarchy(BaseHierarchy):
|
|
|
257
261
|
tree = self._get_hierarchy(adjacency)
|
|
258
262
|
dendrogram, _ = get_dendrogram(tree)
|
|
259
263
|
dendrogram = np.array(dendrogram)
|
|
260
|
-
dendrogram[:, 2]
|
|
264
|
+
dendrogram[:, 2] += 1 - min(dendrogram[:, 2])
|
|
261
265
|
self.dendrogram_ = reorder_dendrogram(dendrogram)
|
|
262
266
|
if self.bipartite:
|
|
263
267
|
self._split_vars(input_matrix.shape)
|
|
Binary file
|