Yog.FSharp
Getting Started Examples API Reference GitHub

Centrality Analysis of a Social Network

This example demonstrates how to identify the most important people in a social network using multiple centrality measures: Degree, Closeness, Betweenness, PageRank, and Eigenvector.

Problem

Given a small network of friends, determine who is the most "central" or influential using different definitions of importance.

#I "../../src/Yog.FSharp/bin/Debug/net10.0"
#r "Yog.FSharp.dll"

open Yog.Model
open Yog.Centrality

// Build a small social network
// 0=Alice, 1=Bob, 2=Carol, 3=Dave, 4=Eve
let network =
    empty Undirected
    |> addNode 0 "Alice" |> addNode 1 "Bob" |> addNode 2 "Carol"
    |> addNode 3 "Dave"  |> addNode 4 "Eve"
    |> addEdge 0 1 1  // Alice - Bob
    |> addEdge 0 2 1  // Alice - Carol
    |> addEdge 1 2 1  // Bob - Carol
    |> addEdge 1 3 1  // Bob - Dave
    |> addEdge 3 4 1  // Dave - Eve

let names = Map.ofList [(0,"Alice"); (1,"Bob"); (2,"Carol"); (3,"Dave"); (4,"Eve")]

let printRanking title (scores: Map<int, float>) =
    printfn "\n=== %s ===" title
    scores |> Map.toList |> List.sortByDescending snd
    |> List.iter (fun (id, score) -> printfn "  %s: %.4f" names.[id] score)

Running the Analysis

// 1. Degree Centrality - who has the most connections?
degree TotalDegree network |> printRanking "Degree Centrality"

// 2. Closeness Centrality - who can reach everyone fastest?
closenessInt network |> printRanking "Closeness Centrality"

// 3. Betweenness Centrality - who sits on the most shortest paths?
betweennessInt network |> printRanking "Betweenness Centrality"

// 4. PageRank - who is connected to other important people?
pagerank defaultPageRankOptions network |> printRanking "PageRank"

// 5. Eigenvector Centrality - whose neighbors are themselves important?
eigenvector 100 0.0001 network |> printRanking "Eigenvector Centrality"

Output

=== Degree Centrality ===
  Bob: 0.7500
  Alice: 0.5000
  Carol: 0.5000
  Dave: 0.5000
  Eve: 0.2500

=== Closeness Centrality ===
  Bob: 0.8000
  Dave: 0.6667
  Alice: 0.5714
  Carol: 0.5714
  Eve: 0.4444

=== Betweenness Centrality ===
  Bob: 4.0000
  Dave: 3.0000
  Alice: 0.0000
  Carol: 0.0000
  Eve: 0.0000

=== PageRank ===
  Bob: 0.2834
  Dave: 0.2126
  Alice: 0.1918
  Carol: 0.1918
  Eve: 0.1204

=== Eigenvector Centrality ===
  Bob: 0.6037
  Alice: 0.4971
  Carol: 0.4971
  Dave: 0.3425
  Eve: 0.1547

Interpretation

Bob is consistently the most central, but Dave's high betweenness reveals his role as the sole bridge to Eve — a pattern not captured by degree alone.

val network: obj
val names: Map<int,string>
Multiple items
module Map from Microsoft.FSharp.Collections

--------------------
type Map<'Key,'Value (requires comparison)> = interface IReadOnlyDictionary<'Key,'Value> interface IReadOnlyCollection<KeyValuePair<'Key,'Value>> interface IEnumerable interface IStructuralEquatable interface IComparable interface IEnumerable<KeyValuePair<'Key,'Value>> interface ICollection<KeyValuePair<'Key,'Value>> interface IDictionary<'Key,'Value> new: elements: ('Key * 'Value) seq -> Map<'Key,'Value> member Add: key: 'Key * value: 'Value -> Map<'Key,'Value> ...

--------------------
new: elements: ('Key * 'Value) seq -> Map<'Key,'Value>
val ofList: elements: ('Key * 'T) list -> Map<'Key,'T> (requires comparison)
val printRanking: title: string -> scores: Map<int,float> -> unit
val title: string
val scores: Map<int,float>
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
Multiple items
val float: value: 'T -> float (requires member op_Explicit)

--------------------
type float = System.Double

--------------------
type float<'Measure> = float
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
val toList: table: Map<'Key,'T> -> ('Key * 'T) list (requires comparison)
Multiple items
module List from Microsoft.FSharp.Collections

--------------------
type List<'T> = | op_Nil | op_ColonColon 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 with get member IsEmpty: bool with get member Item: index: int -> 'T with get ...
val sortByDescending: projection: ('T -> 'Key) -> list: 'T list -> 'T list (requires comparison)
val snd: tuple: ('T1 * 'T2) -> 'T2
val iter: action: ('T -> unit) -> list: 'T list -> unit
val id: int
val score: float