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