Skip to content

Instantly share code, notes, and snippets.

@Thorium Thorium/Program.fs
Last active Apr 24, 2019

Embed
What would you like to do?
Creating graphical visualisation of a Markov chain

Markov chain example from: https://github.com/mariuszwojcik/Presentations/blob/master/Introduction%20to%20Markov%20Chains%20in%20F%23/code/src/WeatherForecast.fsx

GrapViz Snipet taken from: http://fssnip.net/7Rf

Create new .NET Framework console application: GrapViz doesn't support .NET Standard and doesn't run in FSI.

  1. Install: http://www.graphviz.org/download/

  2. Add <appSettings> under <configuration> to app.config/web.config Add Nuget / Paket: GraphViz.NET

  3. Add under <configuration><appSettings>: <add key="graphVizLocation" value="C:\Program Files (x86)\Graphviz2.38\bin" />

Add references to System.Configuration.dll and System.Drawing.dll

sample

module GraphVizSample
open GraphVizWrapper
open GraphVizWrapper.Commands
open GraphVizWrapper.Queries
open System
open System.Configuration
open System.Drawing
open System.IO
/// Creates some pngs
let genereateGraphFile (path:string) graphVizImageData =
let procqry = GetStartProcessQuery()
let infoqry = GetProcessStartInfoQuery()
let wrapper = GraphGeneration(procqry, infoqry,
RegisterLayoutPluginCommand(infoqry, procqry))
// You probably don't need all 7:
//[0..7] // different layout of graphs, e.g.: Enums.RenderingEngine.Neato
[1] |> List.map(fun i ->
wrapper.RenderingEngine <- enum<Enums.RenderingEngine> i
//wrapper.GraphvizPath <- "C:/Program Files (x86)/Graphviz2.38/bin//"
let output = wrapper.GenerateGraph(graphVizImageData, Enums.GraphReturnType.Png)
use image = System.Drawing.Image.FromStream(new MemoryStream(output))
let filename = sprintf "%sgraph-%i-%i.png" path (output.GetHashCode()) i
do image.Save(filename, System.Drawing.Imaging.ImageFormat.Png)
let creationtime = File.GetLastWriteTime filename
filename, creationtime
)
/// Replace your version of data generation.
/// This is just a silly example to give some starting point.
/// Note: If you use Guid based Ids, it's better to use some text-prefix!
let sampleGraphData items1 items2 =
// shapes: box, diamond, circle, ...
// colors: lightgray, lightblue, goldenrod2, thistle2, ...
// Click on picture under http://www.graphviz.org/Gallery.php
// ...and picture again to get the sample txt-file.
let idPrefix = "item"
let nodes1 =
let items = items1 |> Array.map(fun (id,name,_) ->
sprintf "%s%s [label=\"%s\"]; " idPrefix id name)
"node [shape=box,style=filled,color=lightblue]; " +
(items |> String.Concat)
let nodes2 =
let printStr =
sprintf "%s%s [label=\"%s\",color=%s]; " idPrefix
"node [shape=circle,style=filled]; " +
(items2 |> Array.map(function
| id, name, true, _ -> printStr id name "goldenrod2"
| id, name, false, _ -> printStr id name "lightgray"
) |> String.Concat)
let graphPart =
let printArrow aFrom aTo =
sprintf "%s%s -> %s%s;" idPrefix aFrom idPrefix aTo
let arrows1 =
items1 |> Array.filter(fun (i,_,lnk) -> lnk<>"")
|> Array.map(fun (item,_,lnk) -> printArrow item lnk)
let arrows2 =
items2 |> Array.filter(fun (i,_,_,lnk) -> lnk<>"")
|> Array.map(fun (item,_,_,lnk) -> printArrow item lnk)
(String.Concat arrows1) + (String.Concat arrows2)
"digraph myDiagram { " + nodes1 + nodes2 + graphPart + "overlap=false}"
// Create some data.
let generateWeatherGraph (data:seq<char * seq<char * float>>) =
let genId() = Guid.NewGuid().ToString("N")
let probabilities = data |> Map.ofSeq
let states =
data |> Seq.toArray |> Array.map (fun k -> fst k, genId())
let statesMap = states |> Map.ofSeq
let stateItems, changeItems =
states |> Array.collect(fun (stateLabel, stateId) ->
probabilities.[stateLabel] |> Seq.toArray
|> Array.map(fun (toState, propabilityLabel) ->
let changeStateId = genId()
(stateId, stateLabel.ToString(), changeStateId),
(changeStateId, propabilityLabel.ToString(), false, statesMap.[toState])
)
) |> Array.unzip
let graphdata = sampleGraphData stateItems changeItems
genereateGraphFile AppDomain.CurrentDomain.BaseDirectory graphdata
// https://github.com/mariuszwojcik/Presentations/blob/ff095259ea75f790723ada0e6e0612150ead061d/Introduction%20to%20Markov%20Chains%20in%20F%23/code/src/WeatherForecast.fsx#L10
let calcTransitionProbability items =
let total = items |> Seq.length
items
|> Seq.groupBy(id)
|> Seq.map(fun (g,i) -> g, float (i |> Seq.length) / float total)
let data =
"RRRRSSSSRRRRRRRRRRRRRRRRRRRSSSSSRR"
|> Seq.windowed 2
|> Seq.groupBy(fun a -> a.[0])
|> Seq.map(fun (k, e) -> (k,e |> Seq.map(Seq.skip 1 >> Seq.head) |> calcTransitionProbability))
[<EntryPoint>]
let main argv =
let r = generateWeatherGraph data
printfn "%A" argv
0 // return an integer exit code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.