Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/networkx/algorithms/centrality/trophic.py: 25%
48 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-20 07:00 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-20 07:00 +0000
1"""Trophic levels"""
2import networkx as nx
3from networkx.utils import not_implemented_for
5__all__ = ["trophic_levels", "trophic_differences", "trophic_incoherence_parameter"]
8@not_implemented_for("undirected")
9@nx._dispatch(edge_attrs="weight")
10def trophic_levels(G, weight="weight"):
11 r"""Compute the trophic levels of nodes.
13 The trophic level of a node $i$ is
15 .. math::
17 s_i = 1 + \frac{1}{k^{in}_i} \sum_{j} a_{ij} s_j
19 where $k^{in}_i$ is the in-degree of i
21 .. math::
23 k^{in}_i = \sum_{j} a_{ij}
25 and nodes with $k^{in}_i = 0$ have $s_i = 1$ by convention.
27 These are calculated using the method outlined in Levine [1]_.
29 Parameters
30 ----------
31 G : DiGraph
32 A directed networkx graph
34 Returns
35 -------
36 nodes : dict
37 Dictionary of nodes with trophic level as the value.
39 References
40 ----------
41 .. [1] Stephen Levine (1980) J. theor. Biol. 83, 195-207
42 """
43 import numpy as np
45 # find adjacency matrix
46 a = nx.adjacency_matrix(G, weight=weight).T.toarray()
48 # drop rows/columns where in-degree is zero
49 rowsum = np.sum(a, axis=1)
50 p = a[rowsum != 0][:, rowsum != 0]
51 # normalise so sum of in-degree weights is 1 along each row
52 p = p / rowsum[rowsum != 0][:, np.newaxis]
54 # calculate trophic levels
55 nn = p.shape[0]
56 i = np.eye(nn)
57 try:
58 n = np.linalg.inv(i - p)
59 except np.linalg.LinAlgError as err:
60 # LinAlgError is raised when there is a non-basal node
61 msg = (
62 "Trophic levels are only defined for graphs where every "
63 + "node has a path from a basal node (basal nodes are nodes "
64 + "with no incoming edges)."
65 )
66 raise nx.NetworkXError(msg) from err
67 y = n.sum(axis=1) + 1
69 levels = {}
71 # all nodes with in-degree zero have trophic level == 1
72 zero_node_ids = (node_id for node_id, degree in G.in_degree if degree == 0)
73 for node_id in zero_node_ids:
74 levels[node_id] = 1
76 # all other nodes have levels as calculated
77 nonzero_node_ids = (node_id for node_id, degree in G.in_degree if degree != 0)
78 for i, node_id in enumerate(nonzero_node_ids):
79 levels[node_id] = y[i]
81 return levels
84@not_implemented_for("undirected")
85@nx._dispatch(edge_attrs="weight")
86def trophic_differences(G, weight="weight"):
87 r"""Compute the trophic differences of the edges of a directed graph.
89 The trophic difference $x_ij$ for each edge is defined in Johnson et al.
90 [1]_ as:
92 .. math::
93 x_ij = s_j - s_i
95 Where $s_i$ is the trophic level of node $i$.
97 Parameters
98 ----------
99 G : DiGraph
100 A directed networkx graph
102 Returns
103 -------
104 diffs : dict
105 Dictionary of edges with trophic differences as the value.
107 References
108 ----------
109 .. [1] Samuel Johnson, Virginia Dominguez-Garcia, Luca Donetti, Miguel A.
110 Munoz (2014) PNAS "Trophic coherence determines food-web stability"
111 """
112 levels = trophic_levels(G, weight=weight)
113 diffs = {}
114 for u, v in G.edges:
115 diffs[(u, v)] = levels[v] - levels[u]
116 return diffs
119@not_implemented_for("undirected")
120@nx._dispatch(edge_attrs="weight")
121def trophic_incoherence_parameter(G, weight="weight", cannibalism=False):
122 r"""Compute the trophic incoherence parameter of a graph.
124 Trophic coherence is defined as the homogeneity of the distribution of
125 trophic distances: the more similar, the more coherent. This is measured by
126 the standard deviation of the trophic differences and referred to as the
127 trophic incoherence parameter $q$ by [1].
129 Parameters
130 ----------
131 G : DiGraph
132 A directed networkx graph
134 cannibalism: Boolean
135 If set to False, self edges are not considered in the calculation
137 Returns
138 -------
139 trophic_incoherence_parameter : float
140 The trophic coherence of a graph
142 References
143 ----------
144 .. [1] Samuel Johnson, Virginia Dominguez-Garcia, Luca Donetti, Miguel A.
145 Munoz (2014) PNAS "Trophic coherence determines food-web stability"
146 """
147 import numpy as np
149 if cannibalism:
150 diffs = trophic_differences(G, weight=weight)
151 else:
152 # If no cannibalism, remove self-edges
153 self_loops = list(nx.selfloop_edges(G))
154 if self_loops:
155 # Make a copy so we do not change G's edges in memory
156 G_2 = G.copy()
157 G_2.remove_edges_from(self_loops)
158 else:
159 # Avoid copy otherwise
160 G_2 = G
161 diffs = trophic_differences(G_2, weight=weight)
162 return np.std(list(diffs.values()))