Louvain method for community detection


Louvain method for community detection

#r "FSharp.FGL"
#r "FSharp.FGL.ArrayAdjacencyGraph"

open FSharp.FGL
open FSharp.FGL.ArrayAdjacencyGraph
open ArrayAdjacencyGraph.Algorithms

The louvain method for communty detection is a easy method to extract the community structure of large networks. It is based on the concept of modularity optimization. The algorithm is divided into two phases that are repeated iteratively:

  • Modularity optimization, in which every vertex is assigned its own community. After that, for each vertex its neighbours are used to calculate the gain of modularity by removing the vertex from its community and placing it in the community of a neighbour. The new commnuity for the vertex is the one with the biggest modularity gain. This is repeated until no furhter changes that increase the overall modularity can be done.
  • Community aggregation, in which the communities found are aggregated in order to build a new, reduced network of communities.

These steps are repeated until no further increase of modularity is possible.

Example

Example Graph

The first step of the tutorial is the creation of a graph that Louvain can analyse. In this case the graph was taken from wikipedia.

//Creating a list of labeled vertices
let vertexList : LVertex<int,string> list =
    [
        for i=0 to 13 do
            (i),(sprintf "%i"i)
    ]

//Creating a list of labeled edges
let edgeList : LEdge<int,float> list =
    [
        0,1,1.; 0,2,1.; 1,2,1.; 1,3,1.;2,3,1.; 3,4,1.; 3,13,1.; 4,5,1.; 4,8,1.; 
        5,6,1.; 5,8,1.; 5,7,1.; 6,7,1.; 7,8,1.; 7,10,1.; 8,9,1.; 9,10,1.; 
        9,12,1.; 9,13,1.; 10,11,1.; 10,13,1.; 11,12,1.; 11,13,1.; 12,13,1.
    ]

//Creating a graph out of the two lists 
let myGraph :ArrayAdjacencyGraph<int,string,float> =
    ArrayAdjacencyGraph(vertexList,edgeList)

Louvain algorithm

The application of the louvain algorithm on the example graph would look like this:

//Returns the graph with the louvain calculation on top of it
let myGraphLouvain : ArrayAdjacencyGraph<int,string*int,float> =
    Louvain.Louvain.louvain myGraph 0.00001

Visualization

The visualization of the graph is made possible by means of Cyjs.NET and looks like this. The different colors each represent their own community.

No value returned by any evaluator
Multiple items
Namespace FSharp

--------------------
Namespace Microsoft.FSharp
Namespace FSharp.FGL
Namespace FSharp.FGL.ArrayAdjacencyGraph
Multiple items
Namespace FSharp.FGL.ArrayAdjacencyGraph

--------------------
Namespace ArrayAdjacencyGraph

--------------------
type ArrayAdjacencyGraph<'Vertex,'Label,'Edge (requires equality and equality)> =
  private new : vertexEdges:Dictionary<'Vertex,LEdge<'Vertex,'Edge> []> * labels:Dictionary<'Vertex,'Label> -> ArrayAdjacencyGraph<'Vertex,'Label,'Edge> + 2 Überladungen
  member AddEdge : LEdge<'Vertex,'Edge> -> ArrayAdjacencyGraph<'Vertex,'Label,'Edge>
  member AddManyEdges : edgeArray:seq<LEdge<'Vertex,'Edge>> -> ArrayAdjacencyGraph<'Vertex,'Label,'Edge>
  member AddManyVertices : vertices:LVertex<'Vertex,'Label> [] -> ArrayAdjacencyGraph<'Vertex,'Label,'Edge>
  member AddVertex : LVertex<'Vertex,'Label> -> ArrayAdjacencyGraph<'Vertex,'Label,'Edge>
  member private AdjacencyGraph : unit -> Dictionary<'Vertex,LEdge<'Vertex,'Edge> []>
  member ConnectedEdgesEmpty : v:'Vertex -> bool
  member ContainsEdge : edge:LEdge<'Vertex,'Edge> -> bool
  member ContainsVertex : vertex:'Vertex -> bool
  member Copy : unit -> ArrayAdjacencyGraph<'Vertex,'Label,'Edge>
  ...

--------------------
new : unit -> ArrayAdjacencyGraph<'Vertex,'Label,'Edge>
new : vertices:LVertex<'Vertex,'Label> list * edges:LEdge<'Vertex,'Edge> list -> ArrayAdjacencyGraph<'Vertex,'Label,'Edge>
Namespace ArrayAdjacencyGraph.Algorithms
val vertexList : LVertex<int,string> list
type LVertex<'Vertex,'Label> = 'Vertex * 'Label
Multiple items
val int : value:'T -> int (requires member op_Explicit)

--------------------
[<Struct>]
type int = int32

--------------------
type int<'Measure> =
  int
Multiple items
val string : value:'T -> string

--------------------
type string = System.String
type 'T list = List<'T>
val i : int
val sprintf : format:Printf.StringFormat<'T> -> 'T
val edgeList : LEdge<int,float> list
type LEdge<'Vertex,'Edge> = 'Vertex * 'Vertex * 'Edge
Multiple items
val float : value:'T -> float (requires member op_Explicit)

--------------------
[<Struct>]
type float = System.Double

--------------------
type float<'Measure> =
  float
val myGraph : ArrayAdjacencyGraph<int,string,float>
val myGraphLouvain : ArrayAdjacencyGraph<int,(string * int),float>
Namespace ArrayAdjacencyGraph.Algorithms.Louvain
Modul Louvain

aus ArrayAdjacencyGraph.Algorithms.Louvain
val louvain : graph:ArrayAdjacencyGraph<'Vertex,'Label,float> -> modularityIncreaseThreshold:float -> ArrayAdjacencyGraph<'Vertex,('Label * int),float> (requires equality)
Namespace Cyjs
Namespace Cyjs.NET
val toCyJS : g:ArrayAdjacencyGraph<int,(string * int),float> -> CyGraph.CyGraph
val g : ArrayAdjacencyGraph<int,(string * int),float>
val vertices : Elements.Node []
Modul Array

aus Microsoft.FSharp.Collections
val map2 : mapping:('T1 -> 'T2 -> 'U) -> array1:'T1 [] -> array2:'T2 [] -> 'U []
val vertex : int
val label : string * int
member ArrayAdjacencyGraph.GetVertices : unit -> 'Vertex []
member ArrayAdjacencyGraph.GetLabels : unit -> 'Label []
val map : mapping:('T -> 'U) -> array:'T [] -> 'U []
val id : int
val ogLabel : string
val newLabel : int
Modul Elements

aus Cyjs.NET
val node : id:string -> dataAttributes:CyParam.CyStyleParam list -> Elements.Node
Modul CyParam

aus Cyjs.NET
val label : v:'a -> CyParam.CyStyleParam
val color : v:'a -> CyParam.CyStyleParam
val edges : Elements.Edge list
Multiple items
Modul List

aus Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
    interface IReadOnlyList<'T>
    interface IReadOnlyCollection<'T>
    interface IEnumerable
    interface IEnumerable<'T>
    member GetReverseIndex : rank:int * offset:int -> int
    member GetSlice : startIndex:int option * endIndex:int option -> 'T list
    static member Cons : head:'T * tail:'T list -> 'T list
    member Head : 'T
    member IsEmpty : bool
    member Item : index:int -> 'T with get
    ...
val map : mapping:('T -> 'U) -> list:'T list -> 'U list
val v1 : int
val v2 : int
val weight : float
val edge : id:string -> sourceId:string -> targetId:string -> dataAttributes:CyParam.CyStyleParam list -> Elements.Edge
val weight : v:'a -> CyParam.CyStyleParam
Modul CyGraph

aus Cyjs.NET
val initEmpty : unit -> CytoscapeModel.Cytoscape
val withElements : elems:seq<CytoscapeModel.Element> -> cy:CyGraph.CyGraph -> CyGraph.CyGraph
val withStyle : selector:string -> cyStyles:seq<CyParam.CyStyleParam> -> cy:CyGraph.CyGraph -> CyGraph.CyGraph
val content : v:'a -> CyParam.CyStyleParam
Modul Text

aus Cyjs.NET.CyParam
Modul Outline

aus Cyjs.NET.CyParam.Text
Modul Background

aus Cyjs.NET.CyParam
val withSize : width:int * height:int -> cy:CyGraph.CyGraph -> CyGraph.CyGraph
val withLayout : ly:Layout -> cy:CyGraph.CyGraph -> CyGraph.CyGraph
Multiple items
Modul Layout

aus Cyjs.NET

--------------------
type Layout =
  inherit DynamicObj
  new : name:string -> Layout
  member name : string

--------------------
new : name:string -> Layout
val initCose : applyOption:(Layout -> Layout) -> Layout
val id : x:'T -> 'T
Modul HTML

aus Cyjs.NET
val toEmbeddedHTML : cy:CytoscapeModel.Cytoscape -> string