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