~ubuntu-branches/ubuntu/utopic/python-networkx/utopic

« back to all changes in this revision

Viewing changes to networkx/algorithms/cluster.py

  • Committer: Package Import Robot
  • Author(s): Sandro Tosi
  • Date: 2012-08-11 12:41:30 UTC
  • mfrom: (1.4.1) (5.1.13 sid)
  • Revision ID: package-import@ubuntu.com-20120811124130-whr6uso7fncyg8bi
Tags: 1.7-1
* New upstream release
* debian/patches/changeset_*
  - removed, included upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"""
2
 
Algorithms to characterize the number of triangles in a graph.
3
 
 
4
 
"""
5
 
__author__ = """Aric Hagberg (hagberg@lanl.gov)\nPieter Swart (swart@lanl.gov)\nDan Schult (dschult@colgate.edu)"""
6
 
#    Copyright (C) 2004-2008 by 
 
1
# -*- coding: utf-8 -*-
 
2
"""Algorithms to characterize the number of triangles in a graph."""
 
3
from itertools import combinations
 
4
import networkx as nx
 
5
from networkx import NetworkXError
 
6
__author__ = """\n""".join(['Aric Hagberg <aric.hagberg@gmail.com>',
 
7
                            'Dan Schult (dschult@colgate.edu)',
 
8
                            'Pieter Swart (swart@lanl.gov)',
 
9
                            'Jordi Torrents <jtorrents@milnou.net>'])
 
10
#    Copyright (C) 2004-2011 by 
7
11
#    Aric Hagberg <hagberg@lanl.gov>
8
12
#    Dan Schult <dschult@colgate.edu>
9
13
#    Pieter Swart <swart@lanl.gov>
10
14
#    All rights reserved.
11
15
#    BSD license.
12
 
 
13
 
__all__= ['triangles', 'average_clustering', 'clustering', 'transitivity']
14
 
 
15
 
import networkx as nx
16
 
from networkx import NetworkXError
17
 
 
18
 
def triangles(G,nbunch=None):
 
16
__all__= ['triangles', 'average_clustering', 'clustering', 'transitivity',
 
17
          'square_clustering']
 
18
 
 
19
def triangles(G, nodes=None):
19
20
    """Compute the number of triangles.
20
21
 
21
 
    Finds the number of triangles that include a node as one of the vertices.
 
22
    Finds the number of triangles that include a node as one vertex.
22
23
 
23
24
    Parameters
24
25
    ----------
25
26
    G : graph
26
27
       A networkx graph
27
 
    nbunch : container of nodes, optional
28
 
       Compute triangles for nodes in nbunch. The default is all nodes in G.
 
28
    nodes : container of nodes, optional (default= all nodes in G)
 
29
       Compute triangles for nodes in this container. 
29
30
 
30
31
    Returns
31
32
    -------
32
33
    out : dictionary
33
 
       Number of trianges keyed by node label.
 
34
       Number of triangles keyed by node label.
34
35
    
35
36
    Examples
36
37
    --------
44
45
 
45
46
    Notes
46
47
    -----
47
 
    When computing triangles for the entire graph 
48
 
    each triangle is counted three times, once at each node.
49
 
 
50
 
    Self loops are ignored.
 
48
    When computing triangles for the entire graph each triangle is counted 
 
49
    three times, once at each node.  Self loops are ignored.
51
50
 
52
51
    """
53
52
    if G.is_directed():
54
53
        raise NetworkXError("triangles() is not defined for directed graphs.")
55
 
    if nbunch in G: 
56
 
        return next(_triangles_and_degree_iter(G,nbunch))[2] // 2 # return single value
57
 
    return dict( (v,t // 2) for v,d,t in _triangles_and_degree_iter(G,nbunch))
 
54
    if nodes in G: 
 
55
        # return single value
 
56
        return next(_triangles_and_degree_iter(G,nodes))[2] // 2
 
57
    return dict( (v,t // 2) for v,d,t in _triangles_and_degree_iter(G,nodes))
58
58
 
59
 
def _triangles_and_degree_iter(G,nbunch=None):
 
59
def _triangles_and_degree_iter(G,nodes=None):
60
60
    """ Return an iterator of (node, degree, triangles).  
61
61
 
62
62
    This double counts triangles so you may want to divide by 2.
66
66
    if G.is_multigraph():
67
67
        raise NetworkXError("Not defined for multigraphs.")
68
68
 
69
 
    if nbunch is None:
70
 
        nodes_nbrs = iter(G.adj.items())
 
69
    if nodes is None:
 
70
        nodes_nbrs = G.adj.items()
71
71
    else:
72
 
        nodes_nbrs= ( (n,G[n]) for n in G.nbunch_iter(nbunch) )
 
72
        nodes_nbrs= ( (n,G[n]) for n in G.nbunch_iter(nodes) )
73
73
 
74
74
    for v,v_nbrs in nodes_nbrs:
75
 
        vs=set(v_nbrs)
76
 
        if v in vs:
77
 
            vs.remove(v)
 
75
        vs=set(v_nbrs)-set([v])
78
76
        ntriangles=0
79
77
        for w in vs:
80
 
            ws=set(G[w])
81
 
            if w in ws:
82
 
                ws.remove(w)
 
78
            ws=set(G[w])-set([w])
83
79
            ntriangles+=len(vs.intersection(ws))
84
80
        yield (v,len(vs),ntriangles)
85
81
 
86
82
 
87
 
def average_clustering(G):
88
 
    """Compute average clustering coefficient.
89
 
 
90
 
    A clustering coefficient for the whole graph is the average, 
 
83
def _weighted_triangles_and_degree_iter(G, nodes=None, weight='weight'):
 
84
    """ Return an iterator of (node, degree, weighted_triangles).  
 
85
    
 
86
    Used for weighted clustering.
 
87
 
 
88
    """
 
89
    if G.is_multigraph():
 
90
        raise NetworkXError("Not defined for multigraphs.")
 
91
 
 
92
    if weight is None or G.edges()==[]:
 
93
        max_weight=1.0
 
94
    else:
 
95
        max_weight=float(max(d.get(weight,1.0) 
 
96
                             for u,v,d in G.edges(data=True)))
 
97
    if nodes is None:
 
98
        nodes_nbrs = G.adj.items()
 
99
    else:
 
100
        nodes_nbrs= ( (n,G[n]) for n in G.nbunch_iter(nodes) )
 
101
 
 
102
    for i,nbrs in nodes_nbrs:
 
103
        inbrs=set(nbrs)-set([i])
 
104
        weighted_triangles=0.0
 
105
        seen=set()
 
106
        for j in inbrs:
 
107
            wij=G[i][j].get(weight,1.0)/max_weight
 
108
            seen.add(j)
 
109
            jnbrs=set(G[j])-seen # this keeps from double counting
 
110
            for k in inbrs&jnbrs:
 
111
                wjk=G[j][k].get(weight,1.0)/max_weight
 
112
                wki=G[i][k].get(weight,1.0)/max_weight
 
113
                weighted_triangles+=(wij*wjk*wki)**(1.0/3.0)
 
114
        yield (i,len(inbrs),weighted_triangles*2)
 
115
 
 
116
 
 
117
def average_clustering(G, nodes=None, weight=None, count_zeros=True):
 
118
    r"""Compute the average clustering coefficient for the graph G.
 
119
 
 
120
    The clustering coefficient for the graph is the average, 
91
121
 
92
122
    .. math::
93
123
 
94
 
       C = \\frac{1}{n}\\sum_{v \in G} c_v,
 
124
       C = \frac{1}{n}\sum_{v \in G} c_v,
95
125
       
96
 
    where :math:`n` is the number of nodes in :math:`G`.
 
126
    where `n` is the number of nodes in `G`.
97
127
 
98
128
    Parameters
99
129
    ----------
100
130
    G : graph
101
 
       A networkx graph
 
131
 
 
132
    nodes : container of nodes, optional (default=all nodes in G)
 
133
       Compute average clustering for nodes in this container. 
 
134
 
 
135
    weight : string or None, optional (default=None)
 
136
       The edge attribute that holds the numerical value used as a weight.
 
137
       If None, then each edge has weight 1.
 
138
 
 
139
    count_zeros : bool (default=False)       
 
140
       If False include only the nodes with nonzero clustering in the average.
102
141
 
103
142
    Returns
104
143
    -------
105
 
    out : float
 
144
    avg : float
106
145
       Average clustering
107
146
    
108
147
    Examples
114
153
    Notes
115
154
    -----
116
155
    This is a space saving routine; it might be faster
117
 
    to use clustering to get a list and then take the average.
 
156
    to use the clustering function to get a list and then take the average.
118
157
 
119
158
    Self loops are ignored.
120
159
 
 
160
    References
 
161
    ----------
 
162
    .. [1] Generalizations of the clustering coefficient to weighted 
 
163
       complex networks by J. Saramäki, M. Kivelä, J.-P. Onnela, 
 
164
       K. Kaski, and J. Kertész, Physical Review E, 75 027105 (2007).  
 
165
       http://jponnela.com/web_documents/a9.pdf
 
166
    .. [2] Marcus Kaiser,  Mean clustering coefficients: the role of isolated 
 
167
       nodes and leafs on clustering measures for small-world networks.
 
168
       http://arxiv.org/abs/0802.2512
121
169
    """
122
 
    order=G.order()
123
 
    s=sum(clustering(G).values())
124
 
    return s/float(order)
125
 
 
126
 
def clustering(G,nbunch=None,weights=False):
127
 
    """ Compute the clustering coefficient for nodes.
128
 
 
 
170
    c=clustering(G,nodes,weight=weight).values()
 
171
    if not count_zeros:
 
172
        c = [v for v in c if v > 0]
 
173
    return sum(c)/float(len(c))
 
174
 
 
175
def clustering(G, nodes=None, weight=None):
 
176
    r"""Compute the clustering coefficient for nodes.
 
177
 
 
178
    For unweighted graphs the clustering of each node `u`
 
179
    is the fraction of possible triangles that exist,
129
180
    For each node find the fraction of possible triangles that exist,
130
181
 
131
182
    .. math::
132
183
 
133
 
      c_v = \\frac{2 T(v)}{deg(v)(deg(v)-1)}
134
 
 
135
 
    where :math:`T(v)` is the number of triangles through node :math:`v`.       
 
184
      c_u = \frac{2 T(u)}{deg(u)(deg(u)-1)},
 
185
 
 
186
    where `T(u)` is the number of triangles through node `u` and
 
187
    `deg(u)` is the degree of `u`.
 
188
 
 
189
    For weighted graphs the clustering is defined
 
190
    as the geometric average of the subgraph edge weights [1]_,
 
191
 
 
192
    .. math::
 
193
 
 
194
       c_u = \frac{1}{deg(u)(deg(u)-1))}
 
195
            \sum_{uv} (\hat{w}_{uv} \hat{w}_{uw} \hat{w}_{vw})^{1/3}.
 
196
      
 
197
    The edge weights `\hat{w}_{uv}` are normalized by the maximum weight in the
 
198
    network `\hat{w}_{uv} = w_{uv}/\max(w)`.
 
199
 
 
200
    The value of `c_u` is assigned to 0 if `deg(u) < 2`.
136
201
 
137
202
    Parameters
138
203
    ----------
139
204
    G : graph
140
 
       A networkx graph
141
 
    nbunch : container of nodes, optional
142
 
       Limit to specified nodes. Default is entire graph.
143
 
    weights : bool, optional
144
 
        If True return fraction of connected triples as dictionary
145
 
        
 
205
 
 
206
    nodes : container of nodes, optional (default=all nodes in G)
 
207
       Compute clustering for nodes in this container. 
 
208
 
 
209
    weight : string or None, optional (default=None)
 
210
       The edge attribute that holds the numerical value used as a weight.
 
211
       If None, then each edge has weight 1.
 
212
 
146
213
    Returns
147
214
    -------
148
 
    out : float, dictionary or tuple of dictionaries
 
215
    out : float, or dictionary
149
216
       Clustering coefficient at specified nodes
150
217
 
151
218
    Examples
156
223
    >>> print(nx.clustering(G))
157
224
    {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0}
158
225
 
159
 
 
160
226
    Notes
161
227
    -----
162
 
    The weights are the fraction of connected triples in the graph
163
 
    which include the keyed node.  Ths is useful for computing
164
 
    transitivity.
165
 
 
166
228
    Self loops are ignored.
167
229
 
 
230
    References
 
231
    ----------
 
232
    .. [1] Generalizations of the clustering coefficient to weighted 
 
233
       complex networks by J. Saramäki, M. Kivelä, J.-P. Onnela, 
 
234
       K. Kaski, and J. Kertész, Physical Review E, 75 027105 (2007).  
 
235
       http://jponnela.com/web_documents/a9.pdf
168
236
    """
169
237
    if G.is_directed():
170
 
        raise NetworkXError("Clustering algorithms are not defined for directed graphs.")
171
 
    if weights:
172
 
        clusterc={}
173
 
        weights={}
174
 
        for v,d,t in _triangles_and_degree_iter(G,nbunch):
175
 
            weights[v]=float(d*(d-1))
176
 
            if t==0:
177
 
                clusterc[v]=0.0
178
 
            else:
179
 
                clusterc[v]=t/float(d*(d-1))
180
 
        scale=1./sum(weights.values())
181
 
        for v,w in weights.items():
182
 
            weights[v]=w*scale
183
 
        return clusterc,weights
 
238
        raise NetworkXError('Clustering algorithms are not defined ',
 
239
                            'for directed graphs.')
 
240
    if weight is not None:
 
241
        td_iter=_weighted_triangles_and_degree_iter(G,nodes,weight)
 
242
    else:
 
243
        td_iter=_triangles_and_degree_iter(G,nodes)
184
244
 
185
245
    clusterc={}
186
 
    for v,d,t in _triangles_and_degree_iter(G,nbunch):
 
246
 
 
247
    for v,d,t in td_iter:
187
248
        if t==0:
188
249
            clusterc[v]=0.0
189
250
        else:
190
251
            clusterc[v]=t/float(d*(d-1))
191
252
 
192
 
    if nbunch in G: 
 
253
    if nodes in G: 
193
254
        return list(clusterc.values())[0] # return single value
194
255
    return clusterc
195
256
 
196
257
def transitivity(G):
197
 
    """Compute transitivity.
198
 
 
199
 
    Finds the fraction of all possible triangles which are in fact triangles.
200
 
    Possible triangles are identified by the number of "triads" (two edges
201
 
    with a shared vertex).
202
 
 
203
 
    T = 3*triangles/triads
204
 
 
 
258
    r"""Compute graph transitivity, the fraction of all possible triangles 
 
259
    present in G.
 
260
 
 
261
    Possible triangles are identified by the number of "triads" 
 
262
    (two edges with a shared vertex).
 
263
 
 
264
    The transitivity is
 
265
 
 
266
    .. math::
 
267
 
 
268
        T = 3\frac{\#triangles}{\#triads}.
205
269
 
206
270
    Parameters
207
271
    ----------
208
272
    G : graph
209
 
       A networkx graph
210
273
 
211
274
    Returns
212
275
    -------
215
278
 
216
279
    Examples
217
280
    --------
218
 
    >>> G=nx.complete_graph(5)
 
281
    >>> G = nx.complete_graph(5)
219
282
    >>> print(nx.transitivity(G))
220
283
    1.0
221
 
 
222
 
"""
 
284
    """
223
285
    triangles=0 # 6 times number of triangles
224
286
    contri=0  # 2 times number of connected triples
225
287
    for v,d,t in _triangles_and_degree_iter(G):
230
292
    else:
231
293
        return triangles/float(contri)
232
294
 
 
295
def square_clustering(G, nodes=None):
 
296
    r""" Compute the squares clustering coefficient for nodes.
 
297
 
 
298
    For each node return the fraction of possible squares that exist at
 
299
    the node [1]_
 
300
 
 
301
    .. math::
 
302
       C_4(v) = \frac{ \sum_{u=1}^{k_v} 
 
303
       \sum_{w=u+1}^{k_v} q_v(u,w) }{ \sum_{u=1}^{k_v} 
 
304
       \sum_{w=u+1}^{k_v} [a_v(u,w) + q_v(u,w)]},
 
305
    
 
306
    where `q_v(u,w)` are the number of common neighbors of `u` and `w` 
 
307
    other than `v` (ie squares), and 
 
308
    `a_v(u,w) = (k_u - (1+q_v(u,w)+\theta_{uv}))(k_w - (1+q_v(u,w)+\theta_{uw}))`,
 
309
    where `\theta_{uw} = 1` if `u` and `w` are connected and 0 otherwise.
 
310
 
 
311
    Parameters
 
312
    ----------
 
313
    G : graph
 
314
 
 
315
    nodes : container of nodes, optional (default=all nodes in G)
 
316
       Compute clustering for nodes in this container. 
 
317
        
 
318
    Returns
 
319
    -------
 
320
    c4 : dictionary
 
321
       A dictionary keyed by node with the square clustering coefficient value. 
 
322
 
 
323
    Examples
 
324
    --------
 
325
    >>> G=nx.complete_graph(5)
 
326
    >>> print(nx.square_clustering(G,0))
 
327
    1.0
 
328
    >>> print(nx.square_clustering(G))
 
329
    {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0}
 
330
 
 
331
    Notes
 
332
    -----
 
333
    While `C_3(v)` (triangle clustering) gives the probability that
 
334
    two neighbors of node v are connected with each other, `C_4(v)` is
 
335
    the probability that two neighbors of node v share a common
 
336
    neighbor different from v. This algorithm can be applied to both
 
337
    bipartite and unipartite networks.
 
338
 
 
339
    References
 
340
    ----------
 
341
    .. [1] Pedro G. Lind, Marta C. González, and Hans J. Herrmann. 2005
 
342
        Cycles and clustering in bipartite networks.
 
343
        Physical Review E (72) 056127.
 
344
    """
 
345
    if nodes is None:
 
346
        node_iter = G
 
347
    else:
 
348
        node_iter =  G.nbunch_iter(nodes) 
 
349
    clustering = {}
 
350
    for v in node_iter:
 
351
        clustering[v] = 0.0
 
352
        potential=0
 
353
        for u,w in combinations(G[v], 2):
 
354
            squares = len((set(G[u]) & set(G[w])) - set([v]))
 
355
            clustering[v] += squares
 
356
            degm = squares + 1.0
 
357
            if w in G[u]:
 
358
                degm += 1
 
359
            potential += (len(G[u]) - degm) * (len(G[w]) - degm) + squares
 
360
        if potential > 0:
 
361
            clustering[v] /= potential
 
362
    if nodes in G: 
 
363
        return list(clustering.values())[0] # return single value
 
364
    return clustering