polars_core/
named_from.rs

1use std::borrow::Cow;
2
3#[cfg(feature = "dtype-duration")]
4use chrono::Duration as ChronoDuration;
5#[cfg(feature = "dtype-date")]
6use chrono::NaiveDate;
7#[cfg(feature = "dtype-datetime")]
8use chrono::NaiveDateTime;
9#[cfg(feature = "dtype-time")]
10use chrono::NaiveTime;
11
12use crate::chunked_array::builder::get_list_builder;
13use crate::prelude::*;
14
15pub trait NamedFrom<T, Phantom: ?Sized> {
16    /// Initialize by name and values.
17    fn new(name: PlSmallStr, _: T) -> Self;
18}
19
20pub trait NamedFromOwned<T> {
21    /// Initialize by name and values.
22    fn from_vec(name: PlSmallStr, _: T) -> Self;
23}
24
25macro_rules! impl_named_from_owned {
26    ($type:ty, $polars_type:ident) => {
27        impl NamedFromOwned<$type> for Series {
28            fn from_vec(name: PlSmallStr, v: $type) -> Self {
29                ChunkedArray::<$polars_type>::from_vec(name, v).into_series()
30            }
31        }
32    };
33}
34
35#[cfg(feature = "dtype-i8")]
36impl_named_from_owned!(Vec<i8>, Int8Type);
37#[cfg(feature = "dtype-i16")]
38impl_named_from_owned!(Vec<i16>, Int16Type);
39impl_named_from_owned!(Vec<i32>, Int32Type);
40impl_named_from_owned!(Vec<i64>, Int64Type);
41#[cfg(feature = "dtype-i128")]
42impl_named_from_owned!(Vec<i128>, Int128Type);
43#[cfg(feature = "dtype-u8")]
44impl_named_from_owned!(Vec<u8>, UInt8Type);
45#[cfg(feature = "dtype-u16")]
46impl_named_from_owned!(Vec<u16>, UInt16Type);
47impl_named_from_owned!(Vec<u32>, UInt32Type);
48impl_named_from_owned!(Vec<u64>, UInt64Type);
49impl_named_from_owned!(Vec<f32>, Float32Type);
50impl_named_from_owned!(Vec<f64>, Float64Type);
51
52macro_rules! impl_named_from {
53    ($type:ty, $polars_type:ident, $method:ident) => {
54        impl<T: AsRef<$type>> NamedFrom<T, $type> for Series {
55            fn new(name: PlSmallStr, v: T) -> Self {
56                ChunkedArray::<$polars_type>::$method(name, v.as_ref()).into_series()
57            }
58        }
59        impl<T: AsRef<$type>> NamedFrom<T, $type> for ChunkedArray<$polars_type> {
60            fn new(name: PlSmallStr, v: T) -> Self {
61                ChunkedArray::<$polars_type>::$method(name, v.as_ref())
62            }
63        }
64    };
65}
66
67impl_named_from!([String], StringType, from_slice);
68impl_named_from!([Vec<u8>], BinaryType, from_slice);
69impl_named_from!([bool], BooleanType, from_slice);
70#[cfg(feature = "dtype-u8")]
71impl_named_from!([u8], UInt8Type, from_slice);
72#[cfg(feature = "dtype-u16")]
73impl_named_from!([u16], UInt16Type, from_slice);
74impl_named_from!([u32], UInt32Type, from_slice);
75impl_named_from!([u64], UInt64Type, from_slice);
76#[cfg(feature = "dtype-i8")]
77impl_named_from!([i8], Int8Type, from_slice);
78#[cfg(feature = "dtype-i16")]
79impl_named_from!([i16], Int16Type, from_slice);
80impl_named_from!([i32], Int32Type, from_slice);
81impl_named_from!([i64], Int64Type, from_slice);
82#[cfg(feature = "dtype-decimal")]
83impl_named_from!([i128], Int128Type, from_slice);
84impl_named_from!([f32], Float32Type, from_slice);
85impl_named_from!([f64], Float64Type, from_slice);
86impl_named_from!([Option<String>], StringType, from_slice_options);
87impl_named_from!([Option<Vec<u8>>], BinaryType, from_slice_options);
88impl_named_from!([Option<bool>], BooleanType, from_slice_options);
89#[cfg(feature = "dtype-u8")]
90impl_named_from!([Option<u8>], UInt8Type, from_slice_options);
91#[cfg(feature = "dtype-u16")]
92impl_named_from!([Option<u16>], UInt16Type, from_slice_options);
93impl_named_from!([Option<u32>], UInt32Type, from_slice_options);
94impl_named_from!([Option<u64>], UInt64Type, from_slice_options);
95#[cfg(feature = "dtype-i8")]
96impl_named_from!([Option<i8>], Int8Type, from_slice_options);
97#[cfg(feature = "dtype-i16")]
98impl_named_from!([Option<i16>], Int16Type, from_slice_options);
99impl_named_from!([Option<i32>], Int32Type, from_slice_options);
100impl_named_from!([Option<i64>], Int64Type, from_slice_options);
101#[cfg(feature = "dtype-decimal")]
102impl_named_from!([Option<i128>], Int128Type, from_slice_options);
103impl_named_from!([Option<f32>], Float32Type, from_slice_options);
104impl_named_from!([Option<f64>], Float64Type, from_slice_options);
105
106macro_rules! impl_named_from_range {
107    ($range:ty, $polars_type:ident) => {
108        impl NamedFrom<$range, $polars_type> for ChunkedArray<$polars_type> {
109            fn new(name: PlSmallStr, range: $range) -> Self {
110                let values = range.collect::<Vec<_>>();
111                ChunkedArray::<$polars_type>::from_vec(name, values)
112            }
113        }
114
115        impl NamedFrom<$range, $polars_type> for Series {
116            fn new(name: PlSmallStr, range: $range) -> Self {
117                ChunkedArray::new(name, range).into_series()
118            }
119        }
120    };
121}
122impl_named_from_range!(std::ops::Range<i64>, Int64Type);
123impl_named_from_range!(std::ops::Range<i32>, Int32Type);
124impl_named_from_range!(std::ops::Range<u64>, UInt64Type);
125impl_named_from_range!(std::ops::Range<u32>, UInt32Type);
126
127impl<T: AsRef<[Series]>> NamedFrom<T, ListType> for Series {
128    fn new(name: PlSmallStr, s: T) -> Self {
129        let series_slice = s.as_ref();
130        let list_cap = series_slice.len();
131
132        if series_slice.is_empty() {
133            return Series::new_empty(name, &DataType::Null);
134        }
135
136        let dt = series_slice[0].dtype();
137
138        let values_cap = series_slice.iter().fold(0, |acc, s| acc + s.len());
139
140        let mut builder = get_list_builder(dt, values_cap, list_cap, name);
141        for series in series_slice {
142            builder.append_series(series).unwrap();
143        }
144        builder.finish().into_series()
145    }
146}
147
148impl<T: AsRef<[Option<Series>]>> NamedFrom<T, [Option<Series>]> for Series {
149    fn new(name: PlSmallStr, s: T) -> Self {
150        let series_slice = s.as_ref();
151        let values_cap = series_slice.iter().fold(0, |acc, opt_s| {
152            acc + opt_s.as_ref().map(|s| s.len()).unwrap_or(0)
153        });
154        let dt = match series_slice.iter().filter_map(|opt| opt.as_ref()).next() {
155            Some(series) => series.dtype(),
156            None => &DataType::Null,
157        };
158
159        let mut builder = get_list_builder(dt, values_cap, series_slice.len(), name);
160        for series in series_slice {
161            builder.append_opt_series(series.as_ref()).unwrap();
162        }
163        builder.finish().into_series()
164    }
165}
166impl<'a, T: AsRef<[&'a str]>> NamedFrom<T, [&'a str]> for Series {
167    fn new(name: PlSmallStr, v: T) -> Self {
168        StringChunked::from_slice(name, v.as_ref()).into_series()
169    }
170}
171
172impl NamedFrom<&Series, str> for Series {
173    fn new(name: PlSmallStr, s: &Series) -> Self {
174        let mut s = s.clone();
175        s.rename(name);
176        s
177    }
178}
179
180impl<'a, T: AsRef<[&'a str]>> NamedFrom<T, [&'a str]> for StringChunked {
181    fn new(name: PlSmallStr, v: T) -> Self {
182        StringChunked::from_slice(name, v.as_ref())
183    }
184}
185
186impl<'a, T: AsRef<[Option<&'a str>]>> NamedFrom<T, [Option<&'a str>]> for Series {
187    fn new(name: PlSmallStr, v: T) -> Self {
188        StringChunked::from_slice_options(name, v.as_ref()).into_series()
189    }
190}
191
192impl<'a, T: AsRef<[Option<&'a str>]>> NamedFrom<T, [Option<&'a str>]> for StringChunked {
193    fn new(name: PlSmallStr, v: T) -> Self {
194        StringChunked::from_slice_options(name, v.as_ref())
195    }
196}
197
198impl<'a, T: AsRef<[Cow<'a, str>]>> NamedFrom<T, [Cow<'a, str>]> for Series {
199    fn new(name: PlSmallStr, v: T) -> Self {
200        StringChunked::from_iter_values(name, v.as_ref().iter().map(|value| value.as_ref()))
201            .into_series()
202    }
203}
204
205impl<'a, T: AsRef<[Cow<'a, str>]>> NamedFrom<T, [Cow<'a, str>]> for StringChunked {
206    fn new(name: PlSmallStr, v: T) -> Self {
207        StringChunked::from_iter_values(name, v.as_ref().iter().map(|value| value.as_ref()))
208    }
209}
210
211impl<'a, T: AsRef<[Option<Cow<'a, str>>]>> NamedFrom<T, [Option<Cow<'a, str>>]> for Series {
212    fn new(name: PlSmallStr, v: T) -> Self {
213        StringChunked::new(name, v).into_series()
214    }
215}
216
217impl<'a, T: AsRef<[Option<Cow<'a, str>>]>> NamedFrom<T, [Option<Cow<'a, str>>]> for StringChunked {
218    fn new(name: PlSmallStr, v: T) -> Self {
219        StringChunked::from_iter_options(
220            name,
221            v.as_ref()
222                .iter()
223                .map(|opt| opt.as_ref().map(|value| value.as_ref())),
224        )
225    }
226}
227
228impl<'a, T: AsRef<[&'a [u8]]>> NamedFrom<T, [&'a [u8]]> for Series {
229    fn new(name: PlSmallStr, v: T) -> Self {
230        BinaryChunked::from_slice(name, v.as_ref()).into_series()
231    }
232}
233
234impl<'a, T: AsRef<[&'a [u8]]>> NamedFrom<T, [&'a [u8]]> for BinaryChunked {
235    fn new(name: PlSmallStr, v: T) -> Self {
236        BinaryChunked::from_slice(name, v.as_ref())
237    }
238}
239
240impl<'a, T: AsRef<[Option<&'a [u8]>]>> NamedFrom<T, [Option<&'a [u8]>]> for Series {
241    fn new(name: PlSmallStr, v: T) -> Self {
242        BinaryChunked::from_slice_options(name, v.as_ref()).into_series()
243    }
244}
245
246impl<'a, T: AsRef<[Option<&'a [u8]>]>> NamedFrom<T, [Option<&'a [u8]>]> for BinaryChunked {
247    fn new(name: PlSmallStr, v: T) -> Self {
248        BinaryChunked::from_slice_options(name, v.as_ref())
249    }
250}
251
252impl<'a, T: AsRef<[Cow<'a, [u8]>]>> NamedFrom<T, [Cow<'a, [u8]>]> for Series {
253    fn new(name: PlSmallStr, v: T) -> Self {
254        BinaryChunked::from_iter_values(name, v.as_ref().iter().map(|value| value.as_ref()))
255            .into_series()
256    }
257}
258
259impl<'a, T: AsRef<[Cow<'a, [u8]>]>> NamedFrom<T, [Cow<'a, [u8]>]> for BinaryChunked {
260    fn new(name: PlSmallStr, v: T) -> Self {
261        BinaryChunked::from_iter_values(name, v.as_ref().iter().map(|value| value.as_ref()))
262    }
263}
264
265impl<'a, T: AsRef<[Option<Cow<'a, [u8]>>]>> NamedFrom<T, [Option<Cow<'a, [u8]>>]> for Series {
266    fn new(name: PlSmallStr, v: T) -> Self {
267        BinaryChunked::new(name, v).into_series()
268    }
269}
270
271impl<'a, T: AsRef<[Option<Cow<'a, [u8]>>]>> NamedFrom<T, [Option<Cow<'a, [u8]>>]>
272    for BinaryChunked
273{
274    fn new(name: PlSmallStr, v: T) -> Self {
275        BinaryChunked::from_iter_options(
276            name,
277            v.as_ref()
278                .iter()
279                .map(|opt| opt.as_ref().map(|value| value.as_ref())),
280        )
281    }
282}
283
284#[cfg(feature = "dtype-date")]
285impl<T: AsRef<[NaiveDate]>> NamedFrom<T, [NaiveDate]> for DateChunked {
286    fn new(name: PlSmallStr, v: T) -> Self {
287        DateChunked::from_naive_date(name, v.as_ref().iter().copied())
288    }
289}
290
291#[cfg(feature = "dtype-date")]
292impl<T: AsRef<[NaiveDate]>> NamedFrom<T, [NaiveDate]> for Series {
293    fn new(name: PlSmallStr, v: T) -> Self {
294        DateChunked::new(name, v).into_series()
295    }
296}
297
298#[cfg(feature = "dtype-date")]
299impl<T: AsRef<[Option<NaiveDate>]>> NamedFrom<T, [Option<NaiveDate>]> for DateChunked {
300    fn new(name: PlSmallStr, v: T) -> Self {
301        DateChunked::from_naive_date_options(name, v.as_ref().iter().copied())
302    }
303}
304
305#[cfg(feature = "dtype-date")]
306impl<T: AsRef<[Option<NaiveDate>]>> NamedFrom<T, [Option<NaiveDate>]> for Series {
307    fn new(name: PlSmallStr, v: T) -> Self {
308        DateChunked::new(name, v).into_series()
309    }
310}
311
312#[cfg(feature = "dtype-datetime")]
313impl<T: AsRef<[NaiveDateTime]>> NamedFrom<T, [NaiveDateTime]> for DatetimeChunked {
314    fn new(name: PlSmallStr, v: T) -> Self {
315        DatetimeChunked::from_naive_datetime(
316            name,
317            v.as_ref().iter().copied(),
318            TimeUnit::Milliseconds,
319        )
320    }
321}
322
323#[cfg(feature = "dtype-datetime")]
324impl<T: AsRef<[NaiveDateTime]>> NamedFrom<T, [NaiveDateTime]> for Series {
325    fn new(name: PlSmallStr, v: T) -> Self {
326        DatetimeChunked::new(name, v).into_series()
327    }
328}
329
330#[cfg(feature = "dtype-datetime")]
331impl<T: AsRef<[Option<NaiveDateTime>]>> NamedFrom<T, [Option<NaiveDateTime>]> for DatetimeChunked {
332    fn new(name: PlSmallStr, v: T) -> Self {
333        DatetimeChunked::from_naive_datetime_options(
334            name,
335            v.as_ref().iter().copied(),
336            TimeUnit::Milliseconds,
337        )
338    }
339}
340
341#[cfg(feature = "dtype-datetime")]
342impl<T: AsRef<[Option<NaiveDateTime>]>> NamedFrom<T, [Option<NaiveDateTime>]> for Series {
343    fn new(name: PlSmallStr, v: T) -> Self {
344        DatetimeChunked::new(name, v).into_series()
345    }
346}
347
348#[cfg(feature = "dtype-duration")]
349impl<T: AsRef<[ChronoDuration]>> NamedFrom<T, [ChronoDuration]> for DurationChunked {
350    fn new(name: PlSmallStr, v: T) -> Self {
351        DurationChunked::from_duration(name, v.as_ref().iter().copied(), TimeUnit::Nanoseconds)
352    }
353}
354
355#[cfg(feature = "dtype-duration")]
356impl<T: AsRef<[ChronoDuration]>> NamedFrom<T, [ChronoDuration]> for Series {
357    fn new(name: PlSmallStr, v: T) -> Self {
358        DurationChunked::new(name, v).into_series()
359    }
360}
361
362#[cfg(feature = "dtype-duration")]
363impl<T: AsRef<[Option<ChronoDuration>]>> NamedFrom<T, [Option<ChronoDuration>]>
364    for DurationChunked
365{
366    fn new(name: PlSmallStr, v: T) -> Self {
367        DurationChunked::from_duration_options(
368            name,
369            v.as_ref().iter().copied(),
370            TimeUnit::Nanoseconds,
371        )
372    }
373}
374
375#[cfg(feature = "dtype-duration")]
376impl<T: AsRef<[Option<ChronoDuration>]>> NamedFrom<T, [Option<ChronoDuration>]> for Series {
377    fn new(name: PlSmallStr, v: T) -> Self {
378        DurationChunked::new(name, v).into_series()
379    }
380}
381
382#[cfg(feature = "dtype-time")]
383impl<T: AsRef<[NaiveTime]>> NamedFrom<T, [NaiveTime]> for TimeChunked {
384    fn new(name: PlSmallStr, v: T) -> Self {
385        TimeChunked::from_naive_time(name, v.as_ref().iter().copied())
386    }
387}
388
389#[cfg(feature = "dtype-time")]
390impl<T: AsRef<[NaiveTime]>> NamedFrom<T, [NaiveTime]> for Series {
391    fn new(name: PlSmallStr, v: T) -> Self {
392        TimeChunked::new(name, v).into_series()
393    }
394}
395
396#[cfg(feature = "dtype-time")]
397impl<T: AsRef<[Option<NaiveTime>]>> NamedFrom<T, [Option<NaiveTime>]> for TimeChunked {
398    fn new(name: PlSmallStr, v: T) -> Self {
399        TimeChunked::from_naive_time_options(name, v.as_ref().iter().copied())
400    }
401}
402
403#[cfg(feature = "dtype-time")]
404impl<T: AsRef<[Option<NaiveTime>]>> NamedFrom<T, [Option<NaiveTime>]> for Series {
405    fn new(name: PlSmallStr, v: T) -> Self {
406        TimeChunked::new(name, v).into_series()
407    }
408}
409
410#[cfg(feature = "object")]
411impl<T: PolarsObject> NamedFrom<&[T], &[T]> for ObjectChunked<T> {
412    fn new(name: PlSmallStr, v: &[T]) -> Self {
413        ObjectChunked::from_slice(name, v)
414    }
415}
416
417#[cfg(feature = "object")]
418impl<T: PolarsObject, S: AsRef<[Option<T>]>> NamedFrom<S, [Option<T>]> for ObjectChunked<T> {
419    fn new(name: PlSmallStr, v: S) -> Self {
420        ObjectChunked::from_slice_options(name, v.as_ref())
421    }
422}
423
424impl<T: PolarsNumericType> ChunkedArray<T> {
425    /// Specialization that prevents an allocation
426    /// prefer this over ChunkedArray::new when you have a `Vec<T::Native>` and no null values.
427    pub fn new_vec(name: PlSmallStr, v: Vec<T::Native>) -> Self {
428        ChunkedArray::from_vec(name, v)
429    }
430}
431
432/// For any [`ChunkedArray`] and [`Series`]
433impl<T: IntoSeries> NamedFrom<T, T> for Series {
434    fn new(name: PlSmallStr, t: T) -> Self {
435        let mut s = t.into_series();
436        s.rename(name);
437        s
438    }
439}
440
441#[cfg(test)]
442mod test {
443    use super::*;
444
445    #[cfg(all(
446        feature = "dtype-datetime",
447        feature = "dtype-duration",
448        feature = "dtype-date",
449        feature = "dtype-time"
450    ))]
451    #[test]
452    fn test_temporal_df_construction() {
453        // check if we can construct.
454        let _df = df![
455            "date" => [NaiveDate::from_ymd_opt(2021, 1, 1).unwrap()],
456            "datetime" => [NaiveDate::from_ymd_opt(2021, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap()],
457            "optional_date" => [Some(NaiveDate::from_ymd_opt(2021, 1, 1).unwrap())],
458            "optional_datetime" => [Some(NaiveDate::from_ymd_opt(2021, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap())],
459            "time" => [NaiveTime::from_hms_opt(23, 23, 23).unwrap()],
460            "optional_time" => [Some(NaiveTime::from_hms_opt(23, 23, 23).unwrap())],
461            "duration" => [ChronoDuration::from_std(std::time::Duration::from_secs(10)).unwrap()],
462            "optional_duration" => [Some(ChronoDuration::from_std(std::time::Duration::from_secs(10)).unwrap())],
463        ].unwrap();
464    }
465
466    #[test]
467    fn build_series_from_empty_series_vec() {
468        let empty_series = Series::new("test".into(), Vec::<Series>::new());
469        assert_eq!(empty_series.len(), 0);
470        assert_eq!(*empty_series.dtype(), DataType::Null);
471        assert_eq!(empty_series.name().as_str(), "test");
472    }
473}