/* * Created on Jul 10, 2007 * * Copyright (c) 2007, the JUNG Project and the Regents of the University * of California * All rights reserved. * * This software is open-source under the BSD license; see either * "license.txt" or * http://jung.sourceforge.net/license.txt for a description. */ package edu.uci.ics.jung.algorithms.scoring; import java.util.HashMap; import java.util.Map; import org.apache.commons.collections15.Transformer; import edu.uci.ics.jung.algorithms.shortestpath.DijkstraDistance; import edu.uci.ics.jung.algorithms.shortestpath.Distance; import edu.uci.ics.jung.algorithms.shortestpath.UnweightedShortestPath; import edu.uci.ics.jung.graph.Hypergraph; /** * Assigns scores to vertices based on their distances to each other vertex in * the graph. * * This class optionally normalizes its results based on the value of its * 'averaging' constructor parameter. If it is <code>true</code>, then the value * returned for vertex v is 1 / (_average_ distance from v to all other * vertices); this is sometimes called <i>closeness centrality</i>. If it is * <code>false</code>, then the value returned is 1 / (_total_ distance from v * to all other vertices); this is sometimes referred to as <i>barycenter * centrality</i>. (If the average/total distance is 0, the value returned is * {@code Double.POSITIVE_INFINITY}.) * * @see BarycenterScorer * @see ClosenessCentrality */ public class DistanceCentralityScorer<V, E> implements VertexScorer<V, Double> { /** * The graph on which the vertex scores are to be calculated. */ protected Hypergraph<V, E> graph; /** * The metric to use for specifying the distance between pairs of vertices. */ protected Distance<V> distance; /** * The cache for the output results. Null encodes "not yet calculated", < 0 * encodes "no such distance exists". */ protected Map<V, Double> output; /** * Specifies whether the values returned are the sum of the v-distances or * the mean v-distance. */ protected boolean averaging; /** * Specifies whether, for a vertex <code>v</code> with missing (null) * distances, <code>v</code>'s score should ignore the missing values or be * set to 'null'. Defaults to 'true'. */ protected boolean ignore_missing; /** * Specifies whether the values returned should ignore self-distances * (distances from <code>v</code> to itself). Defaults to 'true'. */ protected boolean ignore_self_distances; /** * Creates an instance with the specified graph, distance metric, and * averaging behavior. * * @param graph * The graph on which the vertex scores are to be calculated. * @param distance * The metric to use for specifying the distance between pairs of * vertices. * @param averaging * Specifies whether the values returned is the sum of all * v-distances or the mean v-distance. * @param ignore_missing * Specifies whether scores for missing distances are to ignore * missing distances or be set to null. * @param ignore_self_distances * Specifies whether distances from a vertex to itself should be * included in its score. */ public DistanceCentralityScorer(Hypergraph<V, E> graph, Distance<V> distance, boolean averaging, boolean ignore_missing, boolean ignore_self_distances) { this.graph = graph; this.distance = distance; this.averaging = averaging; this.ignore_missing = ignore_missing; this.ignore_self_distances = ignore_self_distances; this.output = new HashMap<V, Double>(); } /** * Equivalent to <code>this(graph, distance, averaging, true, true)</code>. * * @param graph * The graph on which the vertex scores are to be calculated. * @param distance * The metric to use for specifying the distance between pairs of * vertices. * @param averaging * Specifies whether the values returned is the sum of all * v-distances or the mean v-distance. */ public DistanceCentralityScorer(Hypergraph<V, E> graph, Distance<V> distance, boolean averaging) { this(graph, distance, averaging, true, true); } /** * Creates an instance with the specified graph and averaging behavior whose * vertex distances are calculated based on the specified edge weights. * * @param graph * The graph on which the vertex scores are to be calculated. * @param edge_weights * The edge weights to use for specifying the distance between * pairs of vertices. * @param averaging * Specifies whether the values returned is the sum of all * v-distances or the mean v-distance. * @param ignore_missing * Specifies whether scores for missing distances are to ignore * missing distances or be set to null. * @param ignore_self_distances * Specifies whether distances from a vertex to itself should be * included in its score. */ public DistanceCentralityScorer(Hypergraph<V, E> graph, Transformer<E, ? extends Number> edge_weights, boolean averaging, boolean ignore_missing, boolean ignore_self_distances) { this(graph, new DijkstraDistance<V, E>(graph, edge_weights), averaging, ignore_missing, ignore_self_distances); } /** * Equivalent to * <code>this(graph, edge_weights, averaging, true, true)</code>. * * @param graph * The graph on which the vertex scores are to be calculated. * @param edge_weights * The edge weights to use for specifying the distance between * pairs of vertices. * @param averaging * Specifies whether the values returned is the sum of all * v-distances or the mean v-distance. */ public DistanceCentralityScorer(Hypergraph<V, E> graph, Transformer<E, ? extends Number> edge_weights, boolean averaging) { this(graph, new DijkstraDistance<V, E>(graph, edge_weights), averaging, true, true); } /** * Creates an instance with the specified graph and averaging behavior whose * vertex distances are calculated on the unweighted graph. * * @param graph * The graph on which the vertex scores are to be calculated. * @param averaging * Specifies whether the values returned is the sum of all * v-distances or the mean v-distance. * @param ignore_missing * Specifies whether scores for missing distances are to ignore * missing distances or be set to null. * @param ignore_self_distances * Specifies whether distances from a vertex to itself should be * included in its score. */ public DistanceCentralityScorer(Hypergraph<V, E> graph, boolean averaging, boolean ignore_missing, boolean ignore_self_distances) { this(graph, new UnweightedShortestPath<V, E>(graph), averaging, ignore_missing, ignore_self_distances); } /** * Equivalent to <code>this(graph, averaging, true, true)</code>. * * @param graph * The graph on which the vertex scores are to be calculated. * @param averaging * Specifies whether the values returned is the sum of all * v-distances or the mean v-distance. */ public DistanceCentralityScorer(Hypergraph<V, E> graph, boolean averaging) { this(graph, new UnweightedShortestPath<V, E>(graph), averaging, true, true); } /** * Calculates the score for the specified vertex. Returns {@code null} if * there are missing distances and such are not ignored by this instance. */ @Override public Double getVertexScore(V v) { Double value = output.get(v); if (value != null) { if (value < 0) { return null; } return value; } Map<V, Number> v_distances = new HashMap<V, Number>( distance.getDistanceMap(v)); if (ignore_self_distances) { v_distances.remove(v); } // if we don't ignore missing distances and there aren't enough // distances, output null (shortcut) if (!ignore_missing) { int num_dests = graph.getVertexCount() - (ignore_self_distances ? 1 : 0); if (v_distances.size() != num_dests) { output.put(v, -1.0); return null; } } Double sum = 0.0; for (V w : graph.getVertices()) { if (w.equals(v) && ignore_self_distances) { continue; } Number w_distance = v_distances.get(w); if (w_distance == null) { if (ignore_missing) { continue; } output.put(v, -1.0); return null; } sum += w_distance.doubleValue(); } value = sum; if (averaging) { value /= v_distances.size(); } double score = value == 0 ? Double.POSITIVE_INFINITY : 1.0 / value; output.put(v, score); return score; } }