Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/networkx/algorithms/assortativity/neighbor_degree.py: 10%
39 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
1import networkx as nx
3__all__ = ["average_neighbor_degree"]
6@nx._dispatch(edge_attrs="weight")
7def average_neighbor_degree(G, source="out", target="out", nodes=None, weight=None):
8 r"""Returns the average degree of the neighborhood of each node.
10 In an undirected graph, the neighborhood `N(i)` of node `i` contains the
11 nodes that are connected to `i` by an edge.
13 For directed graphs, `N(i)` is defined according to the parameter `source`:
15 - if source is 'in', then `N(i)` consists of predecessors of node `i`.
16 - if source is 'out', then `N(i)` consists of successors of node `i`.
17 - if source is 'in+out', then `N(i)` is both predecessors and successors.
19 The average neighborhood degree of a node `i` is
21 .. math::
23 k_{nn,i} = \frac{1}{|N(i)|} \sum_{j \in N(i)} k_j
25 where `N(i)` are the neighbors of node `i` and `k_j` is
26 the degree of node `j` which belongs to `N(i)`. For weighted
27 graphs, an analogous measure can be defined [1]_,
29 .. math::
31 k_{nn,i}^{w} = \frac{1}{s_i} \sum_{j \in N(i)} w_{ij} k_j
33 where `s_i` is the weighted degree of node `i`, `w_{ij}`
34 is the weight of the edge that links `i` and `j` and
35 `N(i)` are the neighbors of node `i`.
38 Parameters
39 ----------
40 G : NetworkX graph
42 source : string ("in"|"out"|"in+out"), optional (default="out")
43 Directed graphs only.
44 Use "in"- or "out"-neighbors of source node.
46 target : string ("in"|"out"|"in+out"), optional (default="out")
47 Directed graphs only.
48 Use "in"- or "out"-degree for target node.
50 nodes : list or iterable, optional (default=G.nodes)
51 Compute neighbor degree only for specified nodes.
53 weight : string or None, optional (default=None)
54 The edge attribute that holds the numerical value used as a weight.
55 If None, then each edge has weight 1.
57 Returns
58 -------
59 d: dict
60 A dictionary keyed by node to the average degree of its neighbors.
62 Raises
63 ------
64 NetworkXError
65 If either `source` or `target` are not one of 'in', 'out', or 'in+out'.
66 If either `source` or `target` is passed for an undirected graph.
68 Examples
69 --------
70 >>> G = nx.path_graph(4)
71 >>> G.edges[0, 1]["weight"] = 5
72 >>> G.edges[2, 3]["weight"] = 3
74 >>> nx.average_neighbor_degree(G)
75 {0: 2.0, 1: 1.5, 2: 1.5, 3: 2.0}
76 >>> nx.average_neighbor_degree(G, weight="weight")
77 {0: 2.0, 1: 1.1666666666666667, 2: 1.25, 3: 2.0}
79 >>> G = nx.DiGraph()
80 >>> nx.add_path(G, [0, 1, 2, 3])
81 >>> nx.average_neighbor_degree(G, source="in", target="in")
82 {0: 0.0, 1: 0.0, 2: 1.0, 3: 1.0}
84 >>> nx.average_neighbor_degree(G, source="out", target="out")
85 {0: 1.0, 1: 1.0, 2: 0.0, 3: 0.0}
87 See Also
88 --------
89 average_degree_connectivity
91 References
92 ----------
93 .. [1] A. Barrat, M. Barthélemy, R. Pastor-Satorras, and A. Vespignani,
94 "The architecture of complex weighted networks".
95 PNAS 101 (11): 3747–3752 (2004).
96 """
97 if G.is_directed():
98 if source == "in":
99 source_degree = G.in_degree
100 elif source == "out":
101 source_degree = G.out_degree
102 elif source == "in+out":
103 source_degree = G.degree
104 else:
105 raise nx.NetworkXError(
106 f"source argument {source} must be 'in', 'out' or 'in+out'"
107 )
109 if target == "in":
110 target_degree = G.in_degree
111 elif target == "out":
112 target_degree = G.out_degree
113 elif target == "in+out":
114 target_degree = G.degree
115 else:
116 raise nx.NetworkXError(
117 f"target argument {target} must be 'in', 'out' or 'in+out'"
118 )
119 else:
120 if source != "out" or target != "out":
121 raise nx.NetworkXError(
122 f"source and target arguments are only supported for directed graphs"
123 )
124 source_degree = target_degree = G.degree
126 # precompute target degrees -- should *not* be weighted degree
127 t_deg = dict(target_degree())
129 # Set up both predecessor and successor neighbor dicts leaving empty if not needed
130 G_P = G_S = {n: {} for n in G}
131 if G.is_directed():
132 # "in" or "in+out" cases: G_P contains predecessors
133 if "in" in source:
134 G_P = G.pred
135 # "out" or "in+out" cases: G_S contains successors
136 if "out" in source:
137 G_S = G.succ
138 else:
139 # undirected leave G_P empty but G_S is the adjacency
140 G_S = G.adj
142 # Main loop: Compute average degree of neighbors
143 avg = {}
144 for n, deg in source_degree(nodes, weight=weight):
145 # handle degree zero average
146 if deg == 0:
147 avg[n] = 0.0
148 continue
150 # we sum over both G_P and G_S, but one of the two is usually empty.
151 if weight is None:
152 avg[n] = (
153 sum(t_deg[nbr] for nbr in G_S[n]) + sum(t_deg[nbr] for nbr in G_P[n])
154 ) / deg
155 else:
156 avg[n] = (
157 sum(dd.get(weight, 1) * t_deg[nbr] for nbr, dd in G_S[n].items())
158 + sum(dd.get(weight, 1) * t_deg[nbr] for nbr, dd in G_P[n].items())
159 ) / deg
160 return avg