diff --git a/.paket/Paket.Restore.targets b/.paket/Paket.Restore.targets
index aed3c3fdc..dfc4aaec7 100644
--- a/.paket/Paket.Restore.targets
+++ b/.paket/Paket.Restore.targets
@@ -7,7 +7,7 @@
$(MSBuildAllProjects);$(MSBuildThisFileFullPath)
$(MSBuildVersion)
- 15.0.0
+ 15.0.0
false
true
@@ -20,59 +20,86 @@
proj
assembly
native
- /Library/Frameworks/Mono.framework/Commands/mono
+ /Library/Frameworks/Mono.framework/Commands/mono
mono
$(PaketRootPath)paket.bootstrapper.exe
$(PaketToolsPath)paket.bootstrapper.exe
$([System.IO.Path]::GetDirectoryName("$(PaketBootStrapperExePath)"))\
+
+ "$(PaketBootStrapperExePath)"
+ $(MonoPath) --runtime=v4.0.30319 "$(PaketBootStrapperExePath)"
+
+
+ True
-
+
+ False
-
- $(PaketRootPath)paket.exe
- $(PaketToolsPath)paket.exe
- $(PaketToolsPath)paket.exe
- $(_PaketBootStrapperExeDir)paket.exe
- paket.exe
+ $(BaseIntermediateOutputPath.TrimEnd('\').TrimEnd('\/'))
+
-
- $(PaketRootPath)paket
- $(PaketToolsPath)paket
- $(PaketToolsPath)paket
+
+
+
+
+
+
+ $(PaketRootPath)paket
+ $(PaketToolsPath)paket
+
-
- $(PaketRootPath)paket.exe
- $(PaketToolsPath)paket.exe
+
+
+
+ $(PaketRootPath)paket.exe
+ $(PaketToolsPath)paket.exe
+
-
- $(PaketBootStrapperExeDir)paket.exe
+
+
+
+ <_DotnetToolsJson Condition="Exists('$(PaketRootPath)/.config/dotnet-tools.json')">$([System.IO.File]::ReadAllText("$(PaketRootPath)/.config/dotnet-tools.json"))
+ <_ConfigContainsPaket Condition=" '$(_DotnetToolsJson)' != ''">$(_DotnetToolsJson.Contains('"paket"'))
+ <_ConfigContainsPaket Condition=" '$(_ConfigContainsPaket)' == ''">false
+
-
- paket
+
+
+
+
+
-
- <_PaketExeExtension>$([System.IO.Path]::GetExtension("$(PaketExePath)"))
- dotnet "$(PaketExePath)"
- $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)"
- "$(PaketExePath)"
+
+
+ <_PaketCommand>dotnet paket
+
+
+
+
+ $(PaketToolsPath)paket
+ $(PaketBootStrapperExeDir)paket
- "$(PaketBootStrapperExePath)"
- $(MonoPath) --runtime=v4.0.30319 "$(PaketBootStrapperExePath)"
+
+ paket
+
-
-
-
- true
- true
+
+
+ <_PaketExeExtension>$([System.IO.Path]::GetExtension("$(PaketExePath)"))
+ <_PaketCommand Condition=" '$(_PaketCommand)' == '' AND '$(_PaketExeExtension)' == '.dll' ">dotnet "$(PaketExePath)"
+ <_PaketCommand Condition=" '$(_PaketCommand)' == '' AND '$(OS)' != 'Windows_NT' AND '$(_PaketExeExtension)' == '.exe' ">$(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)"
+ <_PaketCommand Condition=" '$(_PaketCommand)' == '' ">"$(PaketExePath)"
+
-
- True
+
+
+
+
- $(BaseIntermediateOutputPath.TrimEnd('\').TrimEnd('\/'))
-
+
@@ -81,7 +108,7 @@
-
+
@@ -203,7 +230,7 @@
$([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[0])
$([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[1])
$([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[4])
- $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[5])
+ $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[5])
%(PaketReferencesFileLinesInfo.PackageVersion)
@@ -246,7 +273,7 @@
-
+
<_NuspecFilesNewLocation Include="$(PaketIntermediateOutputPath)\$(Configuration)\*.nuspec"/>
@@ -300,7 +327,7 @@
DevelopmentDependency="$(DevelopmentDependency)"
BuildOutputInPackage="@(_BuildOutputInPackage)"
TargetPathsToSymbols="@(_TargetPathsToSymbols)"
- SymbolPackageFormat="symbols.nupkg"
+ SymbolPackageFormat="$(SymbolPackageFormat)"
TargetFrameworks="@(_TargetFrameworks)"
AssemblyName="$(AssemblyName)"
PackageOutputPath="$(PackageOutputAbsolutePath)"
@@ -347,7 +374,7 @@
DevelopmentDependency="$(DevelopmentDependency)"
BuildOutputInPackage="@(_BuildOutputInPackage)"
TargetPathsToSymbols="@(_TargetPathsToSymbols)"
- SymbolPackageFormat="symbols.nupkg"
+ SymbolPackageFormat="$(SymbolPackageFormat)"
TargetFrameworks="@(_TargetFrameworks)"
AssemblyName="$(AssemblyName)"
PackageOutputPath="$(PackageOutputAbsolutePath)"
diff --git a/FSharp.Plotly.sln b/FSharp.Plotly.sln
index ec711caaa..df8a58fd1 100644
--- a/FSharp.Plotly.sln
+++ b/FSharp.Plotly.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27703.2000
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29613.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{63297B98-5CED-492C-A5B7-A5B4F73CF142}"
ProjectSection(SolutionItems) = preProject
@@ -36,6 +36,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "content", "content", "{8E6D
docsrc\content\bar-charts.fsx = docsrc\content\bar-charts.fsx
docsrc\content\box-plots.fsx = docsrc\content\box-plots.fsx
docsrc\content\bubble-charts.fsx = docsrc\content\bubble-charts.fsx
+ docsrc\content\candlestick.fsx = docsrc\content\candlestick.fsx
docsrc\content\choropleth-map.fsx = docsrc\content\choropleth-map.fsx
docsrc\content\contour-plots.fsx = docsrc\content\contour-plots.fsx
docsrc\content\errorbars.fsx = docsrc\content\errorbars.fsx
@@ -51,6 +52,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "content", "content", "{8E6D
docsrc\content\plotly-wpf.fsx = docsrc\content\plotly-wpf.fsx
docsrc\content\polar-charts.fsx = docsrc\content\polar-charts.fsx
docsrc\content\range-plots.fsx = docsrc\content\range-plots.fsx
+ docsrc\content\sankey.fsx = docsrc\content\sankey.fsx
docsrc\content\shapes.fsx = docsrc\content\shapes.fsx
docsrc\content\splom.fsx = docsrc\content\splom.fsx
docsrc\content\violin-plots.fsx = docsrc\content\violin-plots.fsx
diff --git a/docsrc/content/candlestick.fsx b/docsrc/content/candlestick.fsx
new file mode 100644
index 000000000..3bd6dbcdc
--- /dev/null
+++ b/docsrc/content/candlestick.fsx
@@ -0,0 +1,42 @@
+(*** hide ***)
+#r "netstandard"
+#r @"../../lib/Formatting/FSharp.Plotly.dll"
+
+(**
+# FSharp.Plotly: Candlestick Charts
+
+*Summary:* This example shows how to create candlestick charts in F#.
+
+A candlestick chart is useful for plotting stock prices over time. A candle
+is a group of high, open, close and low values over a period of time, e.g. 1 minute, 5 minute, hour, day, etc..
+The x-axis is usually dateime values and y is a sequence of candle structures.
+*)
+
+open FSharp.Plotly
+open FSharp.Plotly.StyleParam
+
+let candles =
+ [|("2020-01-17T13:40:00", 0.68888, 0.68888, 0.68879, 0.6888);
+ ("2020-01-17T13:41:00", 0.68883, 0.68884, 0.68875, 0.68877);
+ ("2020-01-17T13:42:00", 0.68878, 0.68889, 0.68878, 0.68886);
+ ("2020-01-17T13:43:00", 0.68886, 0.68886, 0.68876, 0.68879);
+ ("2020-01-17T13:44:00", 0.68879, 0.68879, 0.68873, 0.68874);
+ ("2020-01-17T13:45:00", 0.68875, 0.68877, 0.68867, 0.68868);
+ ("2020-01-17T13:46:00", 0.68869, 0.68887, 0.68869, 0.68883);
+ ("2020-01-17T13:47:00", 0.68883, 0.68899, 0.68883, 0.68899);
+ ("2020-01-17T13:48:00", 0.68898, 0.689, 0.68885, 0.68889);
+ ("2020-01-17T13:49:00", 0.68889, 0.68893, 0.68881, 0.68893);
+ ("2020-01-17T13:50:00", 0.68891, 0.68896, 0.68886, 0.68891);
+ |]
+ |> Array.map (fun (d,o,h,l,c)->System.DateTime.Parse d, Candle.Create(o,h,l,c))
+
+
+let ch1 = candles |> Chart.Candelstick
+
+
+(***do-not-eval***)
+ch1 |> Chart.Show
+
+(*** include-value:ch1 ***)
+
+
diff --git a/docsrc/content/sankey.fsx b/docsrc/content/sankey.fsx
new file mode 100644
index 000000000..125fb5469
--- /dev/null
+++ b/docsrc/content/sankey.fsx
@@ -0,0 +1,38 @@
+(*** hide ***)
+#r "netstandard"
+#r @"../../lib/Formatting/FSharp.Plotly.dll"
+
+(**
+# FSharp.Plotly: Sankey Charts
+
+*Summary:* This example shows how to create sankey charts in F#.
+
+Sankey charts are a visualization of multiple, linked graphs layed out linearly.
+These are usually used to depict flow between nodes or stations.
+To create Sankey, a set of nodes and links between them are required.
+These are created using the provided Node and Link structures.
+*)
+
+open FSharp.Plotly
+open FSharp.Plotly.StyleParam
+let n1 = Node.Create("a",color="Black")
+let n2 = Node.Create("b",color="Red")
+let n3 = Node.Create("c",color="Purple")
+let n4 = Node.Create("d",color="Green")
+let n5 = Node.Create("e",color="Orange")
+let link1 = Link.Create(n1,n2,value=1.0)
+let link2 = Link.Create(n2,n3,value=2.0)
+let link3 = Link.Create(n1,n5,value=1.3)
+let link4 = Link.Create(n4,n5,value=1.5)
+let link5 = Link.Create(n3,n5,value=0.5)
+
+let ch1 =
+ Chart.Sankey([n1;n2;n3;n4;n5],[link1;link2;link3;link4;link5])
+ |> Chart.withTitle "Sankey Sample"
+
+
+(***do-not-eval***)
+ch1 |> Chart.Show
+
+(*** include-value:ch1 ***)
+
diff --git a/lib/Formatting/FSharp.Plotly.dll b/lib/Formatting/FSharp.Plotly.dll
index e2169580e..e5de7f78f 100644
Binary files a/lib/Formatting/FSharp.Plotly.dll and b/lib/Formatting/FSharp.Plotly.dll differ
diff --git a/src/FSharp.Plotly/CandelstickExtension.fs b/src/FSharp.Plotly/CandelstickExtension.fs
new file mode 100644
index 000000000..f581f9b15
--- /dev/null
+++ b/src/FSharp.Plotly/CandelstickExtension.fs
@@ -0,0 +1,67 @@
+namespace FSharp.Plotly
+
+open Trace
+open System
+
+type Candle =
+ {
+ High : float
+ Low : float
+ Close : float
+ Open : float
+ }
+ with
+ static member Create(o,h,l,c) =
+ {
+ High = h
+ Low = l
+ Close = c
+ Open = o
+ }
+
+[]
+module CandelstickExtension =
+
+ module Trace =
+ let initCandelstick (applyStyle:Trace->Trace) =
+ FSharp.Plotly.Trace("candlestick") |> applyStyle
+
+
+ type TraceStyle with
+ static member Candlestick
+ (
+ data : (#IConvertible*Candle) seq,
+ ?increasing : Line,
+ ?decreasing : Line,
+ ?line : Line
+ ) =
+ (fun (trace:('T :> Trace)) ->
+ DynObj.setValue trace "open" (data |> Seq.map snd |> Seq.map(fun x->x.Open))
+ DynObj.setValue trace "high" (data |> Seq.map snd |> Seq.map(fun x->x.High))
+ DynObj.setValue trace "low" (data |> Seq.map snd |> Seq.map(fun x->x.Low))
+ DynObj.setValue trace "close" (data |> Seq.map snd |> Seq.map(fun x->x.Close))
+ DynObj.setValue trace "x" (data |> Seq.map fst)
+ DynObj.setValue trace "xaxis" "x"
+ DynObj.setValue trace "yaxis" "y"
+ DynObj.setValueOpt trace "line" line
+ DynObj.setValueOpt trace "increasing" increasing
+ DynObj.setValueOpt trace "decreasing" decreasing
+ trace
+ )
+
+ type Chart with
+ static member Candelstick
+ (
+ data : (#IConvertible*Candle) seq,
+ ?increasing : Line,
+ ?decreasing : Line,
+ ?line : Line
+ ) =
+ Trace.initCandelstick(TraceStyle.Candlestick
+ (
+ data,
+ ?increasing=increasing,
+ ?decreasing=decreasing,
+ ?line=line
+ ))
+ |> GenericChart.ofTraceObject
diff --git a/src/FSharp.Plotly/FSharp.Plotly.fsproj b/src/FSharp.Plotly/FSharp.Plotly.fsproj
index 83e594c74..04bd8443f 100644
--- a/src/FSharp.Plotly/FSharp.Plotly.fsproj
+++ b/src/FSharp.Plotly/FSharp.Plotly.fsproj
@@ -38,6 +38,8 @@
+
+
diff --git a/src/FSharp.Plotly/SankeyExtension.fs b/src/FSharp.Plotly/SankeyExtension.fs
new file mode 100644
index 000000000..54cd8a135
--- /dev/null
+++ b/src/FSharp.Plotly/SankeyExtension.fs
@@ -0,0 +1,237 @@
+namespace FSharp.Plotly
+
+open Trace
+
+type Node =
+ {
+ Label : string
+ Groups : string[] option
+ XRank : int option
+ YRank : int option
+ Color : obj option
+ LineColor : obj option
+ LineWidth : float option
+ }
+ with
+ static member Create(label,?groups,?xRank,?yRank,?color,?lineColor,?lineWidth) =
+ {
+ Label = label
+ Groups = groups
+ XRank = xRank
+ YRank = yRank
+ Color = color
+ LineColor = lineColor
+ LineWidth = lineWidth
+ }
+
+type Link =
+ {
+ Source : Node
+ Target : Node
+ Value : float option
+ Label : string option
+ Color : obj option
+ LineColor : obj option
+ LineWidth : float option
+
+ }
+ with
+ static member Create(src,tgt,?value,?label,?color,?lineColor,?lineWidth) =
+ {
+ Source = src
+ Target = tgt
+ Value = value
+ Label = label
+ Color = color
+ LineColor = lineColor
+ LineWidth = lineWidth
+ }
+
+[]
+module SankeyExtension =
+
+ module Trace =
+ let initSankey (applyStyle:Trace->Trace) =
+ FSharp.Plotly.Trace("sankey") |> applyStyle
+
+ type TraceStyle with
+ static member Sankey
+ (
+ nodes:Node seq,
+ links:Link seq,
+ ?nodePadding:float,
+ ?nodeThicknes:float,
+ ?nodeColor:obj,
+ ?nodeLineColor:obj,
+ ?nodeLineWidth:float,
+ ?linkColor:obj,
+ ?linkLineColor: obj,
+ ?linkLineWidth:float
+ ) =
+ (fun (trace:('T :> Trace)) ->
+ let nonUniqueLabels = nodes |> Seq.countBy (fun x->x.Label) |> Seq.filter (fun (_,c) -> c > 1)
+ if nonUniqueLabels |> Seq.length > 0 then failwithf "duplicated label names %A" (nonUniqueLabels |> Seq.map fst)
+ let lblMap = nodes |> Seq.mapi(fun i x->x.Label,i) |> Map.ofSeq // give each node an index
+
+ let link =
+ let linkClrs =
+ links
+ |> Seq.map (fun x->x.Color)
+ |> Seq.map (function Some x -> x | None -> linkColor |> Option.defaultValue null)
+ |> fun xs -> if xs |> Seq.exists (fun x-> x <> null) then xs |> Seq.toArray |> Some else None
+
+ let linkLineClrs =
+ links
+ |> Seq.map (fun x->x.LineColor)
+ |> Seq.map (function Some x -> x | None -> linkLineColor |> Option.defaultValue null)
+ |> fun xs -> if xs |> Seq.exists (fun x-> x <> null) then xs |> Seq.toArray |> Some else None
+
+ let linkLineWidths =
+ links
+ |> Seq.map (fun x->x.LineWidth)
+ |> Seq.map (function Some x -> x | None -> linkLineWidth |> Option.defaultValue 0.5)
+ |> fun xs -> if xs |> Seq.exists (fun x-> x <> 0.5) then xs |> Seq.toArray |> Some else None
+
+ let values =
+ links
+ |> Seq.map (fun x->x.Value)
+ |> fun xs -> if xs |> Seq.exists Option.isSome then
+ xs |> Seq.map(function Some x -> box x | None -> null) |> Seq.toArray |> Some
+ else
+ None
+
+ let line =
+ match (linkLineClrs,linkLineWidths) with
+ | None,None -> None
+ | cs,ws ->
+ let ln = new DynamicObj()
+ DynObj.setValueOpt ln "color" cs
+ DynObj.setValueOpt ln "width" ws
+ Some ln
+
+ let l = new DynamicObj()
+ DynObj.setValue l "source" (links |> Seq.map (fun x->lblMap.[x.Source.Label]))
+ DynObj.setValue l "target" (links |> Seq.map (fun x->lblMap.[x.Target.Label]))
+ DynObj.setValueOpt l "color" linkClrs
+ DynObj.setValueOpt l "value" values
+ DynObj.setValueOpt l "line" line
+
+ l
+
+ let node =
+ let groups =
+ nodes
+ |> Seq.collect(fun x->x.Groups |> Option.defaultValue [||] |> Array.map (fun g-> g,lblMap.[x.Label]))
+ |> Seq.groupBy fst
+ |> Seq.map (fun (g,gs) -> gs |> Seq.map snd)
+ |> fun xs -> if Seq.isEmpty xs then None else Some (xs |> Seq.map Seq.toArray |> Seq.toArray)
+
+ let xRanks =
+ nodes
+ |> Seq.map (fun x -> x.XRank |> Option.map box |> Option.defaultValue null)
+ |> fun xs -> if xs |> Seq.exists (fun x-> x <> null) then xs |> Seq.toArray |> Some else None
+
+ let yRanks =
+ nodes
+ |> Seq.map (fun x -> x.YRank |> Option.map box |> Option.defaultValue null)
+ |> fun xs -> if xs |> Seq.exists (fun x-> x <> null) then xs |> Seq.toArray |> Some else None
+
+ let nodeClrs =
+ nodes
+ |> Seq.map (fun x->x.Color)
+ |> Seq.map (function Some x -> x | None -> nodeColor |> Option.defaultValue null)
+ |> fun xs -> if xs |> Seq.exists (fun x-> x <> null) then xs |> Seq.toArray |> Some else None
+
+ let nodeLineClrs =
+ nodes
+ |> Seq.map (fun x->x.LineColor)
+ |> Seq.map (function Some x -> x | None -> nodeLineColor |> Option.defaultValue null)
+ |> fun xs -> if xs |> Seq.exists (fun x-> x <> null) then xs |> Seq.toArray |> Some else None
+
+ let nodeLineWidths =
+ nodes
+ |> Seq.map (fun x->x.LineWidth)
+ |> Seq.map (function Some x -> x | None -> nodeLineWidth |> Option.defaultValue 0.5)
+ |> fun xs -> if xs |> Seq.exists (fun x-> x <> 0.5) then xs |> Seq.toArray |> Some else None
+
+ let line =
+ match (nodeLineClrs,nodeLineWidths) with
+ | None,None -> None
+ | cs,ws ->
+ let ln = new DynamicObj()
+ DynObj.setValueOpt ln "color" cs
+ DynObj.setValueOpt ln "width" ws
+ Some ln
+
+ let n = new DynamicObj()
+ DynObj.setValue n "label" (nodes |> Seq.map (fun x->x.Label))
+ DynObj.setValueOpt n "groups" groups
+ DynObj.setValueOpt n "pad" nodePadding
+ DynObj.setValueOpt n "thickness" nodeThicknes
+ DynObj.setValueOpt n "x" xRanks
+ DynObj.setValueOpt n "y" yRanks
+ DynObj.setValueOpt n "color" nodeClrs
+ DynObj.setValueOpt n "line" line
+ n
+
+ DynObj.setValue trace "node" node
+ DynObj.setValue trace "link" link
+
+ trace
+ )
+
+ type Chart with
+ static member Sankey
+ (
+ nodes:Node seq,
+ links:Link seq,
+ ?nodePadding:float,
+ ?nodeThicknes:float,
+ ?nodeColor:obj,
+ ?nodeLineColor:obj,
+ ?nodeLineWidth:float,
+ ?linkColor:obj,
+ ?linkLineColor: obj,
+ ?linkLineWidth:float
+ ) =
+ Trace.initSankey(TraceStyle.Sankey
+ (
+ nodes,
+ links,
+ ?nodePadding=nodePadding,
+ ?nodeThicknes=nodeThicknes,
+ ?nodeColor=nodeColor,
+ ?nodeLineColor=nodeLineColor,
+ ?nodeLineWidth=nodeLineWidth,
+ ?linkColor=linkColor,
+ ?linkLineColor=linkLineColor,
+ ?linkLineWidth=linkLineWidth
+ ))
+ |> GenericChart.ofTraceObject
+
+
+ (*
+ #load "SetEnv.fsx"
+ open FSharp.Plotly
+ open PlotlyExts
+ let testSankey() =
+ let n1 = Node.Create("a",color="Black")
+ let n2 = Node.Create("b",color="Red")
+ let n3 = Node.Create("c",color="Purple")
+ let n4 = Node.Create("d",color="Green")
+ let n5 = Node.Create("e",color="Orange")
+ let link1 = Link.Create(n1,n2,value=1.0)
+ let link2 = Link.Create(n2,n3,value=2.0)
+ let link3 = Link.Create(n1,n5,value=1.3)
+ let link4 = Link.Create(n4,n5,value=1.5)
+ let link5 = Link.Create(n3,n5,value=0.5)
+ Chart.Sankey([n1;n2;n3;n4;n5],[link1;link2;link3;link4;link5])
+ |> Chart.withTitle "Sankey Sample"
+ |> Chart.Show
+
+ testSankey()
+ *)
+
+
+
+
\ No newline at end of file