scikit-network 0.31.0__cp39-cp39-win_amd64.whl → 0.32.1__cp39-cp39-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.

Files changed (114) hide show
  1. {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/AUTHORS.rst +3 -0
  2. {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/METADATA +19 -3
  3. {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/RECORD +112 -105
  4. {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/WHEEL +1 -1
  5. sknetwork/__init__.py +1 -1
  6. sknetwork/classification/base.py +1 -1
  7. sknetwork/classification/base_rank.py +3 -3
  8. sknetwork/classification/diffusion.py +21 -13
  9. sknetwork/classification/knn.py +19 -13
  10. sknetwork/classification/metrics.py +1 -1
  11. sknetwork/classification/pagerank.py +12 -8
  12. sknetwork/classification/propagation.py +22 -15
  13. sknetwork/classification/tests/test_diffusion.py +10 -0
  14. sknetwork/classification/vote.cp39-win_amd64.pyd +0 -0
  15. sknetwork/classification/vote.cpp +14549 -8668
  16. sknetwork/clustering/__init__.py +3 -1
  17. sknetwork/clustering/base.py +1 -1
  18. sknetwork/clustering/kcenters.py +253 -0
  19. sknetwork/clustering/leiden.py +241 -0
  20. sknetwork/clustering/leiden_core.cp39-win_amd64.pyd +0 -0
  21. sknetwork/clustering/leiden_core.cpp +31564 -0
  22. sknetwork/clustering/leiden_core.pyx +124 -0
  23. sknetwork/clustering/louvain.py +118 -83
  24. sknetwork/clustering/louvain_core.cp39-win_amd64.pyd +0 -0
  25. sknetwork/clustering/louvain_core.cpp +21876 -16332
  26. sknetwork/clustering/louvain_core.pyx +86 -94
  27. sknetwork/clustering/postprocess.py +2 -2
  28. sknetwork/clustering/propagation_clustering.py +4 -4
  29. sknetwork/clustering/tests/test_API.py +7 -3
  30. sknetwork/clustering/tests/test_kcenters.py +92 -0
  31. sknetwork/clustering/tests/test_leiden.py +34 -0
  32. sknetwork/clustering/tests/test_louvain.py +2 -3
  33. sknetwork/data/load.py +2 -4
  34. sknetwork/data/parse.py +41 -20
  35. sknetwork/data/tests/test_parse.py +9 -12
  36. sknetwork/embedding/__init__.py +0 -1
  37. sknetwork/embedding/base.py +20 -19
  38. sknetwork/embedding/force_atlas.py +3 -2
  39. sknetwork/embedding/louvain_embedding.py +1 -1
  40. sknetwork/embedding/random_projection.py +5 -3
  41. sknetwork/embedding/spectral.py +0 -73
  42. sknetwork/embedding/tests/test_API.py +4 -28
  43. sknetwork/embedding/tests/test_louvain_embedding.py +4 -9
  44. sknetwork/embedding/tests/test_spectral.py +2 -5
  45. sknetwork/embedding/tests/test_svd.py +1 -1
  46. sknetwork/gnn/base_layer.py +3 -3
  47. sknetwork/gnn/gnn_classifier.py +40 -86
  48. sknetwork/gnn/layer.py +1 -1
  49. sknetwork/gnn/loss.py +1 -1
  50. sknetwork/gnn/optimizer.py +4 -3
  51. sknetwork/gnn/tests/test_base_layer.py +4 -4
  52. sknetwork/gnn/tests/test_gnn_classifier.py +12 -39
  53. sknetwork/gnn/utils.py +8 -8
  54. sknetwork/hierarchy/base.py +27 -0
  55. sknetwork/hierarchy/louvain_hierarchy.py +45 -41
  56. sknetwork/hierarchy/paris.cp39-win_amd64.pyd +0 -0
  57. sknetwork/hierarchy/paris.cpp +27521 -20771
  58. sknetwork/hierarchy/paris.pyx +7 -7
  59. sknetwork/hierarchy/postprocess.py +16 -16
  60. sknetwork/hierarchy/tests/test_algos.py +5 -0
  61. sknetwork/linalg/__init__.py +1 -1
  62. sknetwork/linalg/diteration.cp39-win_amd64.pyd +0 -0
  63. sknetwork/linalg/diteration.cpp +13916 -8050
  64. sknetwork/linalg/{normalization.py → normalizer.py} +17 -14
  65. sknetwork/linalg/operators.py +1 -1
  66. sknetwork/linalg/ppr_solver.py +1 -1
  67. sknetwork/linalg/push.cp39-win_amd64.pyd +0 -0
  68. sknetwork/linalg/push.cpp +23187 -16973
  69. sknetwork/linalg/tests/test_normalization.py +3 -7
  70. sknetwork/linalg/tests/test_operators.py +2 -6
  71. sknetwork/linalg/tests/test_ppr.py +1 -1
  72. sknetwork/linkpred/base.py +12 -1
  73. sknetwork/linkpred/nn.py +6 -6
  74. sknetwork/path/distances.py +11 -4
  75. sknetwork/path/shortest_path.py +1 -1
  76. sknetwork/path/tests/test_distances.py +7 -0
  77. sknetwork/path/tests/test_search.py +2 -2
  78. sknetwork/ranking/base.py +11 -6
  79. sknetwork/ranking/betweenness.cp39-win_amd64.pyd +0 -0
  80. sknetwork/ranking/betweenness.cpp +5256 -2190
  81. sknetwork/ranking/pagerank.py +13 -12
  82. sknetwork/ranking/tests/test_API.py +0 -2
  83. sknetwork/ranking/tests/test_betweenness.py +1 -1
  84. sknetwork/ranking/tests/test_pagerank.py +11 -5
  85. sknetwork/regression/base.py +18 -1
  86. sknetwork/regression/diffusion.py +24 -10
  87. sknetwork/regression/tests/test_diffusion.py +8 -0
  88. sknetwork/topology/__init__.py +3 -1
  89. sknetwork/topology/cliques.cp39-win_amd64.pyd +0 -0
  90. sknetwork/topology/cliques.cpp +23528 -16848
  91. sknetwork/topology/core.cp39-win_amd64.pyd +0 -0
  92. sknetwork/topology/core.cpp +22849 -16581
  93. sknetwork/topology/cycles.py +243 -0
  94. sknetwork/topology/minheap.cp39-win_amd64.pyd +0 -0
  95. sknetwork/topology/minheap.cpp +19495 -13469
  96. sknetwork/topology/structure.py +2 -42
  97. sknetwork/topology/tests/test_cycles.py +65 -0
  98. sknetwork/topology/tests/test_structure.py +2 -16
  99. sknetwork/topology/triangles.cp39-win_amd64.pyd +0 -0
  100. sknetwork/topology/triangles.cpp +5283 -1397
  101. sknetwork/topology/triangles.pyx +7 -4
  102. sknetwork/topology/weisfeiler_lehman_core.cp39-win_amd64.pyd +0 -0
  103. sknetwork/topology/weisfeiler_lehman_core.cpp +14781 -8915
  104. sknetwork/utils/format.py +1 -1
  105. sknetwork/utils/membership.py +2 -2
  106. sknetwork/visualization/__init__.py +2 -2
  107. sknetwork/visualization/dendrograms.py +55 -7
  108. sknetwork/visualization/graphs.py +261 -44
  109. sknetwork/visualization/tests/test_dendrograms.py +9 -9
  110. sknetwork/visualization/tests/test_graphs.py +63 -57
  111. sknetwork/embedding/louvain_hierarchy.py +0 -142
  112. sknetwork/embedding/tests/test_louvain_hierarchy.py +0 -19
  113. {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/LICENSE +0 -0
  114. {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/top_level.txt +0 -0
@@ -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.bias = \
134
- layer.bias - (self.learning_rate * m_derivative_bias_corr) / (np.sqrt(v_derivative_bias_corr)
135
- + self.eps)
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(all(self.base_layer.bias[0] == np.zeros((len(self.labels), 1)).T[0]))
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
- sagelayer = BaseLayer(layer_type='sageconv', out_channels=len(self.labels))
36
- self.assertTrue('sample_size' in sagelayer.__repr__())
37
- self.assertTrue('sageconv' in sagelayer.__repr__())
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, history=True, validation=0.5,
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, history=True, validation=0.5,
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, reinit=False)
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(all([np.all(weight != layer.weight) for weight, layer in zip(weights, gnn.layers)]))
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 on Thu Apr 21 2022
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, list]):
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, list], layer_types: Union[str, BaseLayer, list],
73
- activations: Union[str, BaseActivation, list], use_bias: Union[bool, list],
74
- normalizations: Union[str, list], self_embeddings: Union[bool, list], sample_sizes: Union[int, list],
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 not isinstance(dims, list):
104
+ if isinstance(dims, int):
105
105
  dims = [dims]
106
106
  n_layers = len(dims)
107
107
 
@@ -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
- Enables node shuffling before optimization.
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., 0., 2.],
63
- [4., 1., 0., 2.],
64
- [6., 0., 0., 3.],
65
- [5., 7., 1., 5.]])
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] -= min(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
- Enables node shuffling before optimization.
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., 0., 2.],
192
- [4., 1., 0., 2.],
193
- [6., 0., 0., 3.],
194
- [5., 7., 1., 5.]])
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:`LouvainIteration`
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] -= min(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)