Sankey Charts in Fsharp

How to make sankey charts in F# with Plotly.


Basic Sankey Diagram

In [1]:
#r "nuget: Plotly.NET,2.0.0-preview.8"
#r "nuget: Plotly.NET.Interactive,2.0.0-preview.8"
#r "nuget: FSharp.Data"
Installed Packages
  • FSharp.Data, 4.2.3
  • Plotly.NET, 2.0.0-preview.8
  • Plotly.NET.Interactive, 2.0.0-preview.8

Loading extensions from Plotly.NET.Interactive.dll

Added Kernel Extension including formatters for Plotly.NET charts.

In [2]:
open Plotly.NET

let nodes = [|
 Node.Create("A1")
 Node.Create("A2")
 Node.Create("B1")
 Node.Create("B2")
 Node.Create("C1")
 Node.Create("C2")
|]

Chart.Sankey(
    nodePadding = 15.,    
    nodeColor = "blue",
    nodeThickness = 20.0,
    nodeLineWidth = 0.5,
    nodes = (nodes |> Seq.ofArray),
    links = [
        Link.Create(src = nodes.[0], tgt = nodes.[2], value = 8.)
        Link.Create(src = nodes.[1], tgt = nodes.[3], value = 4.)
        Link.Create(src = nodes.[0], tgt = nodes.[3], value = 3.)
        Link.Create(src = nodes.[2], tgt = nodes.[4], value = 8.)
        Link.Create(src = nodes.[3], tgt = nodes.[4], value = 4.)
        Link.Create(src = nodes.[3], tgt = nodes.[5], value = 2.)
])
|> Chart.withLayout(Layout.init(Title = Title.init("Basic Sankey Diagram"), Font = Font.init(Size = 10.)))
Out[2]:

More complex Sankey diagram with colored links

In [3]:
open FSharp.Data

type remoteData = JsonProvider<"https://raw.githubusercontent.com/plotly/plotly.js/master/test/image/mocks/sankey_energy.json">
let data = remoteData.Load("https://raw.githubusercontent.com/plotly/plotly.js/master/test/image/mocks/sankey_energy.json")

let node = data.Data.[0].Node
let node_labels = data.Data.[0].Node.Label
let node_color = data.Data.[0].Node.Color
let source = data.Data.[0].Link.Source
let target = data.Data.[0].Link.Target
let values = data.Data.[0].Link.Value

let nodes = [for i in 0..node_labels.Length-1 -> Node.Create(label=node_labels.[i],color=node_color.[i])]
let links = [for i in 0..source.Length-1 -> Link.Create(src=nodes.[source.[i]],tgt=nodes.[target.[i]],value=float values.[i])]

let title = "Energy forecast for 2050<br>Source: Department of Energy & Climate Change, Tom Counsell via <a href='https://bost.ocks.org/mike/sankey/'>Mike Bostock</a>"
Chart.Sankey(nodes=nodes,links=links,
    nodePadding=float node.Pad,
    nodeThickness=float node.Thickness
    )
// |> GenericChart.mapTrace (fun x ->  x.SetValue("valueformat", ".0f")
//                                     x.SetValue("valuesuffix", ".TWh")
//                                     x)
|> Chart.withLayout(Layout.init(Width = 1000, Font = Font.init(Size = 10.), Title = Title.init(title)))
Out[3]:
Agricultural 'waste'Bio-conversionLiquidLossesSolidGasBiofuel importsBiomass importsCoal importsCoalCoal reservesDistrict heatingIndustryHeating and cooling - commercialHeating and cooling - homesElectricity gridOver generation / exportsH2 conversionRoad transportAgricultureRail transportLighting & appliances - commercialLighting & appliances - homesGas importsNgasGas reservesThermal generationGeothermalH2HydroInternational shippingDomestic aviationInternational aviationNational navigationMarine algaeNuclearOil importsOilOil reservesOther wastePumped heatSolar PVSolar ThermalSolarTidalUK land based bioenergyWaveWind
Energy forecast for 2050Source: Department of Energy & Climate Change, Tom Counsell via Mike Bostock

Style Sankey Diagram

In [4]:
open FSharp.Data

type remoteData = JsonProvider<"https://raw.githubusercontent.com/plotly/plotly.js/master/test/image/mocks/sankey_energy.json">
let data = remoteData.Load("https://raw.githubusercontent.com/plotly/plotly.js/master/test/image/mocks/sankey_energy.json")

let node = data.Data.[0].Node
let node_labels = data.Data.[0].Node.Label
let node_color = data.Data.[0].Node.Color
let source = data.Data.[0].Link.Source
let target = data.Data.[0].Link.Target
let values = data.Data.[0].Link.Value

let nodes = [for i in 0..node_labels.Length-1 -> Node.Create(label=node_labels.[i],color=node_color.[i])]
let links = [for i in 0..source.Length-1 -> Link.Create(src=nodes.[source.[i]],tgt=nodes.[target.[i]],value=float values.[i])]

let title = "Energy forecast for 2050<br>Source: Department of Energy & Climate Change, Tom Counsell via <a href='https://bost.ocks.org/mike/sankey/'>Mike Bostock</a>"
Chart.Sankey(nodes=nodes,links=links,
    nodePadding=float node.Pad,
    nodeThickness=float node.Thickness
    )
// |> GenericChart.mapTrace (fun x ->  x.SetValue("valueformat", ".0f")
//                                     x.SetValue("valuesuffix", ".TWh")
//                                     x)
|> Chart.withLayout(Layout.init(Width = 1000, Font = Font.init(Size = 10.,Color=Color.fromString "white"), Title = Title.init(title),PlotBGColor=Color.fromString "gray",PaperBGColor=Color.fromString "gray",HoverMode=StyleParam.HoverMode.X))
Out[4]:
Agricultural 'waste'Bio-conversionLiquidLossesSolidGasBiofuel importsBiomass importsCoal importsCoalCoal reservesDistrict heatingIndustryHeating and cooling - commercialHeating and cooling - homesElectricity gridOver generation / exportsH2 conversionRoad transportAgricultureRail transportLighting & appliances - commercialLighting & appliances - homesGas importsNgasGas reservesThermal generationGeothermalH2HydroInternational shippingDomestic aviationInternational aviationNational navigationMarine algaeNuclearOil importsOilOil reservesOther wastePumped heatSolar PVSolar ThermalSolarTidalUK land based bioenergyWaveWind
Energy forecast for 2050Source: Department of Energy & Climate Change, Tom Counsell via Mike Bostock

Define Node Position