Skip to content

Pareto chart implementation #431

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Plotly.NET.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
70 changes: 70 additions & 0 deletions docs/distribution-charts/pareto-chart.fsx
Original file line number Diff line number Diff line change
@@ -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)&emsp;
[![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. <sup>[Source](https://en.wikipedia.org/wiki/Pareto_chart)</sup>
*)

let pareto = Chart.Pareto(keysValues = data, Name="Language", Label="Respondents")

(*** condition: ipynb ***)
#if IPYNB
pareto
#endif // IPYNB

(***hide***)
pareto |> GenericChart.toChartHTML
(***include-it-raw***)

42 changes: 42 additions & 0 deletions src/Plotly.NET.CSharp/ChartAPI/Chart2D.cs
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,48 @@ public static GenericChart Range<XType, YType, TextType>(
UpperName: UpperName.ToOption(),
UseDefaults: UseDefaults.ToOption()
);

/// <summary> Creates a Pareto chart. </summary>
/// <param name="keysValues">Sets the (key,value) pairs that are plotted as the size and key of each bar.</param>
/// <param name="Name">Sets the trace name. The trace name appear as the legend item and on hover</param>
/// <param name="Label">Sets the y axis label.</param>
/// <param name="ShowGrid">Determines whether or not grid lines are drawn. If "true", the grid lines are drawn for the pareto distribution figure; defaults to true.</param>
public static GenericChart Pareto<TLabel>(
IEnumerable<(TLabel,double)> keysValues
, Optional<string> Name
, Optional<string> Label
, Optional<bool> ShowGrid
)
where TLabel : IConvertible
=>
Chart2D.Chart.Pareto(
keysValues.Select(t => t.ToTuple())
, Name: Name.ToOption()
, Label: Label.ToOption()
, ShowGrid: ShowGrid.ToOption()
);
/// <summary> Creates a Pareto chart. </summary>
/// <param name="labels">Sets the labels that are matching the <see paramref="values"/>.</param>
/// <param name="values">Sets the values that are plotted as the size of each bar.</param>
/// <param name="Name">Sets the trace name. The trace name appear as the legend item and on hover</param>
/// <param name="Label">Sets the y axis label.</param>
/// <param name="ShowGrid">Determines whether or not grid lines are drawn. If "true", the grid lines are drawn for the pareto distribution figure; defaults to true.</param>
public static GenericChart Pareto<TLabel>(
IEnumerable<TLabel> labels
, IEnumerable<double> values
, Optional<string> Name
, Optional<string> Label
, Optional<bool> ShowGrid
)
where TLabel : IConvertible
=>
Chart2D.Chart.Pareto(
labels
, values
, Name: Name.ToOption()
, Label: Label.ToOption()
, ShowGrid: ShowGrid.ToOption()
);

/// <summary> 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.</summary>
/// <param name="x">Sets the x coordinates of the plotted data.</param>
Expand Down
12 changes: 12 additions & 0 deletions src/Plotly.NET.CSharp/TupleExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Plotly.NET.CSharp;

/// <summary>
/// Convenience to convert from C# struct tuple literals to the value tuple ones.
/// </summary>
internal static class TupleExtensions
{
/// <summary>
/// Converts a 2 tuple.
/// </summary>
internal static Tuple<T1,T2> ToTuple<T1,T2>(this ValueTuple<T1,T2> t) => Tuple.Create(t.Item1, t.Item2);
}
76 changes: 74 additions & 2 deletions src/Plotly.NET/ChartAPI/Chart2D.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1547,8 +1547,80 @@ module Chart2D =
?UpperName = UpperName,
?UseDefaults = UseDefaults
)



/// <summary> Creates a Pareto chart. </summary>
/// <param name="keysValues">Sets the (key,value) pairs that are plotted as the size and key of each bar.</param>
/// <param name="Name">Sets the trace name. The trace name appear as the legend item and on hover</param>
/// <param name="Label">Sets the y axis label.</param>
/// <param name="ShowGrid">Determines whether or not grid lines are drawn. If "true", the grid lines are drawn for the pareto distribution figure; defaults to true.</param>
[<Extension>]
static member Pareto
(
keysValues: seq<#IConvertible * float>
, [<Optional; DefaultParameterValue(null)>] ?Name: string
, [<Optional; DefaultParameterValue(null)>] ?Label: string
, [<Optional; DefaultParameterValue(true)>] ?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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually expose settings like this as method argument, and setting sensible default values.

, 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
)

/// <summary> Creates a Pareto chart. </summary>
/// <param name="labels">Sets the labels that are matching the <see paramref="values"/>.</param>
/// <param name="values">Sets the values that are plotted as the size of each bar.</param>
/// <param name="Name">Sets the trace name. The trace name appear as the legend item and on hover</param>
/// <param name="Label">Sets the y axis label.</param>
/// <param name="ShowGrid">Determines whether or not grid lines are drawn. If "true", the grid lines are drawn for the pareto distribution figure; defaults to true.</param>
static member Pareto
(
labels: seq<#IConvertible>
, values: seq<float>
, [<Optional; DefaultParameterValue(null)>] ?Name: string
, [<Optional; DefaultParameterValue(null)>] ?Label: string
, [<Optional; DefaultParameterValue(true)>] ?ShowGrid: bool
) =
Chart.Pareto(Seq.zip labels values, ?Name=Name, ?Label=Label, ?ShowGrid=ShowGrid)

/// <summary> 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.</summary>
/// <param name="x">Sets the x coordinates of the plotted data.</param>
/// <param name="y">Sets the y coordinates of the plotted data.</param>
Expand Down