Source code for flyqma.annotation.spatial.infomap
import numpy as np
try:
import infomap
except ImportError:
raise UserWarning('No infomap package found. In order to use the community-based annotation scheme you must install it via PyPI.')
[docs]class InfoMap:
"""
Object for performing infomap flow-based community detection.
Attributes:
infomap (infomap.Infomap) - infomap object
node_to_module (dict) - {node: module} pairs
classifier (vectorized func) - maps nodes to modules
aggregator (CommunityAggregator)
"""
def __init__(self, edges, **kwargs):
"""
Instantiate infomap community detection. Two-level community detection is used by default.
Args:
edges (list) - (i, j, weight) tuple for each edge
kwargs: keyword arguments for build_network method, including:
twolevel (bool) - if True, perform two-level clustering
N (int) - number of trials
"""
self.infomap = self.build_network(edges, **kwargs)
self.run()
node_to_module, classifier = self.build_classifier()
self.node_to_module = node_to_module
self.classifier = classifier
self.aggregator = CommunityAggregator(self.infomap)
def __call__(self, x, level=None):
""" Returns predicted class labels for values. """
return self.aggregator(self.classifier(x), level)
@property
def max_depth(self):
""" Maximum tree depth. """
return self.infomap.maxTreeDepth()
[docs] @staticmethod
def build_network(edges, twolevel=False, N=25):
"""
Compile InfoMap object from graph edges.
Args:
twolevel (bool) - if True, perform two-level clustering
N (int) - number of trials
"""
# define arguments
args = '--undirected --silent -N {:d}'.format(N)
if twolevel:
args = '--two-level ' + args
# instantiate infomap
infomap_obj = infomap.Infomap(args)
network = infomap_obj.network()
# add edges
_ = [network.addLink(*e) for e in edges]
return infomap_obj
[docs] def run(self, report=False):
"""
Run infomap community detection.
Args:
report (bool) - if True, print number of modules found
"""
self.infomap.run()
if report:
print("Found {:d} modules.".format(self.infomap.numTopModules()))
[docs] def build_classifier(self):
"""
Construct node to module classifier.
Returns:
node_to_module (dict) - {node: module} pairs
classifier (vectorized func) - maps nodes to modules
"""
node_to_module = {}
for node in self.infomap.iterLeafNodes():
node_to_module[node.physicalId] = node.moduleIndex()
return node_to_module, np.vectorize(node_to_module.get)
# alternate more efficient method:
# multilevel = imap.infomap.getMultilevelModules()
# unique_paths = set([p[:depth] for p in multilevel.values()])
# path_to_community = {path[:depth]: i for i, path in dict(enumerate(unique_paths)).items()}
# node_to_community = {node: path_to_community[path[:depth]] for node, path in multilevel.asdict().items()}