Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/networkx/algorithms/structuralholes.py: 21%

56 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-10-20 07:00 +0000

1"""Functions for computing measures of structural holes.""" 

2 

3import networkx as nx 

4 

5__all__ = ["constraint", "local_constraint", "effective_size"] 

6 

7 

8@nx._dispatch(edge_attrs="weight") 

9def mutual_weight(G, u, v, weight=None): 

10 """Returns the sum of the weights of the edge from `u` to `v` and 

11 the edge from `v` to `u` in `G`. 

12 

13 `weight` is the edge data key that represents the edge weight. If 

14 the specified key is `None` or is not in the edge data for an edge, 

15 that edge is assumed to have weight 1. 

16 

17 Pre-conditions: `u` and `v` must both be in `G`. 

18 

19 """ 

20 try: 

21 a_uv = G[u][v].get(weight, 1) 

22 except KeyError: 

23 a_uv = 0 

24 try: 

25 a_vu = G[v][u].get(weight, 1) 

26 except KeyError: 

27 a_vu = 0 

28 return a_uv + a_vu 

29 

30 

31@nx._dispatch(edge_attrs="weight") 

32def normalized_mutual_weight(G, u, v, norm=sum, weight=None): 

33 """Returns normalized mutual weight of the edges from `u` to `v` 

34 with respect to the mutual weights of the neighbors of `u` in `G`. 

35 

36 `norm` specifies how the normalization factor is computed. It must 

37 be a function that takes a single argument and returns a number. 

38 The argument will be an iterable of mutual weights 

39 of pairs ``(u, w)``, where ``w`` ranges over each (in- and 

40 out-)neighbor of ``u``. Commons values for `normalization` are 

41 ``sum`` and ``max``. 

42 

43 `weight` can be ``None`` or a string, if None, all edge weights 

44 are considered equal. Otherwise holds the name of the edge 

45 attribute used as weight. 

46 

47 """ 

48 scale = norm(mutual_weight(G, u, w, weight) for w in set(nx.all_neighbors(G, u))) 

49 return 0 if scale == 0 else mutual_weight(G, u, v, weight) / scale 

50 

51 

52@nx._dispatch(edge_attrs="weight") 

53def effective_size(G, nodes=None, weight=None): 

54 r"""Returns the effective size of all nodes in the graph ``G``. 

55 

56 The *effective size* of a node's ego network is based on the concept 

57 of redundancy. A person's ego network has redundancy to the extent 

58 that her contacts are connected to each other as well. The 

59 nonredundant part of a person's relationships is the effective 

60 size of her ego network [1]_. Formally, the effective size of a 

61 node $u$, denoted $e(u)$, is defined by 

62 

63 .. math:: 

64 

65 e(u) = \sum_{v \in N(u) \setminus \{u\}} 

66 \left(1 - \sum_{w \in N(v)} p_{uw} m_{vw}\right) 

67 

68 where $N(u)$ is the set of neighbors of $u$ and $p_{uw}$ is the 

69 normalized mutual weight of the (directed or undirected) edges 

70 joining $u$ and $v$, for each vertex $u$ and $v$ [1]_. And $m_{vw}$ 

71 is the mutual weight of $v$ and $w$ divided by $v$ highest mutual 

72 weight with any of its neighbors. The *mutual weight* of $u$ and $v$ 

73 is the sum of the weights of edges joining them (edge weights are 

74 assumed to be one if the graph is unweighted). 

75 

76 For the case of unweighted and undirected graphs, Borgatti proposed 

77 a simplified formula to compute effective size [2]_ 

78 

79 .. math:: 

80 

81 e(u) = n - \frac{2t}{n} 

82 

83 where `t` is the number of ties in the ego network (not including 

84 ties to ego) and `n` is the number of nodes (excluding ego). 

85 

86 Parameters 

87 ---------- 

88 G : NetworkX graph 

89 The graph containing ``v``. Directed graphs are treated like 

90 undirected graphs when computing neighbors of ``v``. 

91 

92 nodes : container, optional 

93 Container of nodes in the graph ``G`` to compute the effective size. 

94 If None, the effective size of every node is computed. 

95 

96 weight : None or string, optional 

97 If None, all edge weights are considered equal. 

98 Otherwise holds the name of the edge attribute used as weight. 

99 

100 Returns 

101 ------- 

102 dict 

103 Dictionary with nodes as keys and the effective size of the node as values. 

104 

105 Notes 

106 ----- 

107 Burt also defined the related concept of *efficiency* of a node's ego 

108 network, which is its effective size divided by the degree of that 

109 node [1]_. So you can easily compute efficiency: 

110 

111 >>> G = nx.DiGraph() 

112 >>> G.add_edges_from([(0, 1), (0, 2), (1, 0), (2, 1)]) 

113 >>> esize = nx.effective_size(G) 

114 >>> efficiency = {n: v / G.degree(n) for n, v in esize.items()} 

115 

116 See also 

117 -------- 

118 constraint 

119 

120 References 

121 ---------- 

122 .. [1] Burt, Ronald S. 

123 *Structural Holes: The Social Structure of Competition.* 

124 Cambridge: Harvard University Press, 1995. 

125 

126 .. [2] Borgatti, S. 

127 "Structural Holes: Unpacking Burt's Redundancy Measures" 

128 CONNECTIONS 20(1):35-38. 

129 http://www.analytictech.com/connections/v20(1)/holes.htm 

130 

131 """ 

132 

133 def redundancy(G, u, v, weight=None): 

134 nmw = normalized_mutual_weight 

135 r = sum( 

136 nmw(G, u, w, weight=weight) * nmw(G, v, w, norm=max, weight=weight) 

137 for w in set(nx.all_neighbors(G, u)) 

138 ) 

139 return 1 - r 

140 

141 effective_size = {} 

142 if nodes is None: 

143 nodes = G 

144 # Use Borgatti's simplified formula for unweighted and undirected graphs 

145 if not G.is_directed() and weight is None: 

146 for v in nodes: 

147 # Effective size is not defined for isolated nodes 

148 if len(G[v]) == 0: 

149 effective_size[v] = float("nan") 

150 continue 

151 E = nx.ego_graph(G, v, center=False, undirected=True) 

152 effective_size[v] = len(E) - (2 * E.size()) / len(E) 

153 else: 

154 for v in nodes: 

155 # Effective size is not defined for isolated nodes 

156 if len(G[v]) == 0: 

157 effective_size[v] = float("nan") 

158 continue 

159 effective_size[v] = sum( 

160 redundancy(G, v, u, weight) for u in set(nx.all_neighbors(G, v)) 

161 ) 

162 return effective_size 

163 

164 

165@nx._dispatch(edge_attrs="weight") 

166def constraint(G, nodes=None, weight=None): 

167 r"""Returns the constraint on all nodes in the graph ``G``. 

168 

169 The *constraint* is a measure of the extent to which a node *v* is 

170 invested in those nodes that are themselves invested in the 

171 neighbors of *v*. Formally, the *constraint on v*, denoted `c(v)`, 

172 is defined by 

173 

174 .. math:: 

175 

176 c(v) = \sum_{w \in N(v) \setminus \{v\}} \ell(v, w) 

177 

178 where $N(v)$ is the subset of the neighbors of `v` that are either 

179 predecessors or successors of `v` and $\ell(v, w)$ is the local 

180 constraint on `v` with respect to `w` [1]_. For the definition of local 

181 constraint, see :func:`local_constraint`. 

182 

183 Parameters 

184 ---------- 

185 G : NetworkX graph 

186 The graph containing ``v``. This can be either directed or undirected. 

187 

188 nodes : container, optional 

189 Container of nodes in the graph ``G`` to compute the constraint. If 

190 None, the constraint of every node is computed. 

191 

192 weight : None or string, optional 

193 If None, all edge weights are considered equal. 

194 Otherwise holds the name of the edge attribute used as weight. 

195 

196 Returns 

197 ------- 

198 dict 

199 Dictionary with nodes as keys and the constraint on the node as values. 

200 

201 See also 

202 -------- 

203 local_constraint 

204 

205 References 

206 ---------- 

207 .. [1] Burt, Ronald S. 

208 "Structural holes and good ideas". 

209 American Journal of Sociology (110): 349–399. 

210 

211 """ 

212 if nodes is None: 

213 nodes = G 

214 constraint = {} 

215 for v in nodes: 

216 # Constraint is not defined for isolated nodes 

217 if len(G[v]) == 0: 

218 constraint[v] = float("nan") 

219 continue 

220 constraint[v] = sum( 

221 local_constraint(G, v, n, weight) for n in set(nx.all_neighbors(G, v)) 

222 ) 

223 return constraint 

224 

225 

226@nx._dispatch(edge_attrs="weight") 

227def local_constraint(G, u, v, weight=None): 

228 r"""Returns the local constraint on the node ``u`` with respect to 

229 the node ``v`` in the graph ``G``. 

230 

231 Formally, the *local constraint on u with respect to v*, denoted 

232 $\ell(v)$, is defined by 

233 

234 .. math:: 

235 

236 \ell(u, v) = \left(p_{uv} + \sum_{w \in N(v)} p_{uw} p_{wv}\right)^2, 

237 

238 where $N(v)$ is the set of neighbors of $v$ and $p_{uv}$ is the 

239 normalized mutual weight of the (directed or undirected) edges 

240 joining $u$ and $v$, for each vertex $u$ and $v$ [1]_. The *mutual 

241 weight* of $u$ and $v$ is the sum of the weights of edges joining 

242 them (edge weights are assumed to be one if the graph is 

243 unweighted). 

244 

245 Parameters 

246 ---------- 

247 G : NetworkX graph 

248 The graph containing ``u`` and ``v``. This can be either 

249 directed or undirected. 

250 

251 u : node 

252 A node in the graph ``G``. 

253 

254 v : node 

255 A node in the graph ``G``. 

256 

257 weight : None or string, optional 

258 If None, all edge weights are considered equal. 

259 Otherwise holds the name of the edge attribute used as weight. 

260 

261 Returns 

262 ------- 

263 float 

264 The constraint of the node ``v`` in the graph ``G``. 

265 

266 See also 

267 -------- 

268 constraint 

269 

270 References 

271 ---------- 

272 .. [1] Burt, Ronald S. 

273 "Structural holes and good ideas". 

274 American Journal of Sociology (110): 349–399. 

275 

276 """ 

277 nmw = normalized_mutual_weight 

278 direct = nmw(G, u, v, weight=weight) 

279 indirect = sum( 

280 nmw(G, u, w, weight=weight) * nmw(G, w, v, weight=weight) 

281 for w in set(nx.all_neighbors(G, u)) 

282 ) 

283 return (direct + indirect) ** 2