Skip to content

Commit

Permalink
Merge pull request #25 from KrishnaswamyLab/dev
Browse files Browse the repository at this point in the history
Bump to graphtools v0.1.10
  • Loading branch information
scottgigante authored Jul 31, 2018
2 parents beca21d + a010211 commit 08ff43a
Show file tree
Hide file tree
Showing 18 changed files with 1,088 additions and 283 deletions.
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
'sphinx.ext.doctest',
'sphinx.ext.coverage',
'sphinx.ext.mathjax',
'sphinx.ext.viewcode']
'sphinx.ext.viewcode',
'sphinxcontrib.bibtex']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['ytemplates']
Expand Down
9 changes: 0 additions & 9 deletions doc/source/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,3 @@ Utilities
:undoc-members:
:inherited-members:
:show-inheritance:

Logging
-------

.. automodule:: graphtools.logging
:members:
:undoc-members:
:inherited-members:
:show-inheritance:
1 change: 1 addition & 0 deletions doc/source/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ scikit-learn>=0.19.1
future
sphinx
sphinxcontrib-napoleon
sphinxcontrib-bibtex
43 changes: 17 additions & 26 deletions graphtools/api.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import numpy as np
import warnings
import tasklogger

from .logging import (set_logging,
log_debug)
from .base import PyGSPGraph
from .graphs import kNNGraph, TraditionalGraph, MNNGraph, LandmarkGraph
from . import base
from . import graphs


def Graph(data,
Expand Down Expand Up @@ -138,7 +137,7 @@ def Graph(data,
------
ValueError : if selected parameters are incompatible.
"""
set_logging(verbose)
tasklogger.set_level(verbose)
if sample_idx is not None and len(np.unique(sample_idx)) == 1:
warnings.warn("Only one unique sample. "
"Not using MNNGraph")
Expand All @@ -159,7 +158,7 @@ def Graph(data,

# set base graph type
if graphtype == "knn":
base = kNNGraph
basegraph = graphs.kNNGraph
if precomputed is not None:
raise ValueError("kNNGraph does not support precomputed "
"values. Use `graphtype='exact'` or "
Expand All @@ -170,13 +169,13 @@ def Graph(data,
"`sample_idx=None`")

elif graphtype == "mnn":
base = MNNGraph
basegraph = graphs.MNNGraph
if precomputed is not None:
raise ValueError("MNNGraph does not support precomputed "
"values. Use `graphtype='exact'` and "
"`sample_idx=None` or `precomputed=None`")
elif graphtype == "exact":
base = TraditionalGraph
basegraph = graphs.TraditionalGraph
if sample_idx is not None:
raise ValueError("TraditionalGraph does not support batch "
"correction. Use `graphtype='mnn'` or "
Expand All @@ -186,32 +185,24 @@ def Graph(data,
"['knn', 'mnn', 'exact', 'auto']")

# set add landmarks if necessary
parent_classes = [base]
parent_classes = [basegraph]
msg = "Building {} graph".format(graphtype)
if n_landmark is not None:
parent_classes.append(LandmarkGraph)
parent_classes.append(graphs.LandmarkGraph)
msg = msg + " with landmarks"
if use_pygsp:
parent_classes.append(PyGSPGraph)
parent_classes.append(base.PyGSPGraph)
if len(parent_classes) > 2:
msg = msg + " with PyGSP inheritance"
else:
msg = msg + " and PyGSP inheritance"

log_debug(msg)

# Python3 syntax only
# class Graph(*parent_classes):
# pass
if len(parent_classes) == 1:
Graph = parent_classes[0]
elif len(parent_classes) == 2:
class Graph(parent_classes[0], parent_classes[1]):
pass
elif len(parent_classes) == 3:
class Graph(parent_classes[0], parent_classes[1], parent_classes[2]):
pass
else:
tasklogger.log_debug(msg)

class_names = [p.__name__.replace("Graph", "") for p in parent_classes]
try:
Graph = eval("graphs." + "".join(class_names) + "Graph")
except NameError:
raise RuntimeError("unknown graph classes {}".format(parent_classes))

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

# build graph and return
log_debug("Initializing {} with arguments {}".format(
tasklogger.log_debug("Initializing {} with arguments {}".format(
parent_classes,
", ".join(["{}='{}'".format(key, value)
for key, value in params.items()
Expand Down
60 changes: 45 additions & 15 deletions graphtools/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from scipy import sparse
import warnings
import numbers
import tasklogger

try:
import pandas as pd
except ImportError:
Expand All @@ -24,10 +26,6 @@
from .utils import (elementwise_minimum,
elementwise_maximum,
set_diagonal)
from .logging import (set_logging,
log_start,
log_complete,
log_debug)


class Base(object):
Expand Down Expand Up @@ -67,6 +65,9 @@ def _get_param_names(cls):

return parameters

def set_params(self, **kwargs):
return self


class Data(Base):
"""Parent class that handles the import and dimensionality reduction of data
Expand Down Expand Up @@ -152,7 +153,7 @@ def _reduce_data(self):
Reduced data matrix
"""
if self.n_pca is not None and self.n_pca < self.data.shape[1]:
log_start("PCA")
tasklogger.log_start("PCA")
if sparse.issparse(self.data):
if isinstance(self.data, sparse.coo_matrix) or \
isinstance(self.data, sparse.lil_matrix) or \
Expand All @@ -166,7 +167,7 @@ def _reduce_data(self):
random_state=self.random_state)
self.data_pca.fit(self.data)
data_nu = self.data_pca.transform(self.data)
log_complete("PCA")
tasklogger.log_complete("PCA")
return data_nu
else:
data_nu = self.data
Expand Down Expand Up @@ -204,6 +205,7 @@ def set_params(self, **params):
raise ValueError("Cannot update n_pca. Please create a new graph")
if 'random_state' in params:
self.random_state = params['random_state']
super().set_params(**params)
return self

def transform(self, Y):
Expand Down Expand Up @@ -342,10 +344,10 @@ def __init__(self, kernel_symm='+',
self._check_symmetrization(kernel_symm, gamma)

if initialize:
log_debug("Initializing kernel...")
tasklogger.log_debug("Initializing kernel...")
self.K
else:
log_debug("Not initializing kernel.")
tasklogger.log_debug("Not initializing kernel.")
super().__init__(**kwargs)

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

Expand Down Expand Up @@ -392,18 +395,18 @@ def _build_kernel(self):
def symmetrize_kernel(self, K):
# symmetrize
if self.kernel_symm == "+":
log_debug("Using addition symmetrization.")
tasklogger.log_debug("Using addition symmetrization.")
K = (K + K.T) / 2
elif self.kernel_symm == "*":
log_debug("Using multiplication symmetrization.")
tasklogger.log_debug("Using multiplication symmetrization.")
K = K.multiply(K.T)
elif self.kernel_symm == 'gamma':
log_debug(
tasklogger.log_debug(
"Using gamma symmetrization (gamma = {}).".format(self.gamma))
K = self.gamma * elementwise_minimum(K, K.T) + \
(1 - self.gamma) * elementwise_maximum(K, K.T)
elif self.kernel_symm is None:
log_debug("Using no symmetrization.")
tasklogger.log_debug("Using no symmetrization.")
pass
else:
# this should never happen
Expand Down Expand Up @@ -438,9 +441,11 @@ def set_params(self, **params):
"""
if 'gamma' in params and params['gamma'] != self.gamma:
raise ValueError("Cannot update gamma. Please create a new graph")
if 'kernel_symm' in params and params['kernel_symm'] != self.kernel_symm:
if 'kernel_symm' in params and \
params['kernel_symm'] != self.kernel_symm:
raise ValueError(
"Cannot update kernel_symm. Please create a new graph")
super().set_params(**params)
return self

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

@property
def diff_aff(self):
"""Symmetric diffusion affinity matrix
Return or calculate the symmetric diffusion affinity matrix
.. math:: A(x,y) = K(x,y) (d(x) d(y))^{-1/2}
where :math:`d` is the degrees (row sums of the kernel.)
Returns
-------
diff_aff : array-like, shape=[n_samples, n_samples]
symmetric diffusion affinity matrix defined as a
doubly-stochastic form of the kernel matrix
"""
row_degrees = np.array(self.kernel.sum(axis=1)).reshape(-1, 1)
col_degrees = np.array(self.kernel.sum(axis=0)).reshape(1, -1)
if sparse.issparse(self.kernel):
return self.kernel.multiply(1 / np.sqrt(row_degrees)).multiply(
1 / np.sqrt(col_degrees))
else:
return (self.kernel / np.sqrt(row_degrees)) / np.sqrt(col_degrees)

@property
def diff_op(self):
"""Synonym for P
Expand Down Expand Up @@ -597,7 +627,7 @@ def __init__(self, data,
# kwargs are ignored
self.n_jobs = n_jobs
self.verbose = verbose
set_logging(verbose)
tasklogger.set_level(verbose)
super().__init__(data, **kwargs)

def get_params(self):
Expand Down
Loading

0 comments on commit 08ff43a

Please sign in to comment.