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

1"""Trophic levels""" 

2import networkx as nx 

3from networkx.utils import not_implemented_for 

4 

5__all__ = ["trophic_levels", "trophic_differences", "trophic_incoherence_parameter"] 

6 

7 

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. 

12 

13 The trophic level of a node $i$ is 

14 

15 .. math:: 

16 

17 s_i = 1 + \frac{1}{k^{in}_i} \sum_{j} a_{ij} s_j 

18 

19 where $k^{in}_i$ is the in-degree of i 

20 

21 .. math:: 

22 

23 k^{in}_i = \sum_{j} a_{ij} 

24 

25 and nodes with $k^{in}_i = 0$ have $s_i = 1$ by convention. 

26 

27 These are calculated using the method outlined in Levine [1]_. 

28 

29 Parameters 

30 ---------- 

31 G : DiGraph 

32 A directed networkx graph 

33 

34 Returns 

35 ------- 

36 nodes : dict 

37 Dictionary of nodes with trophic level as the value. 

38 

39 References 

40 ---------- 

41 .. [1] Stephen Levine (1980) J. theor. Biol. 83, 195-207 

42 """ 

43 import numpy as np 

44 

45 # find adjacency matrix 

46 a = nx.adjacency_matrix(G, weight=weight).T.toarray() 

47 

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] 

53 

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 

68 

69 levels = {} 

70 

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 

75 

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] 

80 

81 return levels 

82 

83 

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. 

88 

89 The trophic difference $x_ij$ for each edge is defined in Johnson et al. 

90 [1]_ as: 

91 

92 .. math:: 

93 x_ij = s_j - s_i 

94 

95 Where $s_i$ is the trophic level of node $i$. 

96 

97 Parameters 

98 ---------- 

99 G : DiGraph 

100 A directed networkx graph 

101 

102 Returns 

103 ------- 

104 diffs : dict 

105 Dictionary of edges with trophic differences as the value. 

106 

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 

117 

118 

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. 

123 

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]. 

128 

129 Parameters 

130 ---------- 

131 G : DiGraph 

132 A directed networkx graph 

133 

134 cannibalism: Boolean 

135 If set to False, self edges are not considered in the calculation 

136 

137 Returns 

138 ------- 

139 trophic_incoherence_parameter : float 

140 The trophic coherence of a graph 

141 

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 

148 

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()))