Repository: networkx/networkx

Issue:
Closeness centrality - normalisation flag - clarity necessary for what normalisation means in this context.
When trying the `networkx.algorithms.centrality.closeness` method it seems to return the same values whether setting `normalisation` to true or false.

Looking at the code:

```
closeness_centrality = {}
for n in nodes:
    sp = path_length(G,n)
    totsp = sum(sp.values())
    if totsp > 0.0 and len(G) > 1:
        closeness_centrality[n] = (len(sp)-1.0) / totsp
       # normalize to number of nodes-1 in connected part
        if normalized:
            s = (len(sp)-1.0) / ( len(G) - 1 )
            closeness_centrality[n] *= s
    else:
        closeness_centrality[n] = 0.0
```

In most cases the length of the `sp` dictionary will be the same as the number of nodes in the graph, thus multiplying `closeness_centrality[n]` by 1 and having no effect?

It seems like the intent is to normalise values across disconnected graphs?



Discussion:
The intent is indeed to normalize relative to the minimum distance possible within the component. I think the documentation is fairly clear about this. But there are other issues going on.

In the cited article, inverse closeness is the sum of the distances divided by (number of nodes -1). Closeness is then (number of nodes -1) divided by sum of distances.  Let N be the number of nodes and n be the number of nodes in the component.

Questions:
- without normalization we have `C = (n-1)/sum_of_dict`.  So normalization already occurs even when `normalized is False`. That should be made clear in docs.
- with normalization we have `C = (n-1)/(N-1) * (n-1)/sum_of_dict` which looks like the reciprocal of what we want to multiply by. Shouldn't the scale factor `s=(n-1)/N-1)` be instead `s=(N-1)/(n-1)`?

The tests don't check normalization so its hard to tell what was intended.

I'm pretty sure I'm guilty here https://github.com/networkx/networkx/commit/7df946596899468c0b114adf3fb581d830b7c051.
I'm not sure what the right answer is.     

If I'm not mistaken, Closeness centrality by definition takes into account the number of nodes, i.e.
`(n-1)/sum(distances from node to all others)`

So in one sense normalisation is a misnomer? That said, in my case I would find it beneficial to have the option to return the raw sum of distances and the number of nodes so that I can 'normalise' using other methods.

Some earlier discussion 
#824
https://groups.google.com/forum/?hl=en#!topic/networkx-discuss/ZfCQDrZcvYI
#241

Following up on the previous discussion, Wasserman and Faust do indeed suggest an "improved" closeness centrality measure that scales as we scale it.  The result is "a ratio of the fraction of actors in the group who are reachable, to the average distance" to the reachable actors [W&F pg 201].  So we've got the right scaling and it matches our documentation.

The only issue I see here is the word "normalized" and whether the documentation sufficiently stresses what that means. I think @shongololo was able to figure out what was going on... but was concerned that it might not be what was intended. I'll change the docs... 

Should I change the name "normalized"?

BTW, to get the sum of the distances on can use the shortest_path routines directly:

```
dict((node, sum(d for _,d in nx.single_source_shortest_path_length(G, node))) for node in nodes)
```

Yes, "normalized" does seem a little bit of a stretch for what this does.  Got a better name?

@dschult the 'improved' closeness centrality is the method I like to use.

Perhaps an `improved_formula` or `alt_formula` flag with an explanation that this 'normalises' based on Wasserman and Faust's method vs. the conventional method?

Or we could just write another function with a different name.

See #2391 for more confusion over normalization.

Resolving patch:
diff --git a/networkx/algorithms/centrality/closeness.py b/networkx/algorithms/centrality/closeness.py
--- a/networkx/algorithms/centrality/closeness.py
+++ b/networkx/algorithms/centrality/closeness.py
@@ -1,50 +1,68 @@
-"""
-Closeness centrality measures.
-"""
 #    Copyright (C) 2004-2017 by
 #    Aric Hagberg <hagberg@lanl.gov>
 #    Dan Schult <dschult@colgate.edu>
 #    Pieter Swart <swart@lanl.gov>
 #    All rights reserved.
 #    BSD license.
+#
+# Authors:  Aric Hagberg <aric.hagberg@gmail.com>
+#           Pieter Swart <swart@lanl.gov>
+#           Sasha Gutfraind <ag362@cornell.edu>
+#           Dan Schult <dschult@colgate.edu>
+"""
+Closeness centrality measures.
+"""
 import functools
 import networkx as nx
-__author__ = "\n".join(['Aric Hagberg <aric.hagberg@gmail.com>',
-                        'Pieter Swart (swart@lanl.gov)',
-                        'Sasha Gutfraind (ag362@cornell.edu)'])
+
 __all__ = ['closeness_centrality']
 
 
-def closeness_centrality(G, u=None, distance=None, normalized=True, reverse=False):
+def closeness_centrality(G, u=None, distance=None,
+                         wf_improved=True, reverse=False):
     r"""Compute closeness centrality for nodes.
 
     Closeness centrality [1]_ of a node `u` is the reciprocal of the
-    sum of the shortest path distances to `u` from all `n-1` other nodes.
-    Since the sum of distances depends on the number of nodes in the
-    graph, closeness is normalized by the sum of minimum possible
-    distances `n-1`.
+    average shortest path distance to `u` over all `n-1` reachable nodes.
 
     .. math::
 
         C(u) = \frac{n - 1}{\sum_{v=1}^{n-1} d(v, u)},
 
     where `d(v, u)` is the shortest-path distance between `v` and `u`,
-    and `n` is the number of nodes in the graph.
+    and `n` is the number of nodes that can reach `u`.
 
     Notice that higher values of closeness indicate higher centrality.
 
+    Wasserman and Faust propose an improved formula for graphs with
+    more than one connected component. The result is "a ratio of the
+    fraction of actors in the group who are reachable, to the average
+    distance" from the reachable actors [2]_. You might think this
+    scale factor is inverted but it is not. As is, nodes from small
+    components receive a smaller closeness value. Letting `N` denote
+    the number of nodes in the graph,
+
+    .. math::
+
+        C_{WF}(u) = \frac{n-1}{N-1} \frac{n - 1}{\sum_{v=1}^{n-1} d(v, u)},
+
     Parameters
     ----------
     G : graph
       A NetworkX graph
+
     u : node, optional
       Return only the value for node u
+
     distance : edge attribute key, optional (default=None)
-      Use the specified edge attribute as the edge distance in shortest 
+      Use the specified edge attribute as the edge distance in shortest
       path calculations
-    normalized : bool, optional
-      If True (default) normalize by the number of nodes in the connected
-      part of the graph.
+
+    wf_improved : bool, optional (default=True)
+      If True, scale by the fraction of nodes reachable. This gives the
+      Wasserman and Faust improved formula. For single component graphs
+      it is the same as the original formula. 
+
     reverse : bool, optional (default=False)
       If True and G is a digraph, reverse the edges of G, using successors
       instead of predecessors.
@@ -65,7 +83,7 @@ def closeness_centrality(G, u=None, distance=None, normalized=True, reverse=Fals
     `n` is the number of nodes in the connected part of graph
     containing the node.  If the graph is not completely connected,
     this algorithm computes the closeness centrality for each
-    connected part separately.
+    connected part separately scaled by that parts size.
 
     If the 'distance' keyword is set to an edge attribute key then the
     shortest-path length will be computed using Dijkstra's algorithm with
@@ -76,12 +94,15 @@ def closeness_centrality(G, u=None, distance=None, normalized=True, reverse=Fals
     .. [1] Linton C. Freeman: Centrality in networks: I.
        Conceptual clarification. Social Networks 1:215-239, 1979.
        http://leonidzhukov.ru/hse/2013/socialnetworks/papers/freeman79-centrality.pdf
+    .. [2] pg. 201 of Wasserman, S. and Faust, K.,
+       Social Network Analysis: Methods and Applications, 1994,
+       Cambridge University Press.
     """
     if distance is not None:
-        # use Dijkstra's algorithm with specified attribute as edge weight 
+        # use Dijkstra's algorithm with specified attribute as edge weight
         path_length = functools.partial(nx.single_source_dijkstra_path_length,
                                         weight=distance)
-    else: # handle either directed or undirected
+    else:  # handle either directed or undirected
         if G.is_directed() and not reverse:
             path_length = nx.single_target_shortest_path_length
         else:
@@ -96,10 +117,10 @@ def closeness_centrality(G, u=None, distance=None, normalized=True, reverse=Fals
         sp = dict(path_length(G, n))
         totsp = sum(sp.values())
         if totsp > 0.0 and len(G) > 1:
-            closeness_centrality[n] = (len(sp)-1.0) / totsp
+            closeness_centrality[n] = (len(sp) - 1.0) / totsp
             # normalize to number of nodes-1 in connected part
-            if normalized:
-                s = (len(sp)-1.0) / ( len(G) - 1 )
+            if wf_improved:
+                s = (len(sp) - 1.0) / (len(G) - 1)
                 closeness_centrality[n] *= s
         else:
             closeness_centrality[n] = 0.0


Test patch:
diff --git a/networkx/algorithms/centrality/tests/test_closeness_centrality.py b/networkx/algorithms/centrality/tests/test_closeness_centrality.py
--- a/networkx/algorithms/centrality/tests/test_closeness_centrality.py
+++ b/networkx/algorithms/centrality/tests/test_closeness_centrality.py
@@ -4,90 +4,109 @@
 from nose.tools import *
 import networkx as nx
 
+
 class TestClosenessCentrality:
     def setUp(self):
-
         self.K = nx.krackhardt_kite_graph()
         self.P3 = nx.path_graph(3)
         self.P4 = nx.path_graph(4)
         self.K5 = nx.complete_graph(5)
 
-        self.C4=nx.cycle_graph(4)
-        self.T=nx.balanced_tree(r=2, h=2)
+        self.C4 = nx.cycle_graph(4)
+        self.T = nx.balanced_tree(r=2, h=2)
         self.Gb = nx.Graph()
-        self.Gb.add_edges_from([(0,1), (0,2), (1,3), (2,3), 
-                                (2,4), (4,5), (3,5)])
-
+        self.Gb.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3),
+                                (2, 4), (4, 5), (3, 5)])
 
         F = nx.florentine_families_graph()
         self.F = F
 
+    def test_wf_improved(self):
+        G = nx.union(self.P4, nx.path_graph([4, 5, 6]))
+        c = nx.closeness_centrality(G)
+        cwf = nx.closeness_centrality(G, wf_improved=False)
+        res = {0: 0.25, 1: 0.375, 2: 0.375, 3: 0.25,
+               4: 0.222, 5: 0.333, 6: 0.222}
+        wf_res = {0: 0.5, 1: 0.75, 2: 0.75, 3: 0.5,
+                  4: 0.667, 5: 1.0, 6: 0.667}
+        for n in G:
+            assert_almost_equal(c[n], res[n], places=3)
+            assert_almost_equal(cwf[n], wf_res[n], places=3)
+
+    def test_digraph(self):
+        G = nx.path_graph(3, create_using=nx.DiGraph())
+        c = nx.closeness_centrality(G)
+        cr = nx.closeness_centrality(G, reverse=True)
+        d = {0: 0.0, 1: 0.500, 2: 0.667}
+        dr = {0: 0.667, 1: 0.500, 2: 0.0}
+        for n in sorted(self.P3):
+            assert_almost_equal(c[n], d[n], places=3)
+            assert_almost_equal(cr[n], dr[n], places=3)
 
     def test_k5_closeness(self):
-        c=nx.closeness_centrality(self.K5)
-        d={0: 1.000,
-           1: 1.000,
-           2: 1.000,
-           3: 1.000,
-           4: 1.000}
+        c = nx.closeness_centrality(self.K5)
+        d = {0: 1.000,
+             1: 1.000,
+             2: 1.000,
+             3: 1.000,
+             4: 1.000}
         for n in sorted(self.K5):
-            assert_almost_equal(c[n],d[n],places=3)
+            assert_almost_equal(c[n], d[n], places=3)
 
     def test_p3_closeness(self):
-        c=nx.closeness_centrality(self.P3)
-        d={0: 0.667,
-           1: 1.000,
-           2: 0.667}
+        c = nx.closeness_centrality(self.P3)
+        d = {0: 0.667,
+             1: 1.000,
+             2: 0.667}
         for n in sorted(self.P3):
-            assert_almost_equal(c[n],d[n],places=3)
+            assert_almost_equal(c[n], d[n], places=3)
 
     def test_krackhardt_closeness(self):
-        c=nx.closeness_centrality(self.K)
-        d={0: 0.529,
-           1: 0.529,
-           2: 0.500,
-           3: 0.600,
-           4: 0.500,
-           5: 0.643,
-           6: 0.643,
-           7: 0.600,
-           8: 0.429,
-           9: 0.310}
+        c = nx.closeness_centrality(self.K)
+        d = {0: 0.529,
+             1: 0.529,
+             2: 0.500,
+             3: 0.600,
+             4: 0.500,
+             5: 0.643,
+             6: 0.643,
+             7: 0.600,
+             8: 0.429,
+             9: 0.310}
         for n in sorted(self.K):
-            assert_almost_equal(c[n],d[n],places=3)
+            assert_almost_equal(c[n], d[n], places=3)
 
     def test_florentine_families_closeness(self):
-        c=nx.closeness_centrality(self.F)
-        d={'Acciaiuoli':    0.368,
-           'Albizzi':       0.483,
-           'Barbadori':     0.4375,
-           'Bischeri':      0.400,
-           'Castellani':    0.389,
-           'Ginori':        0.333,
-           'Guadagni':      0.467,
-           'Lamberteschi':  0.326,
-           'Medici':        0.560,
-           'Pazzi':         0.286,
-           'Peruzzi':       0.368,
-           'Ridolfi':       0.500,
-           'Salviati':      0.389,
-           'Strozzi':       0.4375,
-           'Tornabuoni':    0.483}
+        c = nx.closeness_centrality(self.F)
+        d = {'Acciaiuoli':    0.368,
+             'Albizzi':       0.483,
+             'Barbadori':     0.4375,
+             'Bischeri':      0.400,
+             'Castellani':    0.389,
+             'Ginori':        0.333,
+             'Guadagni':      0.467,
+             'Lamberteschi':  0.326,
+             'Medici':        0.560,
+             'Pazzi':         0.286,
+             'Peruzzi':       0.368,
+             'Ridolfi':       0.500,
+             'Salviati':      0.389,
+             'Strozzi':       0.4375,
+             'Tornabuoni':    0.483}
         for n in sorted(self.F):
-            assert_almost_equal(c[n],d[n],places=3)
+            assert_almost_equal(c[n], d[n], places=3)
 
     def test_weighted_closeness(self):
-        XG=nx.Graph()
-        XG.add_weighted_edges_from([('s','u',10), ('s','x',5), ('u','v',1),
-                                    ('u','x',2), ('v','y',1), ('x','u',3),
-                                    ('x','v',5), ('x','y',2), ('y','s',7),
-                                    ('y','v',6)])
-        c=nx.closeness_centrality(XG,distance='weight')
-        d={'y': 0.200,
-           'x': 0.286,
-           's': 0.138,
-           'u': 0.235,
-           'v': 0.200}
+        edges = ([('s', 'u', 10), ('s', 'x', 5), ('u', 'v', 1),
+                  ('u', 'x', 2), ('v', 'y', 1), ('x', 'u', 3),
+                  ('x', 'v', 5), ('x', 'y', 2), ('y', 's', 7), ('y', 'v', 6)])
+        XG = nx.Graph()
+        XG.add_weighted_edges_from(edges)
+        c = nx.closeness_centrality(XG, distance='weight')
+        d = {'y': 0.200,
+             'x': 0.286,
+             's': 0.138,
+             'u': 0.235,
+             'v': 0.200}
         for n in sorted(XG):
-            assert_almost_equal(c[n],d[n],places=3)
-
+            assert_almost_equal(c[n], d[n], places=3)