diff --git a/Plotly.NET.sln b/Plotly.NET.sln index 623671b6c..5947c10fa 100644 --- a/Plotly.NET.sln +++ b/Plotly.NET.sln @@ -148,6 +148,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "04_distribution-charts", "0 docs\distribution-charts\point-density.fsx = docs\distribution-charts\point-density.fsx docs\distribution-charts\splom.fsx = docs\distribution-charts\splom.fsx docs\distribution-charts\violin-plots.fsx = docs\distribution-charts\violin-plots.fsx + docs\distribution-charts\pareto-chart.fsx = docs\distribution-charts\pareto-chart.fsx EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01_chart-layout", "01_chart-layout", "{C7D0EF67-9A18-49DD-AC79-944E384BD8D0}" diff --git a/docs/distribution-charts/pareto-chart.fsx b/docs/distribution-charts/pareto-chart.fsx new file mode 100644 index 000000000..ab153ac40 --- /dev/null +++ b/docs/distribution-charts/pareto-chart.fsx @@ -0,0 +1,70 @@ +(** +--- +title: Pareto chart +category: Distribution Charts +categoryindex: 5 +index: 9 +--- +*) + +(*** hide ***) + +(*** condition: prepare ***) +#r "nuget: Newtonsoft.JSON, 13.0.1" +#r "nuget: DynamicObj, 2.0.0" +#r "nuget: Giraffe.ViewEngine.StrongName, 2.0.0-alpha1" +#r "../../src/Plotly.NET/bin/Release/netstandard2.0/Plotly.NET.dll" + +Plotly.NET.Defaults.DefaultDisplayOptions <- + Plotly.NET.DisplayOptions.init (PlotlyJSReference = Plotly.NET.PlotlyJSReference.NoReference) + +(*** condition: ipynb ***) +#if IPYNB +#r "nuget: Plotly.NET, {{fsdocs-package-version}}" +#r "nuget: Plotly.NET.Interactive, {{fsdocs-package-version}}" +#endif // IPYNB + +(** +# Pareto chart + +[![Binder]({{root}}img/badge-binder.svg)](https://mybinder.org/v2/gh/plotly/plotly.net/gh-pages?urlpath=/tree/home/jovyan/{{fsdocs-source-basename}}.ipynb)  +[![Notebook]({{root}}img/badge-notebook.svg)]({{root}}{{fsdocs-source-basename}}.ipynb) + +*Summary:* This example shows how to create a Pareto chart in F#. + +Let's first create some data for the purpose of creating example charts: + +*) + +open Plotly.NET + +let data = + [ + "C#" , 420. + "F#" , 10008 + "Smalltalk" , 777 + "Pascal" , 543 + "Perl" , 666 + "VB.NET" , 640 + "C" , 111 + "ChucK" , 1230 + "ARexx" , 4440 + ] + +(** + +A Pareto chart is a type of chart that contains both bars and a line graph, where individual values are represented in descending order by bars, and the cumulative total is represented by the line. +The chart is named for the Pareto principle, which, in turn, derives its name from Vilfredo Pareto, a noted Italian economist. [Source](https://en.wikipedia.org/wiki/Pareto_chart) +*) + +let pareto = Chart.Pareto(keysValues = data, Name="Language", Label="Respondents") + +(*** condition: ipynb ***) +#if IPYNB +pareto +#endif // IPYNB + +(***hide***) +pareto |> GenericChart.toChartHTML +(***include-it-raw***) + diff --git a/src/Plotly.NET.CSharp/ChartAPI/Chart2D.cs b/src/Plotly.NET.CSharp/ChartAPI/Chart2D.cs index 336f4b4fd..53690e176 100644 --- a/src/Plotly.NET.CSharp/ChartAPI/Chart2D.cs +++ b/src/Plotly.NET.CSharp/ChartAPI/Chart2D.cs @@ -600,6 +600,48 @@ public static GenericChart Range( UpperName: UpperName.ToOption(), UseDefaults: UseDefaults.ToOption() ); + + /// Creates a Pareto chart. + /// Sets the (key,value) pairs that are plotted as the size and key of each bar. + /// Sets the trace name. The trace name appear as the legend item and on hover + /// Sets the y axis label. + /// Determines whether or not grid lines are drawn. If "true", the grid lines are drawn for the pareto distribution figure; defaults to true. + public static GenericChart Pareto( + IEnumerable<(TLabel,double)> keysValues + , Optional Name + , Optional Label + , Optional ShowGrid + ) + where TLabel : IConvertible + => + Chart2D.Chart.Pareto( + keysValues.Select(t => t.ToTuple()) + , Name: Name.ToOption() + , Label: Label.ToOption() + , ShowGrid: ShowGrid.ToOption() + ); + /// Creates a Pareto chart. + /// Sets the labels that are matching the . + /// Sets the values that are plotted as the size of each bar. + /// Sets the trace name. The trace name appear as the legend item and on hover + /// Sets the y axis label. + /// Determines whether or not grid lines are drawn. If "true", the grid lines are drawn for the pareto distribution figure; defaults to true. + public static GenericChart Pareto( + IEnumerable labels + , IEnumerable values + , Optional Name + , Optional Label + , Optional ShowGrid + ) + where TLabel : IConvertible + => + Chart2D.Chart.Pareto( + labels + , values + , Name: Name.ToOption() + , Label: Label.ToOption() + , ShowGrid: ShowGrid.ToOption() + ); /// Creates an Area chart, which uses a Line plotted between the given datums in a 2D space, additionally colouring the area between the line and the Y Axis. /// Sets the x coordinates of the plotted data. diff --git a/src/Plotly.NET.CSharp/TupleExtensions.cs b/src/Plotly.NET.CSharp/TupleExtensions.cs new file mode 100644 index 000000000..119e469f0 --- /dev/null +++ b/src/Plotly.NET.CSharp/TupleExtensions.cs @@ -0,0 +1,12 @@ +namespace Plotly.NET.CSharp; + +/// +/// Convenience to convert from C# struct tuple literals to the value tuple ones. +/// +internal static class TupleExtensions +{ + /// + /// Converts a 2 tuple. + /// + internal static Tuple ToTuple(this ValueTuple t) => Tuple.Create(t.Item1, t.Item2); +} \ No newline at end of file diff --git a/src/Plotly.NET/ChartAPI/Chart2D.fs b/src/Plotly.NET/ChartAPI/Chart2D.fs index 053914b97..9ab0c17bc 100644 --- a/src/Plotly.NET/ChartAPI/Chart2D.fs +++ b/src/Plotly.NET/ChartAPI/Chart2D.fs @@ -1547,8 +1547,80 @@ module Chart2D = ?UpperName = UpperName, ?UseDefaults = UseDefaults ) - - + + /// Creates a Pareto chart. + /// Sets the (key,value) pairs that are plotted as the size and key of each bar. + /// Sets the trace name. The trace name appear as the legend item and on hover + /// Sets the y axis label. + /// Determines whether or not grid lines are drawn. If "true", the grid lines are drawn for the pareto distribution figure; defaults to true. + [] + static member Pareto + ( + keysValues: seq<#IConvertible * float> + , [] ?Name: string + , [] ?Label: string + , [] ?ShowGrid: bool + ) = + let orderedLabels, orderedValues = + keysValues + |> Seq.sortByDescending snd + |> Seq.unzip + + let sum = orderedValues |> Seq.sum + let topPaddingRatio = 0.05 + let cumulativeSum = + Seq.scan (+) 0. orderedValues + |> Seq.skip 1 + + let paretoValues = + Seq.zip orderedLabels cumulativeSum + |> Seq.map (fun (label,value) -> label, value / sum * 100.) + + let bars = Chart.Column(Seq.zip orderedLabels orderedValues,?Name=Name) + + let lines = + Chart.Line( + paretoValues + , Name = "Cumulative %" + , ShowLegend = true + , ShowMarkers = true + , Marker = Marker.init(Size = 8, Symbol = StyleParam.MarkerSymbol.Cross, Angle = 45.) + ) + |> Chart.withAxisAnchor (Y = 2) + + [bars;lines] + |> Chart.combine + |> Chart.withYAxisStyle ( + ?TitleText = Label + , Id = StyleParam.SubPlotId.YAxis 1 + , ShowGrid = false + , MinMax = (0.,sum * (1.+topPaddingRatio)) + ) + |> Chart.withYAxisStyle ( + TitleText = "%" + , Side = StyleParam.Side.Right + , Id = StyleParam.SubPlotId.YAxis 2 + , MinMax = (0.,100. * (1.+topPaddingRatio)) + , Overlaying = StyleParam.LinearAxisId.Y 1 + , ?ShowGrid = ShowGrid + ) + + /// Creates a Pareto chart. + /// Sets the labels that are matching the . + /// Sets the values that are plotted as the size of each bar. + /// Sets the trace name. The trace name appear as the legend item and on hover + /// Sets the y axis label. + /// Determines whether or not grid lines are drawn. If "true", the grid lines are drawn for the pareto distribution figure; defaults to true. + static member Pareto + ( + labels: seq<#IConvertible> + , values: seq + , [] ?Name: string + , [] ?Label: string + , [] ?ShowGrid: bool + ) = + Chart.Pareto(Seq.zip labels values, ?Name=Name, ?Label=Label, ?ShowGrid=ShowGrid) + /// Creates an Area chart, which uses a Line plotted between the given datums in a 2D space, additionally colouring the area between the line and the Y Axis. /// Sets the x coordinates of the plotted data. /// Sets the y coordinates of the plotted data.