plotlars/plots/piechart.rs
1use bon::bon;
2
3use plotly::{
4 Layout as LayoutPlotly,
5 Pie,
6 Trace,
7};
8
9use polars::frame::DataFrame;
10use serde::Serialize;
11
12use crate::{
13 common::{Layout, PlotHelper, Polar},
14 components::Text,
15};
16
17/// A structure representing a pie chart.
18///
19/// The `PieChart` struct allows for the creation and customization of pie charts, supporting
20/// features such as labels, hole size for donut-style charts, slice pulling, rotation, and customizable plot titles.
21/// It is ideal for visualizing proportions and distributions in categorical data.
22///
23/// # Arguments
24///
25/// * `data` - A reference to the `DataFrame` containing the data to be plotted.
26/// * `labels` - A string slice specifying the column name to be used for slice labels.
27/// * `hole` - An optional `f64` value specifying the size of the hole in the center of the pie chart.
28/// A value of `0.0` creates a full pie chart, while a value closer to `1.0` creates a thinner ring.
29/// * `pull` - An optional `f64` value specifying the fraction by which each slice should be pulled out from the center.
30/// * `rotation` - An optional `f64` value specifying the starting angle (in degrees) of the first slice.
31/// * `plot_title` - An optional `Text` struct specifying the title of the plot.
32///
33/// # Example
34///
35/// ## Basic Pie Chart with Customization
36///
37/// ```rust
38/// use plotlars::{PieChart, Plot, Text};
39///
40/// let dataset = LazyCsvReader::new("data/penguins.csv")
41/// .finish()
42/// .unwrap()
43/// .select([
44/// col("species"),
45/// ])
46/// .collect()
47/// .unwrap();
48///
49/// PieChart::builder()
50/// .data(&dataset)
51/// .labels("species")
52/// .hole(0.4)
53/// .pull(0.01)
54/// .rotation(20.0)
55/// .plot_title(
56/// Text::from("Pie Chart")
57/// .font("Arial")
58/// .size(18)
59/// )
60/// .build()
61/// .plot();
62/// ```
63///
64/// 
65#[derive(Clone, Serialize)]
66pub struct PieChart {
67 traces: Vec<Box<dyn Trace + 'static>>,
68 layout: LayoutPlotly,
69}
70
71#[bon]
72impl PieChart {
73 #[builder(on(String, into), on(Text, into))]
74 pub fn new(
75 data: &DataFrame,
76 labels: &str,
77 hole: Option<f64>,
78 pull: Option<f64>,
79 rotation: Option<f64>,
80 plot_title: Option<Text>,
81 ) -> Self {
82 let x_title = None;
83 let y_title = None;
84 let z_title = None;
85 let legend_title = None;
86 let x_axis = None;
87 let y_axis = None;
88 let z_axis = None;
89 let legend = None;
90
91 let layout = Self::create_layout(
92 plot_title,
93 x_title,
94 y_title,
95 None, // y2_title,
96 z_title,
97 legend_title,
98 x_axis,
99 y_axis,
100 None, // y2_axis,
101 z_axis,
102 legend,
103 );
104
105 let mut traces = vec![];
106
107 let trace = Self::create_trace(data, labels, hole, pull, rotation);
108
109 traces.push(trace);
110
111 Self { traces, layout }
112 }
113
114 fn create_trace(
115 data: &DataFrame,
116 labels: &str,
117 hole: Option<f64>,
118 pull: Option<f64>,
119 rotation: Option<f64>,
120 ) -> Box<dyn Trace + 'static> {
121 let labels = Self::get_string_column(data, labels)
122 .iter()
123 .filter_map(|s| {
124 if s.is_some() {
125 Some(s.clone().unwrap().to_owned())
126 } else {
127 None
128 }
129 })
130 .collect::<Vec<String>>();
131
132 let mut trace = Pie::<u32>::from_labels(&labels);
133
134 if let Some(hole) = hole {
135 trace = trace.hole(hole);
136 }
137
138 if let Some(pull) = pull {
139 trace = trace.pull(pull);
140 }
141
142 if let Some(rotation) = rotation {
143 trace = trace.rotation(rotation);
144 }
145
146 trace
147 }
148}
149
150impl Layout for PieChart {}
151impl Polar for PieChart {}
152
153impl PlotHelper for PieChart {
154 fn get_layout(&self) -> &LayoutPlotly {
155 &self.layout
156 }
157
158 fn get_traces(&self) -> &Vec<Box<dyn Trace + 'static>> {
159 &self.traces
160 }
161}