Skip to content

Instantly share code, notes, and snippets.

@eglassman
Forked from micahstubbs/0-thumbnail.png
Created January 26, 2017 11:04
Show Gist options
  • Select an option

  • Save eglassman/935bf79b9dbd7d241a4195c47727a26f to your computer and use it in GitHub Desktop.

Select an option

Save eglassman/935bf79b9dbd7d241a4195c47727a26f to your computer and use it in GitHub Desktop.

Revisions

  1. @micahstubbs micahstubbs revised this gist Jun 14, 2016. No changes.
  2. @micahstubbs micahstubbs revised this gist Jun 14, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@
    <head>
    <title>Blocks Graph - Readme Mentions</title>
    <meta charset="utf-8" />
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script src="https://d3js.org/d3.v3.min.js"></script>
    <script src="jsLouvain.js"></script>
    <style>
    /*body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }*/
  3. @micahstubbs micahstubbs revised this gist Nov 29, 2015. No changes.
  4. @micahstubbs micahstubbs revised this gist Nov 28, 2015. 1 changed file with 0 additions and 0 deletions.
    Binary file added thumbnail.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  5. @micahstubbs micahstubbs renamed this gist Nov 28, 2015. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes
  6. @micahstubbs micahstubbs revised this gist Nov 28, 2015. 1 changed file with 5769 additions and 2609 deletions.
    8,378 changes: 5,769 additions & 2,609 deletions readme-blocks-graph.json
    5,769 additions, 2,609 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
  7. @micahstubbs micahstubbs revised this gist Nov 23, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    <html>
    <head>
    <title>Edge creation</title>
    <title>Blocks Graph - Readme Mentions</title>
    <meta charset="utf-8" />
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script src="jsLouvain.js"></script>
  8. @micahstubbs micahstubbs revised this gist Nov 23, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -15,5 +15,5 @@ a lineage of bl.ocks that informed this idea:
    * [Blocks Graph with Links](http://bl.ocks.org/curran/be4a45ec74357e7d9b10) from [curran](http://bl.ocks.org/curran)
    * [Dynamic Size](http://bl.ocks.org/curran/db1e524cae5e4344b2e6) from [curran](http://bl.ocks.org/curran)


    MIT License

  9. @micahstubbs micahstubbs revised this gist Nov 23, 2015. 1 changed file with 6 additions and 6 deletions.
    12 changes: 6 additions & 6 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -8,12 +8,12 @@ source data and scripts used to generate the graph data are at [this repository]

    a lineage of bl.ocks that informed this idea:

    [all the blocks](http://bl.ocks.org/enjalot/6ac67b0d8ed673c9aa61) from [enjalot](http://bl.ocks.org/enjalot)
    [Networks - Graphs 7](http://bl.ocks.org/emeeks/f2f6883ac7c965d09b90) from [emeeks](http://bl.ocks.org/emeeks)
    [Blocks Graph](http://bl.ocks.org/curran/1da93bab4cdc708f41ae) from [curran](http://bl.ocks.org/curran)
    [Blocks Graph Edges Only](http://bl.ocks.org/curran/daf6bc9db8b0a28e3973) from [curran](http://bl.ocks.org/curran)
    [Blocks Graph with Links](http://bl.ocks.org/curran/be4a45ec74357e7d9b10) from [curran](http://bl.ocks.org/curran)
    [Dynamic Size](http://bl.ocks.org/curran/db1e524cae5e4344b2e6) from [curran](http://bl.ocks.org/curran)
    * [all the blocks](http://bl.ocks.org/enjalot/6ac67b0d8ed673c9aa61) from [enjalot](http://bl.ocks.org/enjalot)
    * [Networks - Graphs 7](http://bl.ocks.org/emeeks/f2f6883ac7c965d09b90) from [emeeks](http://bl.ocks.org/emeeks)
    * [Blocks Graph](http://bl.ocks.org/curran/1da93bab4cdc708f41ae) from [curran](http://bl.ocks.org/curran)
    * [Blocks Graph Edges Only](http://bl.ocks.org/curran/daf6bc9db8b0a28e3973) from [curran](http://bl.ocks.org/curran)
    * [Blocks Graph with Links](http://bl.ocks.org/curran/be4a45ec74357e7d9b10) from [curran](http://bl.ocks.org/curran)
    * [Dynamic Size](http://bl.ocks.org/curran/db1e524cae5e4344b2e6) from [curran](http://bl.ocks.org/curran)



  10. @micahstubbs micahstubbs revised this gist Nov 23, 2015. No changes.
  11. @micahstubbs micahstubbs revised this gist Nov 23, 2015. 1 changed file with 0 additions and 0 deletions.
    Binary file added thumbnail.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  12. @micahstubbs micahstubbs revised this gist Nov 23, 2015. No changes.
  13. @micahstubbs micahstubbs revised this gist Nov 23, 2015. 3 changed files with 10236 additions and 0 deletions.
    19 changes: 19 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    a look at `README.md` files from [bl.ocks.org](http://bl.ocks.org/) that contain links to other bl.ocks

    click on a node to view that bl.ock

    community detection done with the [jLouvain](https://github.com/upphiminn/jLouvain) library

    source data and scripts used to generate the graph data are at [this repository](https://github.com/micahstubbs/readme-vis)

    a lineage of bl.ocks that informed this idea:

    [all the blocks](http://bl.ocks.org/enjalot/6ac67b0d8ed673c9aa61) from [enjalot](http://bl.ocks.org/enjalot)
    [Networks - Graphs 7](http://bl.ocks.org/emeeks/f2f6883ac7c965d09b90) from [emeeks](http://bl.ocks.org/emeeks)
    [Blocks Graph](http://bl.ocks.org/curran/1da93bab4cdc708f41ae) from [curran](http://bl.ocks.org/curran)
    [Blocks Graph Edges Only](http://bl.ocks.org/curran/daf6bc9db8b0a28e3973) from [curran](http://bl.ocks.org/curran)
    [Blocks Graph with Links](http://bl.ocks.org/curran/be4a45ec74357e7d9b10) from [curran](http://bl.ocks.org/curran)
    [Dynamic Size](http://bl.ocks.org/curran/db1e524cae5e4344b2e6) from [curran](http://bl.ocks.org/curran)



    376 changes: 376 additions & 0 deletions jsLouvain.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,376 @@
    /*
    Author: Corneliu S. (github.com/upphiminn)
    This is a javascript implementation of the Louvain
    community detection algorithm (http://arxiv.org/abs/0803.0476)
    Based on https://bitbucket.org/taynaud/python-louvain/overview
    */
    (function(){
    jLouvain = function(){
    //Constants
    var __PASS_MAX = -1
    var __MIN = 0.0000001

    //Local vars
    var original_graph_nodes;
    var original_graph_edges;
    var original_graph = {};
    var partition_init;

    //Helpers
    function make_set(array){
    var set = {};
    array.forEach(function(d,i){
    set[d] = true;
    });
    return Object.keys(set);
    };

    function obj_values(obj){
    var vals = [];
    for( var key in obj ) {
    if ( obj.hasOwnProperty(key) ) {
    vals.push(obj[key]);
    }
    }
    return vals;
    };

    function get_degree_for_node(graph, node){
    var neighbours = graph._assoc_mat[node] ? Object.keys(graph._assoc_mat[node]) : [];
    var weight = 0;
    neighbours.forEach(function(neighbour,i){
    var value = graph._assoc_mat[node][neighbour] || 1;
    if(node == neighbour)
    value *= 2;
    weight += value;
    });
    return weight;
    };

    function get_neighbours_of_node(graph, node){
    if(typeof graph._assoc_mat[node] == 'undefined')
    return [];

    var neighbours = Object.keys(graph._assoc_mat[node]);
    return neighbours;
    }


    function get_edge_weight(graph, node1, node2){
    return graph._assoc_mat[node1] ? graph._assoc_mat[node1][node2] : undefined;
    }

    function get_graph_size(graph){
    var size = 0;
    graph.edges.forEach(function(edge){
    size += edge.weight;
    });
    return size;
    }

    function add_edge_to_graph(graph, edge){
    update_assoc_mat(graph, edge);

    var edge_index = graph.edges.map(function(d){
    return d.source+'_'+d.target;
    }).indexOf(edge.source+'_'+edge.target);

    if(edge_index != -1)
    graph.edges[edge_index].weight = edge.weight;
    else
    graph.edges.push(edge);
    }

    function make_assoc_mat(edge_list){
    var mat = {};
    edge_list.forEach(function(edge, i){
    mat[edge.source] = mat[edge.source] || {};
    mat[edge.source][edge.target] = edge.weight;
    mat[edge.target] = mat[edge.target] || {};
    mat[edge.target][edge.source] = edge.weight;
    });

    return mat;
    }

    function update_assoc_mat(graph, edge){
    graph._assoc_mat[edge.source] = graph._assoc_mat[edge.source] || {};
    graph._assoc_mat[edge.source][edge.target] = edge.weight;
    graph._assoc_mat[edge.target] = graph._assoc_mat[edge.target] || {};
    graph._assoc_mat[edge.target][edge.source] = edge.weight;
    }

    function clone(obj){
    if(obj == null || typeof(obj) != 'object')
    return obj;

    var temp = obj.constructor();

    for(var key in obj)
    temp[key] = clone(obj[key]);
    return temp;
    }

    //Core-Algorithm Related
    function init_status(graph, status, part){
    status['nodes_to_com'] = {};
    status['total_weight'] = 0;
    status['internals'] = {};
    status['degrees'] = {};
    status['gdegrees'] = {};
    status['loops'] = {};
    status['total_weight'] = get_graph_size(graph);

    if(typeof part == 'undefined'){
    graph.nodes.forEach(function(node,i){
    status.nodes_to_com[node] = i;
    var deg = get_degree_for_node(graph, node);
    if (deg < 0)
    throw 'Bad graph type, use positive weights!';
    status.degrees[i] = deg;
    status.gdegrees[node] = deg;
    status.loops[node] = get_edge_weight(graph, node, node) || 0;
    status.internals[i] = status.loops[node];
    });
    }else{
    graph.nodes.forEach(function(node,i){
    var com = part[node];
    status.nodes_to_com[node] = com;
    var deg = get_degree_for_node(graph, node);
    status.degrees[com] = (status.degrees[com] || 0) + deg;
    status.gdegrees[node] = deg;
    var inc = 0.0;

    var neighbours = get_neighbours_of_node(graph, node);
    neighbours.forEach(function(neighbour, i){
    var weight = graph._assoc_mat[node][neighbour];
    if (weight <= 0){
    throw "Bad graph type, use positive weights";
    }

    if(part[neighbour] == com){
    if (neighbour == node){
    inc += weight;
    }else{
    inc += weight/2.0;
    }
    }
    });
    status.internals[com] = (status.internals[com] || 0) + inc;
    });
    }
    }

    function __modularity(status){
    var links = status.total_weight;
    var result = 0.0;
    var communities = make_set(obj_values(status.nodes_to_com));

    communities.forEach(function(com,i){
    var in_degree = status.internals[com] || 0 ;
    var degree = status.degrees[com] || 0 ;
    if(links > 0){
    result = result + in_degree / links - Math.pow((degree / (2.0*links)), 2);
    }
    });
    return result;
    }

    function __neighcom(node, graph, status){
    // compute the communities in the neighb. of the node, with the graph given by
    // node_to_com

    var weights = {};
    var neighboorhood = get_neighbours_of_node(graph, node);//make iterable;

    neighboorhood.forEach(function(neighbour, i){
    if(neighbour != node){
    var weight = graph._assoc_mat[node][neighbour] || 1;
    var neighbourcom = status.nodes_to_com[neighbour];
    weights[neighbourcom] = (weights[neighbourcom] || 0) + weight;
    }
    });

    return weights;
    }

    function __insert(node, com, weight, status){
    //insert node into com and modify status
    status.nodes_to_com[node] = +com;
    status.degrees[com] = (status.degrees[com] || 0) + (status.gdegrees[node]||0);
    status.internals[com] = (status.internals[com] || 0) + weight + (status.loops[node]||0);
    }

    function __remove(node, com, weight, status){
    //remove node from com and modify status
    status.degrees[com] = ((status.degrees[com] || 0) - (status.gdegrees[node] || 0));
    status.internals[com] = ((status.internals[com] || 0) - weight -(status.loops[node] ||0));
    status.nodes_to_com[node] = -1;
    }

    function __renumber(dict){
    var count = 0;
    var ret = clone(dict); //deep copy :)
    var new_values = {};
    var dict_keys = Object.keys(dict);
    dict_keys.forEach(function(key){
    var value = dict[key];
    var new_value = typeof new_values[value] =='undefined' ? -1 : new_values[value];
    if(new_value == -1){
    new_values[value] = count;
    new_value = count;
    count = count + 1;
    }
    ret[key] = new_value;
    });
    return ret;
    }

    function __one_level(graph, status){
    //Compute one level of the Communities Dendogram.
    var modif = true,
    nb_pass_done = 0,
    cur_mod = __modularity(status),
    new_mod = cur_mod;

    while (modif && nb_pass_done != __PASS_MAX){
    cur_mod = new_mod;
    modif = false;
    nb_pass_done += 1

    graph.nodes.forEach(function(node,i){
    var com_node = status.nodes_to_com[node];
    var degc_totw = (status.gdegrees[node] || 0) / (status.total_weight * 2.0);
    var neigh_communities = __neighcom(node, graph, status);
    __remove(node, com_node, (neigh_communities[com_node] || 0.0), status);
    var best_com = com_node;
    var best_increase = 0;
    var neigh_communities_entries = Object.keys(neigh_communities);//make iterable;

    neigh_communities_entries.forEach(function(com,i){
    var incr = neigh_communities[com] - (status.degrees[com] || 0.0) * degc_totw;
    if (incr > best_increase){
    best_increase = incr;
    best_com = com;
    }
    });

    __insert(node, best_com, neigh_communities[best_com] || 0, status);

    if(best_com != com_node)
    modif = true;
    });
    new_mod = __modularity(status);
    if(new_mod - cur_mod < __MIN)
    break;
    }
    }

    function induced_graph(partition, graph){
    var ret = {nodes:[], edges:[], _assoc_mat: {}};
    var w_prec, weight;
    //add nodes from partition values
    var partition_values = obj_values(partition);
    ret.nodes = ret.nodes.concat(make_set(partition_values)); //make set
    graph.edges.forEach(function(edge,i){
    weight = edge.weight || 1;
    var com1 = partition[edge.source];
    var com2 = partition[edge.target];
    w_prec = (get_edge_weight(ret, com1, com2) || 0);
    var new_weight = (w_prec + weight);
    add_edge_to_graph(ret, {'source': com1, 'target': com2, 'weight': new_weight});
    });
    return ret;
    }

    function partition_at_level(dendogram, level){
    var partition = clone(dendogram[0]);
    for(var i = 1; i < level + 1; i++ )
    Object.keys(partition).forEach(function(key,j){
    var node = key;
    var com = partition[key];
    partition[node] = dendogram[i][com];
    });
    return partition;
    }


    function generate_dendogram(graph, part_init){

    if(graph.edges.length == 0){
    var part = {};
    graph.nodes.forEach(function(node,i){
    part[node] = node;
    });
    return part;
    }
    var status = {};

    init_status(original_graph, status, part_init);
    var mod = __modularity(status);
    var status_list = [];
    __one_level(original_graph, status);
    var new_mod = __modularity(status);
    var partition = __renumber(status.nodes_to_com);
    status_list.push(partition);
    mod = new_mod;
    var current_graph = induced_graph(partition, original_graph);
    init_status(current_graph, status);

    while (true){
    __one_level(current_graph, status);
    new_mod = __modularity(status);
    if(new_mod - mod < __MIN)
    break;

    partition = __renumber(status.nodes_to_com);
    status_list.push(partition);

    mod = new_mod;
    current_graph = induced_graph(partition, current_graph);
    init_status(current_graph, status);
    }

    return status_list;
    }

    var core = function(){
    var status = {};
    var dendogram = generate_dendogram(original_graph, partition_init);
    return partition_at_level(dendogram, dendogram.length - 1);
    };

    core.nodes = function(nds){
    if(arguments.length > 0){
    original_graph_nodes = nds;
    }
    return core;
    };

    core.edges = function(edgs){
    if(typeof original_graph_nodes == 'undefined')
    throw 'Please provide the graph nodes first!';

    if(arguments.length > 0){
    original_graph_edges = edgs;
    var assoc_mat = make_assoc_mat(edgs);
    original_graph = { 'nodes': original_graph_nodes,
    'edges': original_graph_edges,
    '_assoc_mat': assoc_mat };
    }
    return core;

    };

    core.partition_init = function(prttn){
    if(arguments.length > 0){
    partition_init = prttn;
    }
    return core;
    };

    return core;
    }
    })();
    9,841 changes: 9,841 additions & 0 deletions readme-blocks-graph.json
    9,841 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
  14. @micahstubbs micahstubbs created this gist Nov 23, 2015.
    244 changes: 244 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,244 @@
    <html>
    <head>
    <title>Edge creation</title>
    <meta charset="utf-8" />
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script src="jsLouvain.js"></script>
    <style>
    /*body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }*/
    /*svg { width: 100%; height: 100%; }*/
    /* Make the chart container fill the page using CSS. */
    #viz {
    position: fixed;
    left: 0px;
    right: 0px;
    top: 0px;
    bottom: 0px;
    }
    </style>
    </head>
    <body>

    <div id="viz"></div>

    <script>

    // Extract the width and height that was computed by CSS.
    var vizDiv = document.getElementById("viz");
    var width = vizDiv.clientWidth;
    var height = vizDiv.clientHeight;
    var minSide = d3.min([width, height]);
    var xOffset = (width - minSide) / 2;
    var yOffset = (height - minSide) / 2;

    // Use the extracted size to set the size of an SVG element.
    var svg = d3.select(vizDiv).append("svg")
    .attr("width", width)
    .attr("height", height)
    .classed("main", true);

    d3.json("readme-blocks-graph.json", function(error,data) { createNetwork(data) });

    function onlyUnique(value, index, self) {
    return self.indexOf(value) === index;
    }

    function createNetwork(graphContainer) {
    var nodeHash = {};
    var nodes = [];
    var edges = [];

    var edgelist = graphContainer["graph"]["edges"];
    var nodelist = graphContainer["graph"]["nodes"];

    edgelist.forEach(function (edge) {
    if (!nodeHash[edge.source]) {
    nodeHash[edge.source] = {
    id: edge.source,
    label: edge.source
    };
    nodes.push(nodeHash[edge.source]);
    }
    if (!nodeHash[edge.target]) {
    nodeHash[edge.target] = {
    id: edge.target,
    label: edge.target
    };
    nodes.push(nodeHash[edge.target]);
    }
    if (edge/*.weight == 5*/) {
    edges.push({id: nodeHash[edge.source].id + "-" + nodeHash[edge.target].id, source: nodeHash[edge.source], target: nodeHash[edge.target], weight: 1/*edge.weight*/});
    }
    });

    // get some attributes from the nodelist (calculated elsewhere)
    // and store them in the nodeHash
    nodelist.forEach(function (node) {
    if(nodeHash[node.id]) {
    nodeHash[node.id]["user"] = node["user"];
    nodeHash[node.id]["createdAt"] = node["createdAt"];
    nodeHash[node.id]["updatedAt"] = node["updatedAt"];
    nodeHash[node.id]["description"] = node["description"];
    }
    })

    // take the attributes now in the nodeHash
    // and hang them on the nodes (calculated here from the edgelist)
    nodes.forEach(function (node) {
    if(nodeHash[node.id]) {
    node["user"] = nodeHash[node.id]["user"];
    node["createdAt"] = nodeHash[node.id]["createdAt"];
    node["updatedAt"] = nodeHash[node.id]["updatedAt"];
    node["description"] = nodeHash[node.id]["description"];
    }
    })

    console.log("nodes", nodes);
    console.log("edges", edges);

    createForceNetwork(nodes, edges);
    }

    function modularityCensus(nodes, edges, moduleHash) {
    edges.forEach(function (edge) {
    if (edge.source.module !== edge.target.module) {
    edge.border = true;
    }
    else {
    edge.border = false;
    }
    });
    nodes.forEach(function (node) {
    var theseEdges = edges.filter(function(d) {return d.source === node || d.target === node});

    var theseSourceModules = theseEdges.map(function (d) {return d.source.module}).filter(onlyUnique);
    var theseTargetModules = theseEdges.map(function (d) {return d.target.module}).filter(onlyUnique);

    if (theseSourceModules.length > 1 || theseTargetModules.length > 1) {
    node.border = true;
    }
    else {
    node.border = false;
    }
    });

    }

    function createForceNetwork(nodes, edges) {

    //create a network from an edgelist

    //var colors = d3.scale.ordinal().domain([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]).range(["#996666", "#66CCCC", "#FFFF99", "#CC9999", "#666633", "#993300", "#999966", "#660000", "#996699", "#cc6633", "#ff9966", "#339999", "#6699cc", "#ffcc66", "#ff6600", "#00ccccc"]);
    var colors = d3.scale.category20();

    var node_data = nodes.map(function (d) {return d.id});
    var edge_data = edges.map(function (d) {return {source: d.source.id, target: d.target.id, weight: 1}; });

    console.log("node_data for jLouvain", node_data);
    console.log("edge_data for jLouvain", edge_data);

    var community = jLouvain().nodes(node_data).edges(edge_data);
    var result = community();

    console.log("jLouvain result", result);

    nodes.forEach(function (node) {
    node.module = result[node.id]
    //console.log("node.module", node.module)
    });

    modularityCensus(nodes, edges, result);

    var force = d3.layout.force().nodes(nodes).links(edges)
    .size([minSide, minSide]) // make a square
    .charge(-100)
    .chargeDistance(100)
    .linkStrength(2)
    .linkDistance(3)
    .gravity(0.07);

    var edgeEnter = d3.select("svg.main").selectAll("g.edge")
    .data(edges, function (d) {return d.id})
    .enter()
    .append("g")
    .attr("class", "edge");

    edgeEnter
    .append("line")
    .style("stroke-width", "1px")
    .style("stroke", "gray")
    .style("pointer-events", "none");

    var nodeEnter = d3.select("svg.main").selectAll("g.node")
    .data(nodes, function (d) {return d.id})
    .enter();

    nodeEnter
    .append("a")
    .attr("xlink:href", function (d){
    return "http://bl.ocks.org/" + d.user + "/" + d.id;
    })
    //.attr("target", "_blank")
    .append("circle")
    .attr("r", 3)
    .attr("class", "foreground")
    .style("fill", function (d) {return colors(d.module)})
    .style("stroke-width", function (d) {return d.border ? "3px" : "1px"})

    /*
    // draw labels over nodes
    nodeEnter.append("text")
    .style("text-anchor", "middle")
    .attr("y", 3)
    .style("stroke-width", "1px")
    .style("stroke-opacity", 0.75)
    .style("stroke", "white")
    .style("font-size", "8px")
    .text(function (d) {return d.id})
    .style("pointer-events", "none")
    nodeEnter.append("text")
    .style("text-anchor", "middle")
    .attr("y", 3)
    .style("font-size", "8px")
    .text(function (d) {return d.id})
    .style("pointer-events", "none")
    */


    force.start();

    for(var i = 0; i < 200; i++){
    force.tick();
    }
    //force.on("tick", updateNetwork);

    // after 200 iterations, we say the network
    // has stabilized and we stop the
    // force layout physics simulation
    force.stop();
    // now we draw the network
    updateNetwork();

    function updateNetwork(e) {

    // draw the links
    d3.select("svg.main").selectAll("line")
    .attr("x1", function (d) {return d.source.x})
    .attr("y1", function (d) {return d.source.y})
    .attr("x2", function (d) {return d.target.x})
    .attr("y2", function (d) {return d.target.y})
    .attr("transform", "translate(" + xOffset + "," + yOffset +")");

    // draw the nodes
    d3.select("svg.main").selectAll("circle")
    .attr("cx", function (d) {return d.x; })
    .attr("cy", function (d) {return d.y; })
    .attr("transform", "translate(" + xOffset + "," + yOffset +")");

    }

    }
    </script>
    </body>
    </html>