Skip to content

Commit 08ff43a

Browse files
authored
Merge pull request #25 from KrishnaswamyLab/dev
Bump to graphtools v0.1.10
2 parents beca21d + a010211 commit 08ff43a

File tree

18 files changed

+1088
-283
lines changed

18 files changed

+1088
-283
lines changed

LICENSE

Lines changed: 674 additions & 0 deletions
Large diffs are not rendered by default.

doc/source/conf.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
'sphinx.ext.doctest',
3838
'sphinx.ext.coverage',
3939
'sphinx.ext.mathjax',
40-
'sphinx.ext.viewcode']
40+
'sphinx.ext.viewcode',
41+
'sphinxcontrib.bibtex']
4142

4243
# Add any paths that contain templates here, relative to this directory.
4344
templates_path = ['ytemplates']

doc/source/reference.rst

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,3 @@ Utilities
3636
:undoc-members:
3737
:inherited-members:
3838
:show-inheritance:
39-
40-
Logging
41-
-------
42-
43-
.. automodule:: graphtools.logging
44-
:members:
45-
:undoc-members:
46-
:inherited-members:
47-
:show-inheritance:

doc/source/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ scikit-learn>=0.19.1
55
future
66
sphinx
77
sphinxcontrib-napoleon
8+
sphinxcontrib-bibtex

graphtools/api.py

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import numpy as np
22
import warnings
3+
import tasklogger
34

4-
from .logging import (set_logging,
5-
log_debug)
6-
from .base import PyGSPGraph
7-
from .graphs import kNNGraph, TraditionalGraph, MNNGraph, LandmarkGraph
5+
from . import base
6+
from . import graphs
87

98

109
def Graph(data,
@@ -138,7 +137,7 @@ def Graph(data,
138137
------
139138
ValueError : if selected parameters are incompatible.
140139
"""
141-
set_logging(verbose)
140+
tasklogger.set_level(verbose)
142141
if sample_idx is not None and len(np.unique(sample_idx)) == 1:
143142
warnings.warn("Only one unique sample. "
144143
"Not using MNNGraph")
@@ -159,7 +158,7 @@ def Graph(data,
159158

160159
# set base graph type
161160
if graphtype == "knn":
162-
base = kNNGraph
161+
basegraph = graphs.kNNGraph
163162
if precomputed is not None:
164163
raise ValueError("kNNGraph does not support precomputed "
165164
"values. Use `graphtype='exact'` or "
@@ -170,13 +169,13 @@ def Graph(data,
170169
"`sample_idx=None`")
171170

172171
elif graphtype == "mnn":
173-
base = MNNGraph
172+
basegraph = graphs.MNNGraph
174173
if precomputed is not None:
175174
raise ValueError("MNNGraph does not support precomputed "
176175
"values. Use `graphtype='exact'` and "
177176
"`sample_idx=None` or `precomputed=None`")
178177
elif graphtype == "exact":
179-
base = TraditionalGraph
178+
basegraph = graphs.TraditionalGraph
180179
if sample_idx is not None:
181180
raise ValueError("TraditionalGraph does not support batch "
182181
"correction. Use `graphtype='mnn'` or "
@@ -186,32 +185,24 @@ def Graph(data,
186185
"['knn', 'mnn', 'exact', 'auto']")
187186

188187
# set add landmarks if necessary
189-
parent_classes = [base]
188+
parent_classes = [basegraph]
190189
msg = "Building {} graph".format(graphtype)
191190
if n_landmark is not None:
192-
parent_classes.append(LandmarkGraph)
191+
parent_classes.append(graphs.LandmarkGraph)
193192
msg = msg + " with landmarks"
194193
if use_pygsp:
195-
parent_classes.append(PyGSPGraph)
194+
parent_classes.append(base.PyGSPGraph)
196195
if len(parent_classes) > 2:
197196
msg = msg + " with PyGSP inheritance"
198197
else:
199198
msg = msg + " and PyGSP inheritance"
200199

201-
log_debug(msg)
202-
203-
# Python3 syntax only
204-
# class Graph(*parent_classes):
205-
# pass
206-
if len(parent_classes) == 1:
207-
Graph = parent_classes[0]
208-
elif len(parent_classes) == 2:
209-
class Graph(parent_classes[0], parent_classes[1]):
210-
pass
211-
elif len(parent_classes) == 3:
212-
class Graph(parent_classes[0], parent_classes[1], parent_classes[2]):
213-
pass
214-
else:
200+
tasklogger.log_debug(msg)
201+
202+
class_names = [p.__name__.replace("Graph", "") for p in parent_classes]
203+
try:
204+
Graph = eval("graphs." + "".join(class_names) + "Graph")
205+
except NameError:
215206
raise RuntimeError("unknown graph classes {}".format(parent_classes))
216207

217208
params = kwargs
@@ -224,7 +215,7 @@ class Graph(parent_classes[0], parent_classes[1], parent_classes[2]):
224215
pass
225216

226217
# build graph and return
227-
log_debug("Initializing {} with arguments {}".format(
218+
tasklogger.log_debug("Initializing {} with arguments {}".format(
228219
parent_classes,
229220
", ".join(["{}='{}'".format(key, value)
230221
for key, value in params.items()

graphtools/base.py

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
from scipy import sparse
1010
import warnings
1111
import numbers
12+
import tasklogger
13+
1214
try:
1315
import pandas as pd
1416
except ImportError:
@@ -24,10 +26,6 @@
2426
from .utils import (elementwise_minimum,
2527
elementwise_maximum,
2628
set_diagonal)
27-
from .logging import (set_logging,
28-
log_start,
29-
log_complete,
30-
log_debug)
3129

3230

3331
class Base(object):
@@ -67,6 +65,9 @@ def _get_param_names(cls):
6765

6866
return parameters
6967

68+
def set_params(self, **kwargs):
69+
return self
70+
7071

7172
class Data(Base):
7273
"""Parent class that handles the import and dimensionality reduction of data
@@ -152,7 +153,7 @@ def _reduce_data(self):
152153
Reduced data matrix
153154
"""
154155
if self.n_pca is not None and self.n_pca < self.data.shape[1]:
155-
log_start("PCA")
156+
tasklogger.log_start("PCA")
156157
if sparse.issparse(self.data):
157158
if isinstance(self.data, sparse.coo_matrix) or \
158159
isinstance(self.data, sparse.lil_matrix) or \
@@ -166,7 +167,7 @@ def _reduce_data(self):
166167
random_state=self.random_state)
167168
self.data_pca.fit(self.data)
168169
data_nu = self.data_pca.transform(self.data)
169-
log_complete("PCA")
170+
tasklogger.log_complete("PCA")
170171
return data_nu
171172
else:
172173
data_nu = self.data
@@ -204,6 +205,7 @@ def set_params(self, **params):
204205
raise ValueError("Cannot update n_pca. Please create a new graph")
205206
if 'random_state' in params:
206207
self.random_state = params['random_state']
208+
super().set_params(**params)
207209
return self
208210

209211
def transform(self, Y):
@@ -342,10 +344,10 @@ def __init__(self, kernel_symm='+',
342344
self._check_symmetrization(kernel_symm, gamma)
343345

344346
if initialize:
345-
log_debug("Initializing kernel...")
347+
tasklogger.log_debug("Initializing kernel...")
346348
self.K
347349
else:
348-
log_debug("Not initializing kernel.")
350+
tasklogger.log_debug("Not initializing kernel.")
349351
super().__init__(**kwargs)
350352

351353
def _check_symmetrization(self, kernel_symm, gamma):
@@ -363,7 +365,8 @@ def _check_symmetrization(self, kernel_symm, gamma):
363365
warnings.warn("kernel_symm='gamma' but gamma not given. "
364366
"Defaulting to gamma=0.5.")
365367
self.gamma = gamma = 0.5
366-
elif not isinstance(gamma, numbers.Number) or gamma < 0 or gamma > 1:
368+
elif not isinstance(gamma, numbers.Number) or \
369+
gamma < 0 or gamma > 1:
367370
raise ValueError("gamma {} not recognized. Expected "
368371
"a float between 0 and 1".format(gamma))
369372

@@ -392,18 +395,18 @@ def _build_kernel(self):
392395
def symmetrize_kernel(self, K):
393396
# symmetrize
394397
if self.kernel_symm == "+":
395-
log_debug("Using addition symmetrization.")
398+
tasklogger.log_debug("Using addition symmetrization.")
396399
K = (K + K.T) / 2
397400
elif self.kernel_symm == "*":
398-
log_debug("Using multiplication symmetrization.")
401+
tasklogger.log_debug("Using multiplication symmetrization.")
399402
K = K.multiply(K.T)
400403
elif self.kernel_symm == 'gamma':
401-
log_debug(
404+
tasklogger.log_debug(
402405
"Using gamma symmetrization (gamma = {}).".format(self.gamma))
403406
K = self.gamma * elementwise_minimum(K, K.T) + \
404407
(1 - self.gamma) * elementwise_maximum(K, K.T)
405408
elif self.kernel_symm is None:
406-
log_debug("Using no symmetrization.")
409+
tasklogger.log_debug("Using no symmetrization.")
407410
pass
408411
else:
409412
# this should never happen
@@ -438,9 +441,11 @@ def set_params(self, **params):
438441
"""
439442
if 'gamma' in params and params['gamma'] != self.gamma:
440443
raise ValueError("Cannot update gamma. Please create a new graph")
441-
if 'kernel_symm' in params and params['kernel_symm'] != self.kernel_symm:
444+
if 'kernel_symm' in params and \
445+
params['kernel_symm'] != self.kernel_symm:
442446
raise ValueError(
443447
"Cannot update kernel_symm. Please create a new graph")
448+
super().set_params(**params)
444449
return self
445450

446451
@property
@@ -462,6 +467,31 @@ def P(self):
462467
self._diff_op = normalize(self.kernel, 'l1', axis=1)
463468
return self._diff_op
464469

470+
@property
471+
def diff_aff(self):
472+
"""Symmetric diffusion affinity matrix
473+
474+
Return or calculate the symmetric diffusion affinity matrix
475+
476+
.. math:: A(x,y) = K(x,y) (d(x) d(y))^{-1/2}
477+
478+
where :math:`d` is the degrees (row sums of the kernel.)
479+
480+
Returns
481+
-------
482+
483+
diff_aff : array-like, shape=[n_samples, n_samples]
484+
symmetric diffusion affinity matrix defined as a
485+
doubly-stochastic form of the kernel matrix
486+
"""
487+
row_degrees = np.array(self.kernel.sum(axis=1)).reshape(-1, 1)
488+
col_degrees = np.array(self.kernel.sum(axis=0)).reshape(1, -1)
489+
if sparse.issparse(self.kernel):
490+
return self.kernel.multiply(1 / np.sqrt(row_degrees)).multiply(
491+
1 / np.sqrt(col_degrees))
492+
else:
493+
return (self.kernel / np.sqrt(row_degrees)) / np.sqrt(col_degrees)
494+
465495
@property
466496
def diff_op(self):
467497
"""Synonym for P
@@ -597,7 +627,7 @@ def __init__(self, data,
597627
# kwargs are ignored
598628
self.n_jobs = n_jobs
599629
self.verbose = verbose
600-
set_logging(verbose)
630+
tasklogger.set_level(verbose)
601631
super().__init__(data, **kwargs)
602632

603633
def get_params(self):

0 commit comments

Comments
 (0)