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