Skip to main content

ggplot_rs/data/
polars_macro.rs

1//! [`from_polars!`](crate::from_polars): polars input that works with
2//! *your* polars version (#466).
3
4/// Convert a polars `DataFrame` into plot-ready columns using **your**
5/// crate's `polars` (#466): the macro expands in your crate, against the
6/// polars version you depend on, so it works whether you use polars 0.46,
7/// 0.52, 0.53 or later — no second polars build, no type mismatch, and
8/// ggplot-rs's own `polars` feature can stay off.
9///
10/// Returns `PolarsResult<Vec<(String, Vec<Value>)>>`, which is
11/// [`GGData`](crate::data::GGData), so it goes straight into
12/// [`GGPlot::new`](crate::GGPlot::new):
13///
14/// ```ignore
15/// use ggplot_rs::prelude::*;
16/// let df = polars::df!("x" => [1, 2, 3], "y" => [2.0, 1.0, 3.0])?;
17/// let plot = GGPlot::new(ggplot_rs::from_polars!(&df)?)
18///     .aes(Aes::new().x("x").y("y"))
19///     .geom_point();
20/// ```
21///
22/// Booleans, strings, integers, floats, `Date` (→ seconds) and `Datetime`
23/// (any unit → seconds) convert natively; every other dtype (categoricals,
24/// decimals, nested …) is cast to its string form. Needs `polars` in scope
25/// as `::polars` with its (default) `dtype-date` / `dtype-datetime`
26/// features.
27#[macro_export]
28macro_rules! from_polars {
29    ($df:expr) => {{
30        (|| -> ::polars::prelude::PolarsResult<::std::vec::Vec<(::std::string::String, ::std::vec::Vec<$crate::data::Value>)>> {
31            use ::polars::prelude::{DataType as D, TimeUnit as U};
32            use $crate::data::Value as V;
33            let df = $df;
34            let mut out = ::std::vec::Vec::new();
35            for name in df.get_column_names() {
36                let name = name.to_string();
37                let c = df.column(&name)?;
38                let values: ::std::vec::Vec<V> = match c.dtype() {
39                    D::Boolean => c.bool()?.into_iter().map(|v| v.map_or(V::Na, V::Bool)).collect(),
40                    D::String => c
41                        .str()?
42                        .into_iter()
43                        .map(|v| v.map_or(V::Na, |s| V::Str(s.to_string())))
44                        .collect(),
45                    D::Float32 | D::Float64 => {
46                        let cc = c.cast(&D::Float64)?;
47                        let v = cc.f64()?.into_iter().map(|v| v.map_or(V::Na, V::Float)).collect();
48                        v
49                    }
50                    D::Int8 | D::Int16 | D::Int32 | D::Int64 | D::UInt8 | D::UInt16 | D::UInt32
51                    | D::UInt64 => {
52                        let cc = c.cast(&D::Int64)?;
53                        let v = cc.i64()?.into_iter().map(|v| v.map_or(V::Na, V::Integer)).collect();
54                        v
55                    }
56                    D::Date => {
57                        let cc = c.cast(&D::Int32)?;
58                        let v = cc
59                            .i32()?
60                            .into_iter()
61                            .map(|v| v.map_or(V::Na, |d| V::DateTime(d as i64 * 86_400)))
62                            .collect();
63                        v
64                    }
65                    D::Datetime(unit, _) => {
66                        let per_second: i64 = match unit {
67                            U::Nanoseconds => 1_000_000_000,
68                            U::Microseconds => 1_000_000,
69                            U::Milliseconds => 1_000,
70                        };
71                        let cc = c.cast(&D::Int64)?;
72                        let v = cc
73                            .i64()?
74                            .into_iter()
75                            .map(|v| v.map_or(V::Na, |t| V::DateTime(t.div_euclid(per_second))))
76                            .collect();
77                        v
78                    }
79                    _ => {
80                        let cc = c.cast(&D::String)?;
81                        let v = cc
82                            .str()?
83                            .into_iter()
84                            .map(|v| v.map_or(V::Na, |s| V::Str(s.to_string())))
85                            .collect();
86                        v
87                    }
88                };
89                out.push((name, values));
90            }
91            Ok(out)
92        })()
93    }};
94}