245 lines
8.4 KiB
Python
245 lines
8.4 KiB
Python
|
# -*- coding: utf-8 -*-
|
|||
|
# Copyright (C) 2004-2019 by
|
|||
|
# Aric Hagberg <hagberg@lanl.gov>
|
|||
|
# Dan Schult <dschult@colgate.edu>
|
|||
|
# Pieter Swart <swart@lanl.gov>
|
|||
|
# All rights reserved.
|
|||
|
# BSD license.
|
|||
|
#
|
|||
|
# Authors:
|
|||
|
# Aric Hagberg <aric.hagberg@gmail.com>
|
|||
|
# Pieter Swart <swart@lanl.gov>
|
|||
|
# Sasha Gutfraind <ag362@cornell.edu>
|
|||
|
"""Functions for computing eigenvector centrality."""
|
|||
|
|
|||
|
from math import sqrt
|
|||
|
|
|||
|
import networkx as nx
|
|||
|
from networkx.utils import not_implemented_for
|
|||
|
|
|||
|
__all__ = ['eigenvector_centrality', 'eigenvector_centrality_numpy']
|
|||
|
|
|||
|
|
|||
|
@not_implemented_for('multigraph')
|
|||
|
def eigenvector_centrality(G, max_iter=100, tol=1.0e-6, nstart=None,
|
|||
|
weight=None):
|
|||
|
r"""Compute the eigenvector centrality for the graph `G`.
|
|||
|
|
|||
|
Eigenvector centrality computes the centrality for a node based on the
|
|||
|
centrality of its neighbors. The eigenvector centrality for node $i$ is
|
|||
|
the $i$-th element of the vector $x$ defined by the equation
|
|||
|
|
|||
|
.. math::
|
|||
|
|
|||
|
Ax = \lambda x
|
|||
|
|
|||
|
where $A$ is the adjacency matrix of the graph `G` with eigenvalue
|
|||
|
$\lambda$. By virtue of the Perron–Frobenius theorem, there is a unique
|
|||
|
solution $x$, all of whose entries are positive, if $\lambda$ is the
|
|||
|
largest eigenvalue of the adjacency matrix $A$ ([2]_).
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : graph
|
|||
|
A networkx graph
|
|||
|
|
|||
|
max_iter : integer, optional (default=100)
|
|||
|
Maximum number of iterations in power method.
|
|||
|
|
|||
|
tol : float, optional (default=1.0e-6)
|
|||
|
Error tolerance used to check convergence in power method iteration.
|
|||
|
|
|||
|
nstart : dictionary, optional (default=None)
|
|||
|
Starting value of eigenvector iteration for each node.
|
|||
|
|
|||
|
weight : None or string, optional (default=None)
|
|||
|
If None, all edge weights are considered equal.
|
|||
|
Otherwise holds the name of the edge attribute used as weight.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
nodes : dictionary
|
|||
|
Dictionary of nodes with eigenvector centrality as the value.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G = nx.path_graph(4)
|
|||
|
>>> centrality = nx.eigenvector_centrality(G)
|
|||
|
>>> sorted((v, '{:0.2f}'.format(c)) for v, c in centrality.items())
|
|||
|
[(0, '0.37'), (1, '0.60'), (2, '0.60'), (3, '0.37')]
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NetworkXPointlessConcept
|
|||
|
If the graph `G` is the null graph.
|
|||
|
|
|||
|
NetworkXError
|
|||
|
If each value in `nstart` is zero.
|
|||
|
|
|||
|
PowerIterationFailedConvergence
|
|||
|
If the algorithm fails to converge to the specified tolerance
|
|||
|
within the specified number of iterations of the power iteration
|
|||
|
method.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
eigenvector_centrality_numpy
|
|||
|
pagerank
|
|||
|
hits
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
The measure was introduced by [1]_ and is discussed in [2]_.
|
|||
|
|
|||
|
The power iteration method is used to compute the eigenvector and
|
|||
|
convergence is **not** guaranteed. Our method stops after ``max_iter``
|
|||
|
iterations or when the change in the computed vector between two
|
|||
|
iterations is smaller than an error tolerance of
|
|||
|
``G.number_of_nodes() * tol``. This implementation uses ($A + I$)
|
|||
|
rather than the adjacency matrix $A$ because it shifts the spectrum
|
|||
|
to enable discerning the correct eigenvector even for networks with
|
|||
|
multiple dominant eigenvalues.
|
|||
|
|
|||
|
For directed graphs this is "left" eigenvector centrality which corresponds
|
|||
|
to the in-edges in the graph. For out-edges eigenvector centrality
|
|||
|
first reverse the graph with ``G.reverse()``.
|
|||
|
|
|||
|
References
|
|||
|
----------
|
|||
|
.. [1] Phillip Bonacich.
|
|||
|
"Power and Centrality: A Family of Measures."
|
|||
|
*American Journal of Sociology* 92(5):1170–1182, 1986
|
|||
|
<http://www.leonidzhukov.net/hse/2014/socialnetworks/papers/Bonacich-Centrality.pdf>
|
|||
|
.. [2] Mark E. J. Newman.
|
|||
|
*Networks: An Introduction.*
|
|||
|
Oxford University Press, USA, 2010, pp. 169.
|
|||
|
|
|||
|
"""
|
|||
|
if len(G) == 0:
|
|||
|
raise nx.NetworkXPointlessConcept('cannot compute centrality for the'
|
|||
|
' null graph')
|
|||
|
# If no initial vector is provided, start with the all-ones vector.
|
|||
|
if nstart is None:
|
|||
|
nstart = {v: 1 for v in G}
|
|||
|
if all(v == 0 for v in nstart.values()):
|
|||
|
raise nx.NetworkXError('initial vector cannot have all zero values')
|
|||
|
# Normalize the initial vector so that each entry is in [0, 1]. This is
|
|||
|
# guaranteed to never have a divide-by-zero error by the previous line.
|
|||
|
nstart_sum = sum(nstart.values())
|
|||
|
x = {k: v / nstart_sum for k, v in nstart.items()}
|
|||
|
nnodes = G.number_of_nodes()
|
|||
|
# make up to max_iter iterations
|
|||
|
for i in range(max_iter):
|
|||
|
xlast = x
|
|||
|
x = xlast.copy() # Start with xlast times I to iterate with (A+I)
|
|||
|
# do the multiplication y^T = x^T A (left eigenvector)
|
|||
|
for n in x:
|
|||
|
for nbr in G[n]:
|
|||
|
w = G[n][nbr].get(weight, 1) if weight else 1
|
|||
|
x[nbr] += xlast[n] * w
|
|||
|
# Normalize the vector. The normalization denominator `norm`
|
|||
|
# should never be zero by the Perron--Frobenius
|
|||
|
# theorem. However, in case it is due to numerical error, we
|
|||
|
# assume the norm to be one instead.
|
|||
|
norm = sqrt(sum(z ** 2 for z in x.values())) or 1
|
|||
|
x = {k: v / norm for k, v in x.items()}
|
|||
|
# Check for convergence (in the L_1 norm).
|
|||
|
if sum(abs(x[n] - xlast[n]) for n in x) < nnodes * tol:
|
|||
|
return x
|
|||
|
raise nx.PowerIterationFailedConvergence(max_iter)
|
|||
|
|
|||
|
|
|||
|
def eigenvector_centrality_numpy(G, weight=None, max_iter=50, tol=0):
|
|||
|
r"""Compute the eigenvector centrality for the graph G.
|
|||
|
|
|||
|
Eigenvector centrality computes the centrality for a node based on the
|
|||
|
centrality of its neighbors. The eigenvector centrality for node $i$ is
|
|||
|
|
|||
|
.. math::
|
|||
|
|
|||
|
Ax = \lambda x
|
|||
|
|
|||
|
where $A$ is the adjacency matrix of the graph G with eigenvalue $\lambda$.
|
|||
|
By virtue of the Perron–Frobenius theorem, there is a unique and positive
|
|||
|
solution if $\lambda$ is the largest eigenvalue associated with the
|
|||
|
eigenvector of the adjacency matrix $A$ ([2]_).
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : graph
|
|||
|
A networkx graph
|
|||
|
|
|||
|
weight : None or string, optional (default=None)
|
|||
|
The name of the edge attribute used as weight.
|
|||
|
If None, all edge weights are considered equal.
|
|||
|
|
|||
|
max_iter : integer, optional (default=100)
|
|||
|
Maximum number of iterations in power method.
|
|||
|
|
|||
|
tol : float, optional (default=1.0e-6)
|
|||
|
Relative accuracy for eigenvalues (stopping criterion).
|
|||
|
The default value of 0 implies machine precision.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
nodes : dictionary
|
|||
|
Dictionary of nodes with eigenvector centrality as the value.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G = nx.path_graph(4)
|
|||
|
>>> centrality = nx.eigenvector_centrality_numpy(G)
|
|||
|
>>> print(['{} {:0.2f}'.format(node, centrality[node]) for node in centrality])
|
|||
|
['0 0.37', '1 0.60', '2 0.60', '3 0.37']
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
eigenvector_centrality
|
|||
|
pagerank
|
|||
|
hits
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
The measure was introduced by [1]_.
|
|||
|
|
|||
|
This algorithm uses the SciPy sparse eigenvalue solver (ARPACK) to
|
|||
|
find the largest eigenvalue/eigenvector pair.
|
|||
|
|
|||
|
For directed graphs this is "left" eigenvector centrality which corresponds
|
|||
|
to the in-edges in the graph. For out-edges eigenvector centrality
|
|||
|
first reverse the graph with ``G.reverse()``.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NetworkXPointlessConcept
|
|||
|
If the graph ``G`` is the null graph.
|
|||
|
|
|||
|
References
|
|||
|
----------
|
|||
|
.. [1] Phillip Bonacich:
|
|||
|
Power and Centrality: A Family of Measures.
|
|||
|
American Journal of Sociology 92(5):1170–1182, 1986
|
|||
|
http://www.leonidzhukov.net/hse/2014/socialnetworks/papers/Bonacich-Centrality.pdf
|
|||
|
.. [2] Mark E. J. Newman:
|
|||
|
Networks: An Introduction.
|
|||
|
Oxford University Press, USA, 2010, pp. 169.
|
|||
|
"""
|
|||
|
import scipy as sp
|
|||
|
from scipy.sparse import linalg
|
|||
|
if len(G) == 0:
|
|||
|
raise nx.NetworkXPointlessConcept('cannot compute centrality for the'
|
|||
|
' null graph')
|
|||
|
M = nx.to_scipy_sparse_matrix(G, nodelist=list(G), weight=weight,
|
|||
|
dtype=float)
|
|||
|
eigenvalue, eigenvector = linalg.eigs(M.T, k=1, which='LR',
|
|||
|
maxiter=max_iter, tol=tol)
|
|||
|
largest = eigenvector.flatten().real
|
|||
|
norm = sp.sign(largest.sum()) * sp.linalg.norm(largest)
|
|||
|
return dict(zip(G, largest / norm))
|
|||
|
|
|||
|
|
|||
|
# fixture for pytest
|
|||
|
def setup_module(module):
|
|||
|
import pytest
|
|||
|
scipy = pytest.importorskip('scipy')
|