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