org.gephi.statistics.plugin.DegreeDistribution.java Source code

Java tutorial

Introduction

Here is the source code for org.gephi.statistics.plugin.DegreeDistribution.java

Source

/*
Copyright 2008-2010 Gephi
Authors : Patick J. McSweeney <pjmcswee@syr.edu>, Sebastien Heymann <seb@gephi.org>
Website : http://www.gephi.org
    
This file is part of Gephi.
    
Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
    
Gephi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.
    
You should have received a copy of the GNU Affero General Public License
along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.statistics.plugin;

import java.io.File;
import java.io.IOException;
import org.gephi.data.attributes.api.AttributeModel;
import org.gephi.statistics.spi.Statistics;
import org.gephi.graph.api.*;
import org.gephi.utils.TempDirUtils;
import org.gephi.utils.TempDirUtils.TempDir;
import org.gephi.utils.longtask.spi.LongTask;
import org.gephi.utils.progress.Progress;
import org.gephi.utils.progress.ProgressTicket;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.entity.StandardEntityCollection;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;

/**
 * This class measures how closely the degree distribution of a
 * network follows a power-law scale.  An alpha value between 2 and 3
 * implies a power law.
 * @author pjmcswees
 */
public class DegreeDistribution implements Statistics, LongTask {

    /**  The combined In/Out-degree distribution. */
    private double[][] combinedDistribution;
    /** The In-degree distribution. */
    private double[][] inDistribution;
    /** The out-degree distribution. */
    private double[][] outDistribution;
    /** Remember if the metric has been canceled.  */
    private boolean isCanceled;
    /** The progress meter for this metric. */
    private ProgressTicket progress;
    /**  Indicates if this network should be directed or undirected.*/
    private boolean isDirected;
    /** The powerlaw value for the combined in/out-degree of this network. */
    private double combinedAlpha;
    /** The powerlaw value for the in-degree of this network. */
    private double inAlpha;
    /** The powerlaw value for the out-degree of this network. */
    private double outAlpha;
    /** The powerlaw value for the combined in/out-degree of this network. */
    private double combinedBeta;
    /** The powerlaw value for the in-degree of this network. */
    private double inBeta;
    /** The powerlaw value for the out-degree of this network. */
    private double outBeta;

    public DegreeDistribution() {
        GraphController graphController = Lookup.getDefault().lookup(GraphController.class);
        if (graphController != null && graphController.getModel() != null) {
            isDirected = graphController.getModel().isDirected();
        }
    }

    /**
     * @param isDirected Indicates the metric's interpretation of this network.
     */
    public void setDirected(boolean isDirected) {
        this.isDirected = isDirected;
    }

    public boolean isDirected() {
        return isDirected;
    }

    /**
     * @return The combined in/out-degree power law value for this network.
     */
    public double getCombinedPowerLaw() {
        return this.combinedAlpha;
    }

    /**
     * @return The combined in/out-degree power law value for this network.
     */
    public double getInPowerLaw() {
        return this.inAlpha;
    }

    /**
     * @return The combined in/out-degree power law value for this network.
     */
    public double getOutPowerLaw() {
        return this.outAlpha;
    }

    /**
     * Calculates the degree distribution for this network.
     * Either a combined degree distribution or separate
     * in-degree distribution and out-degree distribution
     * is calculated based on the mDirected variable.
     *
     * @param graphModel
     */
    public void execute(GraphModel graphModel, AttributeModel attributeModel) {
        //Get the graph from the graphController, based
        //on the mDirected variable.
        HierarchicalGraph graph;
        if (isDirected) {
            graph = graphModel.getHierarchicalDirectedGraphVisible();
        } else {
            graph = graphModel.getHierarchicalUndirectedGraphVisible();
        }
        execute(graph, attributeModel);
    }

    public void execute(HierarchicalGraph graph, AttributeModel attributeModel) {
        //Mark this as not yet canceled.
        isCanceled = false;

        graph.readLock();

        //Start 
        Progress.start(progress, graph.getNodeCount());

        //Consider the in and out degree of every node
        if (isDirected) {
            inDistribution = new double[2][2 * graph.getNodeCount()];
            outDistribution = new double[2][2 * graph.getNodeCount()];
        } else {
            combinedDistribution = new double[2][2 * graph.getNodeCount()];
        }

        int nodeCount = 0;
        for (Node node : graph.getNodes()) {
            if (isDirected) {
                int inDegree = ((HierarchicalDirectedGraph) graph).getTotalInDegree(node);
                int outDegree = ((HierarchicalDirectedGraph) graph).getTotalOutDegree(node);
                inDistribution[1][inDegree]++;
                outDistribution[1][outDegree]++;
                inDistribution[0][inDegree] = inDegree;
                outDistribution[0][outDegree] = outDegree;
            } else {
                int combinedDegree = ((HierarchicalUndirectedGraph) graph).getTotalDegree(node);
                combinedDistribution[1][combinedDegree]++;
                combinedDistribution[0][combinedDegree] = combinedDegree;
            }
            Progress.progress(progress, nodeCount);
            nodeCount++;
            if (isCanceled) {
                graph.readUnlockAll();
                return;
            }
        }

        graph.readUnlock();

        if (isDirected) {
            double[] inFit = new double[2];
            double[] outFit = new double[2];
            leastSquares(inDistribution[1], inFit);
            leastSquares(outDistribution[1], outFit);
            inAlpha = inFit[1];
            inBeta = inFit[0];
            outAlpha = outFit[1];
            outBeta = outFit[0];
        } else {
            double[] fit = new double[2];
            leastSquares(combinedDistribution[1], fit);
            combinedAlpha = fit[1];
            combinedBeta = fit[0];
        }
    }

    /**
     * Fits the logarithm distribution/degree to a straight line of the form:
     *   a + b *x which is then interpreted as a*x^y in the non-logarithmic scale
     *
     * @param dist The distribution of node degrees to fit to a logarithmized straight line
     *
     * @return An array of 2 doubles
     *               index 0: b
     *               index 1: a
     *
     *  For more see Wolfram Least Squares Fitting
     */
    public void leastSquares(double[] dist, double[] res) {
        //Vararibles to compute
        double SSxx = 0;
        double SSxy = 0;
        //double SSyy = 0;

        //Compute the average log(x) value when for positive (>0) values
        double avgX = 0;
        double avgY = 0;
        double nonZero = 0;
        for (int i = 1; i < dist.length; i++) {
            if (dist[i] > 0) {
                avgX += Math.log(i);
                avgY += Math.log(dist[i]);
                nonZero++;
            }

            if (isCanceled) {
                return;
            }

        }
        avgX /= nonZero;
        avgY /= nonZero;

        //compute the variance of log(x)
        for (int i = 1; i < dist.length; i++) {
            if (dist[i] > 0) {
                SSxx += Math.pow(Math.log(i) - avgX, 2);
                //SSyy += Math.pow(Math.log(dist[i]) - avgY, 2);
                SSxy += (Math.log(i) - avgX) * (Math.log(dist[i]) - avgY);
            }
        }

        //Compute and return the results
        res[0] = SSxy / SSxx;
        res[1] = avgY - res[0] * avgX;
    }

    /**
     * @return A String report based on the interpretation of the network.
     */
    public String getReport() {
        return (isDirected) ? getDirectedReport() : getUndirectedReport();
    }

    /**
     *
     * @return The directed version of the report.
     */
    private String getDirectedReport() {
        double inMax = 0;
        XYSeries inSeries2 = new XYSeries("Series 2");
        for (int i = 1; i < inDistribution[1].length; i++) {
            if (inDistribution[1][i] > 0) {
                inSeries2.add((Math.log(inDistribution[0][i]) / Math.log(Math.E)),
                        (Math.log(inDistribution[1][i]) / Math.log(Math.E)));
                inMax = (float) Math.max((Math.log(inDistribution[0][i]) / Math.log(Math.E)), inMax);
            }
        }
        double inA = inAlpha;
        double inB = inBeta;

        String inImageFile = "";
        String outImageFile = "";
        try {

            XYSeries inSeries1 = new XYSeries(inAlpha + " ");
            inSeries1.add(0, inA);
            inSeries1.add(inMax, inA + inB * inMax);

            XYSeriesCollection inDataset = new XYSeriesCollection();
            inDataset.addSeries(inSeries1);
            inDataset.addSeries(inSeries2);

            JFreeChart inChart = ChartFactory.createXYLineChart("In-Degree Distribution", "In-Degree", "Occurrence",
                    inDataset, PlotOrientation.VERTICAL, true, false, false);
            XYPlot inPlot = (XYPlot) inChart.getPlot();
            XYLineAndShapeRenderer inRenderer = new XYLineAndShapeRenderer();
            inRenderer.setSeriesLinesVisible(0, true);
            inRenderer.setSeriesShapesVisible(0, false);
            inRenderer.setSeriesLinesVisible(1, false);
            inRenderer.setSeriesShapesVisible(1, true);
            inRenderer.setSeriesShape(1, new java.awt.geom.Ellipse2D.Double(0, 0, 1, 1));
            inPlot.setBackgroundPaint(java.awt.Color.WHITE);
            inPlot.setDomainGridlinePaint(java.awt.Color.GRAY);
            inPlot.setRangeGridlinePaint(java.awt.Color.GRAY);

            inPlot.setRenderer(inRenderer);

            final ChartRenderingInfo info = new ChartRenderingInfo(new StandardEntityCollection());

            TempDir tempDir = TempDirUtils.createTempDir();
            final String fileName = "inDistribution.png";
            final File file1 = tempDir.createFile(fileName);
            inImageFile = "<IMG SRC=\"file:" + file1.getAbsolutePath() + "\" "
                    + "WIDTH=\"600\" HEIGHT=\"400\" BORDER=\"0\" USEMAP=\"#chart\"></IMG>";
            ChartUtilities.saveChartAsPNG(file1, inChart, 600, 400, info);

            double outMax = 0;
            XYSeries outSeries2 = new XYSeries("Series 2");
            for (int i = 1; i < outDistribution[1].length; i++) {
                if (outDistribution[1][i] > 0) {
                    outSeries2.add((Math.log(outDistribution[0][i]) / Math.log(Math.E)),
                            (Math.log(outDistribution[1][i]) / Math.log(Math.E)));
                    outMax = (float) Math.max((Math.log(outDistribution[0][i]) / Math.log(Math.E)), outMax);
                }
            }
            double outA = outAlpha;
            double outB = outBeta;

            XYSeries outSeries1 = new XYSeries(outAlpha + " ");
            outSeries1.add(0, outA);
            outSeries1.add(outMax, outA + outB * outMax);

            XYSeriesCollection outDataset = new XYSeriesCollection();
            outDataset.addSeries(outSeries1);
            outDataset.addSeries(outSeries2);

            JFreeChart outchart = ChartFactory.createXYLineChart("Out-Degree Distribution", "Out-Degree",
                    "Occurrence", outDataset, PlotOrientation.VERTICAL, true, false, false);
            XYPlot outPlot = (XYPlot) outchart.getPlot();
            XYLineAndShapeRenderer outRenderer = new XYLineAndShapeRenderer();
            outRenderer.setSeriesLinesVisible(0, true);
            outRenderer.setSeriesShapesVisible(0, false);
            outRenderer.setSeriesLinesVisible(1, false);
            outRenderer.setSeriesShapesVisible(1, true);
            outRenderer.setSeriesShape(1, new java.awt.geom.Ellipse2D.Double(0, 0, 1, 1));
            outPlot.setBackgroundPaint(java.awt.Color.WHITE);
            outPlot.setDomainGridlinePaint(java.awt.Color.GRAY);
            outPlot.setRangeGridlinePaint(java.awt.Color.GRAY);

            outPlot.setRenderer(outRenderer);

            final ChartRenderingInfo info2 = new ChartRenderingInfo(new StandardEntityCollection());
            final String fileName2 = "outDistribution.png";
            final File file2 = tempDir.createFile(fileName2);
            outImageFile = "<IMG SRC=\"file:" + file2.getAbsolutePath() + "\" "
                    + "WIDTH=\"600\" HEIGHT=\"400\" BORDER=\"0\" USEMAP=\"#chart\"></IMG>";
            ChartUtilities.saveChartAsPNG(file2, outchart, 600, 400, info2);
        } catch (IOException e) {
            Exceptions.printStackTrace(e);
        }

        String report = "<HTML> <BODY> <h1>Degree Distribution Metric Report </h1> " + "<hr>" + "<br>"
                + "<h2> Parameters: </h2>" + "Network Interpretation:  " + (isDirected ? "directed" : "undirected")
                + "<br>" + "<br> <h2> Results: </h2>" + "In-Degree Power Law: -" + inAlpha + "\n <BR>" + inImageFile
                + "<br>Out-Degree Power Law: -" + outAlpha + "\n <BR>" + outImageFile + "</BODY> </HTML>";

        return report;
    }

    /**
     *
     * @return The undirected version of this report.
     */
    private String getUndirectedReport() {
        double max = 0;
        XYSeries series2 = new XYSeries("Series 2");
        for (int i = 1; i < combinedDistribution[1].length; i++) {
            if (combinedDistribution[1][i] > 0) {
                series2.add((Math.log(combinedDistribution[0][i]) / Math.log(Math.E)),
                        (Math.log(combinedDistribution[1][i]) / Math.log(Math.E)));
                max = (float) Math.max((Math.log(combinedDistribution[0][i]) / Math.log(Math.E)), max);
            }
        }
        double a = combinedAlpha;
        double b = combinedBeta;

        XYSeries series1 = new XYSeries(combinedAlpha + " ");
        series1.add(0, a);
        series1.add(max, a + b * max);

        XYSeriesCollection dataset = new XYSeriesCollection();
        dataset.addSeries(series1);
        dataset.addSeries(series2);

        JFreeChart chart = ChartFactory.createXYLineChart("Degree Distribution", "Degree", "Occurrence", dataset,
                PlotOrientation.VERTICAL, true, false, false);
        XYPlot plot = (XYPlot) chart.getPlot();
        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
        renderer.setSeriesLinesVisible(0, true);
        renderer.setSeriesShapesVisible(0, false);
        renderer.setSeriesLinesVisible(1, false);
        renderer.setSeriesShapesVisible(1, true);
        renderer.setSeriesShape(1, new java.awt.geom.Ellipse2D.Double(0, 0, 1, 1));
        plot.setBackgroundPaint(java.awt.Color.WHITE);
        plot.setDomainGridlinePaint(java.awt.Color.GRAY);
        plot.setRangeGridlinePaint(java.awt.Color.GRAY);

        plot.setRenderer(renderer);

        String imageFile = "";
        try {
            final ChartRenderingInfo info = new ChartRenderingInfo(new StandardEntityCollection());
            TempDir tempDir = TempDirUtils.createTempDir();
            final String fileName = "distribution.png";
            final File file1 = tempDir.createFile(fileName);
            imageFile = "<IMG SRC=\"file:" + file1.getAbsolutePath() + "\" "
                    + "WIDTH=\"600\" HEIGHT=\"400\" BORDER=\"0\" USEMAP=\"#chart\"></IMG>";
            ChartUtilities.saveChartAsPNG(file1, chart, 600, 400, info);

        } catch (IOException e) {
            System.out.println(e.toString());
        }

        String report = "<HTML> <BODY> <h1>Degree Distribution Metric Report </h1> " + "<hr>" + "<br>"
                + "<h2> Parameters: </h2>" + "Network Interpretation:  " + (isDirected ? "directed" : "undirected")
                + "<br>" + "<br> <h2> Results: </h2>" + "Degree Power Law: -" + combinedAlpha + "\n <BR>"
                + imageFile + "</BODY> </HTML>";
        return report;
    }

    /**
     * @return Indicates that the metric canceled flag was set.
     */
    public boolean cancel() {
        isCanceled = true;
        return true;
    }

    /**
     * @param progressTicket Sets the progress meter for the metric.
     */
    public void setProgressTicket(ProgressTicket pProgressTicket) {
        progress = pProgressTicket;
    }
}