Snippets

Tuomas Hietanen Creating graphical visualisation of a Markov chain

Created by Tuomas Hietanen
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

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

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.