Graph centrality is a concept used in network analysis to identify and measure the relative importance or significance of nodes within a graph.
It helps us understand which nodes play a central role in the network, indicating their influence, importance, or prominence.
Centrality measures can be applied to various types of networks, including social networks, transportation networks, communication networks, and more.
Step 1 is the creation of a graph example where we can visualise the graph centrality.
We will visualise the graph via Cytopscape
open Graphoscope
open Cytoscape.NET
open FSharpAux
let centralityEdges =
seq{
1,1,2,2,1.
1,1,4,4,1.
1,1,5,5,1.
1,1,6,6,1.
2,2,3,3,1.
}
let centralityGraph = AdjGraph.ofSeq centralityEdges
let renderCyGraph (nodeLabelF:int -> CyParam.CyStyleParam ) =
CyGraph.initEmpty ()
|> CyGraph.withElements [
for (sk,s,tk,t,el) in (AdjGraph.toSeq centralityGraph) do
let sk, tk = (string sk), (string tk)
yield Elements.node sk [ nodeLabelF s ]
yield Elements.node tk [ nodeLabelF t ]
yield Elements.edge (sprintf "%s_%s" sk tk) sk tk [ ]
]
|> CyGraph.withStyle "node"
[
CyParam.content =. CyParam.label
CyParam.color "#A00975"
]
|> CyGraph.withStyle "edge"
[
CyParam.content =. CyParam.label
CyParam.Curve.style "bezier"
CyParam.color "#438AFE"
]
|> CyGraph.withLayout (
Layout.initBreadthfirst(Layout.LayoutOptions.Generic())
)
|> CyGraph.withZoom(CytoscapeModel.Zoom.Init(ZoomingEnabled=false))
|> CyGraph.withSize(800, 400)
|> Cytoscape.NET.HTML.toGraphHTML()
renderCyGraph (fun x -> CyParam.label $"Node: {x}")
Closeness centrality assesses how quickly a node can reach all other nodes in the network.
Nodes with high closeness centrality are considered central because they are close to many other nodes in terms of geodesic distance (the shortest path).
let closenessCentrality =
Measures.ClosenessCentrality.computeWithEdgeData centralityGraph
renderCyGraph (fun x -> CyParam.label ($"Node: {x};Closeness: {((closenessCentrality.Item x)|>Math.round 3)}"))
Betweenness centrality measures how often a node lies on the shortest path between pairs of other nodes.
Nodes with high betweenness centrality act as bridges or intermediaries in the network.
let betweenness =
Measures.BetweennessCentrality.computeWithEdgeData centralityGraph
renderCyGraph (fun x -> CyParam.label ($"Node: {x};Betweenness: {betweenness.Item x}"))
Node eccentricity is a concept used in graph theory and network analysis to measure the centrality or importance of a node within a graph.
It quantifies how far a node is from the farthest other node in the network in terms of the shortest path length.
In other words, it represents the maximum distance between a node and any other node in the graph.
let eccentricity (node:'NodeKey) =
Measures.Eccentricity.computeOfNodeWithEdgeData centralityGraph node
renderCyGraph (fun x -> CyParam.label ($"Node: {x};Eccentricity: {eccentricity x}"))
No value returned by any evaluator
|
Another important metric to take into account involves statistics related to all the shortest paths within a graph.
These statistics encompass the Diameter (which represents the longest among the shortest paths), the Radius (representing the shortest of the shortest paths), and the average path length.
As these metrics rely on information from all the shortest paths within a graph, they are fundamentally derived from the results of the Floyd-Warshall algorithm for calculating shortest paths.
Therefore it is wise to calculate this once and reuse it for the calulations.
let diameter =
Measures.Diameter.ofAdjGraph id centralityGraph
let radius =
Measures.Radius.ofAdjGraph id centralityGraph
"The given graph has a diameter of 3 and a radius of 2."
|
namespace Graphoscope
namespace Cytoscape
namespace Cytoscape.NET
namespace FSharpAux
val centralityEdges: seq<int * int * int * int * float>
Multiple items
val seq: sequence: seq<'T> -> seq<'T>
<summary>Builds a sequence using sequence expression syntax</summary>
<param name="sequence">The input sequence.</param>
<returns>The result sequence.</returns>
<example id="seq-cast-example"><code lang="fsharp">
seq { for i in 0..10 do yield (i, i*i) }
</code></example>
--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>
<summary>An abbreviation for the CLI type <see cref="T:System.Collections.Generic.IEnumerable`1" /></summary>
<remarks>
See the <see cref="T:Microsoft.FSharp.Collections.SeqModule" /> module for further operations related to sequences.
See also <a href="https://docs.microsoft.com/dotnet/fsharp/language-reference/sequences">F# Language Guide - Sequences</a>.
</remarks>
val centralityGraph: AdjGraph<int,int,float>
Multiple items
module AdjGraph
from Graphoscope
--------------------
type AdjGraph =
new: unit -> AdjGraph
static member addEdge: sourceKey: 'NodeKey -> targetKey: 'NodeKey -> data: 'EdgeData -> graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison)
static member addEdges: edgeSeq: seq<'NodeKey * 'NodeKey * 'EdgeData> -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison)
static member addElement: nk1: 'NodeKey -> nd1: 'NodeData -> nk2: 'NodeKey -> nd2: 'NodeData -> ed: 'EdgeData -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison)
static member addNode: nk: 'NodeKey -> nd: 'NodeData -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison)
static member addNodes: nodeSeq: seq<'NodeKey * 'NodeData> -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison)
static member containsEdge: v1: 'NodeKey -> v2: 'NodeKey -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> bool (requires comparison)
static member containsNode: vk: 'NodeKey -> g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> bool (requires comparison)
static member countEdges: graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> int (requires comparison)
static member countNodes: g: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> int (requires comparison)
...
<summary>
Functions to operate on the AdjGraph representation
</summary>
--------------------
type AdjGraph<'NodeKey,'NodeData,'EdgeData (requires comparison)> = System.Collections.Generic.Dictionary<'NodeKey,('NodeData * System.Collections.Generic.Dictionary<'NodeKey,'EdgeData>)>
<summary>
Basic Adjacency Graph representation
</summary>
--------------------
new: unit -> AdjGraph
static member AdjGraph.ofSeq: edgelist: seq<'NodeKey * 'NodeData * 'NodeKey * 'NodeData * 'EdgeData> -> AdjGraph<'NodeKey,'NodeData,'EdgeData> (requires comparison)
val renderCyGraph: nodeLabelF: (int -> CyParam.CyStyleParam) -> string
val nodeLabelF: (int -> CyParam.CyStyleParam)
Multiple items
val int: value: 'T -> int (requires member op_Explicit)
<summary>Converts the argument to signed 32-bit integer. This is a direct conversion for all
primitive numeric types. For strings, the input is converted using <c>Int32.Parse()</c>
with InvariantCulture settings. Otherwise the operation requires an appropriate
static conversion method on the input type.</summary>
<param name="value">The input value.</param>
<returns>The converted int</returns>
<example id="int-example"><code lang="fsharp"></code></example>
--------------------
[<Struct>]
type int = int32
<summary>An abbreviation for the CLI type <see cref="T:System.Int32" />.</summary>
<category>Basic Types</category>
--------------------
type int<'Measure> =
int
<summary>The type of 32-bit signed integer numbers, annotated with a unit of measure. The unit
of measure is erased in compiled code and when values of this type
are analyzed using reflection. The type is representationally equivalent to
<see cref="T:System.Int32" />.</summary>
<category>Basic Types with Units of Measure</category>
module CyParam
from Cytoscape.NET
type CyStyleParam =
{
Name: string
Value: obj
}
module CyGraph
from Cytoscape.NET
val initEmpty: unit -> CyGraph.CyGraph
val withElements: elems: seq<CytoscapeModel.Element> -> cy: CyGraph.CyGraph -> CyGraph.CyGraph
val sk: int
val s: int
val tk: int
val t: int
val el: float
static member AdjGraph.toSeq: graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> seq<'NodeKey * 'NodeData * 'NodeKey * 'NodeData * 'EdgeData> (requires comparison and comparison and comparison)
val sk: string
val tk: string
Multiple items
val string: value: 'T -> string
<summary>Converts the argument to a string using <c>ToString</c>.</summary>
<remarks>For standard integer and floating point values the and any type that implements <c>IFormattable</c><c>ToString</c> conversion uses <c>CultureInfo.InvariantCulture</c>. </remarks>
<param name="value">The input value.</param>
<returns>The converted string.</returns>
<example id="string-example"><code lang="fsharp"></code></example>
--------------------
type string = System.String
<summary>An abbreviation for the CLI type <see cref="T:System.String" />.</summary>
<category>Basic Types</category>
module Elements
from Cytoscape.NET
val node: id: string -> dataAttributes: CyParam.CyStyleParam list -> Elements.Node
val edge: id: string -> sourceId: string -> targetId: string -> dataAttributes: CyParam.CyStyleParam list -> Elements.Edge
val sprintf: format: Printf.StringFormat<'T> -> 'T
<summary>Print to a string using the given format.</summary>
<param name="format">The formatter.</param>
<returns>The formatted result.</returns>
<example>See <c>Printf.sprintf</c> (link: <see cref="M:Microsoft.FSharp.Core.PrintfModule.PrintFormatToStringThen``1" />) for examples.</example>
val withStyle: selector: string -> cyStyles: seq<CyParam.CyStyleParam> -> cy: CyGraph.CyGraph -> CyGraph.CyGraph
val content: v: 'a -> CyParam.CyStyleParam
val label: v: 'a -> CyParam.CyStyleParam
val color: v: 'a -> CyParam.CyStyleParam
module Curve
from Cytoscape.NET.CyParam
val style: v: 'a -> CyParam.CyStyleParam
val withLayout: ly: Layout -> cy: CyGraph.CyGraph -> CyGraph.CyGraph
Multiple items
module Layout
from Cytoscape.NET
--------------------
type Layout =
inherit DynamicObj
new: name: string -> Layout
member name: string
<summary>
Layout type inherits from dynamic object
</summary>
--------------------
new: name: string -> Layout
val initBreadthfirst: applyOption: (Layout -> Layout) -> Layout
<summary>
initializes a layout of type "breadthfirst" applying the givin layout option function.
The "breadthfirst" layout puts nodes in a hierarchy, based on a breadthfirst traversal of the graph. It is best suited to trees
and forests in its default top-down mode, and it is best suited to DAGs in its circle mode.
</summary>
Multiple items
type LayoutOptions =
new: unit -> LayoutOptions
static member Cose: ?Refresh: int * ?BoundingBox: 'a0 * ?NodeDimensionsIncludeLabels: bool * ?Randomize: bool * ?ComponentSpacing: int * ?NodeRepulsion: 'a1 * ?NodeOverlap: int * ?IdealEdgeLength: 'a2 * ?EdgeElasticity: 'a3 * ?NestingFactor: float * ?Gravity: int * ?NumIter: int * ?InitialTemp: int * ?CoolingFactor: float * ?MinTemp: float -> ('L -> 'L) (requires 'L :> Layout)
static member Generic: ?Positions: 'a0 * ?Zoom: 'a1 * ?Pan: 'a2 * ?Fit: bool * ?Padding: int * ?Animate: bool * ?AnimationDuration: int * ?AnimationEasing: 'a3 * ?AnimateFilter: 'a4 * ?AnimationThreshold: int * ?Ready: 'a5 * ?Stop: 'a6 * ?Transform: 'a7 -> ('L -> 'L) (requires 'L :> Layout)
<summary>
Functions provide the options of the Layout objects
</summary>
--------------------
new: unit -> Layout.LayoutOptions
static member Layout.LayoutOptions.Generic: ?Positions: 'a0 * ?Zoom: 'a1 * ?Pan: 'a2 * ?Fit: bool * ?Padding: int * ?Animate: bool * ?AnimationDuration: int * ?AnimationEasing: 'a3 * ?AnimateFilter: 'a4 * ?AnimationThreshold: int * ?Ready: 'a5 * ?Stop: 'a6 * ?Transform: 'a7 -> ('L -> 'L) (requires 'L :> Layout)
val withZoom: zoom: CytoscapeModel.Zoom -> cy: CyGraph.CyGraph -> CyGraph.CyGraph
namespace Cytoscape.NET.CytoscapeModel
Multiple items
type Zoom =
inherit DynamicObj
new: unit -> Zoom
static member Init: ?Level: float * ?Position: (int * int) * ?RenderedPosition: (int * int) * ?ZoomingEnabled: bool -> Zoom
static member Update: ?Level: float * ?Position: (int * int) * ?RenderedPosition: (int * int) * ?ZoomingEnabled: bool -> (Zoom -> Zoom)
--------------------
new: unit -> CytoscapeModel.Zoom
static member CytoscapeModel.Zoom.Init: ?Level: float * ?Position: (int * int) * ?RenderedPosition: (int * int) * ?ZoomingEnabled: bool -> CytoscapeModel.Zoom
val withSize: width: int * height: int -> cy: CyGraph.CyGraph -> CyGraph.CyGraph
type HTML =
static member CreateGraphHTML: graphData: string * zooming: string * divId: string * cytoscapeReference: CytoscapeJSReference * ?Width: int * ?Height: int -> XmlNode list
static member CreateGraphScript: graphData: string * zooming: string * cytoscapeReference: CytoscapeJSReference -> XmlNode
static member Doc: graphHTML: XmlNode list * cytoscapeReference: CytoscapeJSReference * ?AdditionalHeadTags: XmlNode list -> XmlNode
static member show: cy: Cytoscape * ?DisplayOpts: DisplayOptions -> unit
static member toEmbeddedHTML: ?DisplayOpts: DisplayOptions -> (Cytoscape -> string)
static member toGraphHTML: ?DisplayOpts: DisplayOptions -> (Cytoscape -> string)
static member toGraphHTMLNodes: ?DisplayOpts: DisplayOptions -> (Cytoscape -> XmlNode list)
<summary>
HTML template for Cytoscape
</summary>
static member HTML.toGraphHTML: ?DisplayOpts: DisplayOptions -> (CytoscapeModel.Cytoscape -> string)
val x: int
val closenessCentrality: System.Collections.Generic.Dictionary<int,float>
namespace Graphoscope.Measures
Multiple items
type ClosenessCentrality =
new: unit -> ClosenessCentrality
static member computWithEdgeDataBy: weightF: ('EdgeData -> float) * graph: FGraph<'NodeKey,'NodeData,'EdgeData> -> Dictionary<'NodeKey,float> (requires comparison)
static member compute: graph: FGraph<'NodeKey,'NodeData,'EdgeData> -> Dictionary<'NodeKey,float> (requires comparison) + 1 overload
static member computeNormalised: graph: FGraph<'NodeKey,'NodeData,'EdgeData> -> Dictionary<'NodeKey,float> (requires comparison) + 1 overload
static member computeNormalisedWithEdgeData: graph: FGraph<'NodeKey,'NodeData,float> -> Dictionary<'NodeKey,float> (requires comparison) + 1 overload
static member computeNormalisedWithEdgeDataBy: weightF: ('EdgeData -> float) * graph: FGraph<'NodeKey,'NodeData,'EdgeData> -> Dictionary<'NodeKey,float> (requires comparison) + 1 overload
static member computeWithEdgeData: graph: FGraph<'NodeKey,'NodeData,float> -> Dictionary<'NodeKey,float> (requires comparison) + 1 overload
static member computeWithEdgeDataBy: weightF: ('EdgeData -> float) * graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> Dictionary<'NodeKey,float> (requires comparison and comparison and comparison)
static member ofAdjGraph: getEdgeWeightF: ('EdgeData -> float) -> graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> Dictionary<'NodeKey,float> (requires comparison and comparison and comparison)
static member ofAdjGraphNode: getEdgeWeightF: ('EdgeData -> float) -> graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> nodeKey: 'NodeKey -> float (requires comparison and comparison and comparison)
...
--------------------
new: unit -> Measures.ClosenessCentrality
static member Measures.ClosenessCentrality.computeWithEdgeData: graph: AdjGraph<'NodeKey,'NodeData,float> -> System.Collections.Generic.Dictionary<'NodeKey,float> (requires comparison and comparison)
static member Measures.ClosenessCentrality.computeWithEdgeData: graph: FGraph<'NodeKey,'NodeData,float> -> System.Collections.Generic.Dictionary<'NodeKey,float> (requires comparison)
property System.Collections.Generic.Dictionary.Item: int -> float with get, set
module Math
from FSharpAux
val round: digits: int -> x: float -> float
<summary>
Rounds a double-precision floating-point value to a specified number of fractional digits.
</summary>
val betweenness: System.Collections.Generic.Dictionary<int,float>
Multiple items
type BetweennessCentrality =
new: unit -> BetweennessCentrality
static member compute: graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> Dictionary<'NodeKey,float> (requires comparison and comparison and comparison)
static member computeWithEdgeData: graph: AdjGraph<'NodeKey,'NodeData,float> -> Dictionary<'NodeKey,float> (requires comparison and comparison)
static member computeWithEdgeDataBy: getEdgeWeight: ('EdgeData -> float) * graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> Dictionary<'NodeKey,float> (requires comparison and comparison and comparison)
static member ofAdjGraph: getEdgeWeight: ('EdgeData -> float) -> graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> Dictionary<'NodeKey,float> (requires comparison and comparison and comparison)
--------------------
new: unit -> Measures.BetweennessCentrality
static member Measures.BetweennessCentrality.computeWithEdgeData: graph: AdjGraph<'NodeKey,'NodeData,float> -> System.Collections.Generic.Dictionary<'NodeKey,float> (requires comparison and comparison)
val eccentricity: node: 'NodeKey -> 'a
val node: 'NodeKey
Multiple items
type Eccentricity =
new: unit -> Eccentricity
static member compute: graph: FGraph<'NodeKey,'NodeData,'EdgeData> -> seq<'NodeKey * float> (requires comparison) + 1 overload
static member computeOfNode: graph: FGraph<'NodeKey,'NodeData,'EdgeData> * nodeKey: 'NodeKey -> float (requires comparison) + 1 overload
static member computeOfNodeWithEdgeData: graph: FGraph<'NodeKey,'NodeData,float> * nodeKey: 'NodeKey -> float (requires comparison) + 1 overload
static member computeOfNodeWithEdgeDataBy: getEdgeWeightF: ('EdgeData -> float) * graph: FGraph<'NodeKey,'NodeData,'EdgeData> * nodeKey: 'NodeKey -> float (requires comparison) + 1 overload
static member computeWithEdgeData: graph: FGraph<'NodeKey,'NodeData,float> -> seq<'NodeKey * float> (requires comparison) + 1 overload
static member computeWithEdgeDataBy: getEdgeWeightF: ('EdgeData -> float) * graph: FGraph<'NodeKey,'NodeData,'EdgeData> -> seq<'NodeKey * float> (requires comparison) + 1 overload
static member ofAdjGraph: getEdgeWeightF: ('EdgeData -> float) -> graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> seq<'NodeKey * float> (requires comparison and comparison and comparison)
static member ofAdjGraphNode: getEdgeWeightF: ('EdgeData -> float) -> graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> nodeKey: 'NodeKey -> float (requires comparison and comparison and comparison)
static member ofFGraph: getEdgeWeightF: ('EdgeData -> float) -> graph: FGraph<'NodeKey,'NodeData,'EdgeData> -> seq<'NodeKey * float> (requires comparison)
...
--------------------
new: unit -> Measures.Eccentricity
static member Measures.Eccentricity.computeOfNodeWithEdgeData: graph: AdjGraph<'NodeKey,'NodeData,float> * nodeKey: 'NodeKey -> float (requires comparison and comparison)
static member Measures.Eccentricity.computeOfNodeWithEdgeData: graph: FGraph<'NodeKey,'NodeData,float> * nodeKey: 'NodeKey -> float (requires comparison)
val diameter: float
Multiple items
type Diameter =
new: unit -> Diameter
static member compute: graph: FGraph<'NodeKey,'NodeData,'EdgeData> -> float (requires comparison) + 1 overload
static member computeWithEdgeData: graph: FGraph<'NodeKey,'NodeData,float> -> float (requires comparison) + 1 overload
static member computeWithEdgeDataBy: weigthF: ('EdgeData -> float) * graph: FGraph<'NodeKey,'NodeData,'EdgeData> -> float (requires comparison) + 1 overload
static member ofAdjGraph: weigthF: ('EdgeData -> float) -> graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> float (requires comparison and comparison and comparison)
static member ofFGraph: weigthF: ('EdgeData -> float) -> graph: FGraph<'NodeKey,'NodeData,'EdgeData> -> float (requires comparison)
static member ofGraph2D: floydWarshall: float[,] -> float
--------------------
new: unit -> Measures.Diameter
static member Measures.Diameter.ofAdjGraph: weigthF: ('EdgeData -> float) -> graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> float (requires comparison and comparison and comparison)
val id: x: 'T -> 'T
<summary>The identity function</summary>
<param name="x">The input value.</param>
<returns>The same value.</returns>
<example id="id-example"><code lang="fsharp">
id 12 // Evaulates to 12
id "abc" // Evaulates to "abc"
</code></example>
val radius: float
Multiple items
type Radius =
new: unit -> Radius
static member compute: graph: FGraph<'NodeKey,'NodeData,'EdgeData> -> float (requires comparison) + 1 overload
static member computeWithEdgeData: graph: FGraph<'NodeKey,'NodeData,float> -> float (requires comparison) + 1 overload
static member computeWithEdgeDataBy: weightF: ('EdgeData -> float) * graph: FGraph<'NodeKey,'NodeData,'EdgeData> -> float (requires comparison) + 1 overload
static member ofAdjGraph: weigthF: ('EdgeData -> float) -> graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> float (requires comparison and comparison and comparison)
static member ofFGraph: weigthF: ('EdgeData -> float) -> graph: FGraph<'NodeKey,'NodeData,'EdgeData> -> float (requires comparison)
static member ofGraph2D: floydWarshall: float[,] -> float
--------------------
new: unit -> Measures.Radius
static member Measures.Radius.ofAdjGraph: weigthF: ('EdgeData -> float) -> graph: AdjGraph<'NodeKey,'NodeData,'EdgeData> -> float (requires comparison and comparison and comparison)