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}