polars_core/series/ops/
downcast.rs

1#![allow(unsafe_op_in_unsafe_fn)]
2use crate::prelude::*;
3use crate::series::implementations::null::NullChunked;
4
5macro_rules! unpack_chunked_err {
6    ($series:expr => $name:expr) => {
7        polars_err!(SchemaMismatch: "invalid series dtype: expected `{}`, got `{}` for series with name `{}`", $name, $series.dtype(), $series.name())
8    };
9}
10
11macro_rules! try_unpack_chunked {
12    ($series:expr, $expected:pat $(if $guard: expr)? => $ca:ty) => {
13        match $series.dtype() {
14            $expected $(if $guard)? => {
15                // Check downcast in debug compiles
16                #[cfg(debug_assertions)]
17                {
18                    Some($series.as_ref().as_any().downcast_ref::<$ca>().unwrap())
19                }
20                #[cfg(not(debug_assertions))]
21                unsafe {
22                    Some(&*($series.as_ref() as *const dyn SeriesTrait as *const $ca))
23                }
24            },
25            _ => None,
26        }
27    };
28}
29
30impl Series {
31    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Int8`]
32    pub fn try_i8(&self) -> Option<&Int8Chunked> {
33        try_unpack_chunked!(self, DataType::Int8 => Int8Chunked)
34    }
35
36    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Int16`]
37    pub fn try_i16(&self) -> Option<&Int16Chunked> {
38        try_unpack_chunked!(self, DataType::Int16 => Int16Chunked)
39    }
40
41    /// Unpack to [`ChunkedArray`]
42    /// ```
43    /// # use polars_core::prelude::*;
44    /// let s = Series::new("foo".into(), [1i32 ,2, 3]);
45    /// let s_squared: Series = s.i32()
46    ///     .unwrap()
47    ///     .into_iter()
48    ///     .map(|opt_v| {
49    ///         match opt_v {
50    ///             Some(v) => Some(v * v),
51    ///             None => None, // null value
52    ///         }
53    /// }).collect();
54    /// ```
55    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Int32`]
56    pub fn try_i32(&self) -> Option<&Int32Chunked> {
57        try_unpack_chunked!(self, DataType::Int32 => Int32Chunked)
58    }
59
60    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Int64`]
61    pub fn try_i64(&self) -> Option<&Int64Chunked> {
62        try_unpack_chunked!(self, DataType::Int64 => Int64Chunked)
63    }
64
65    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Int128`]
66    #[cfg(feature = "dtype-i128")]
67    pub fn try_i128(&self) -> Option<&Int128Chunked> {
68        try_unpack_chunked!(self, DataType::Int128 => Int128Chunked)
69    }
70
71    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Float32`]
72    pub fn try_f32(&self) -> Option<&Float32Chunked> {
73        try_unpack_chunked!(self, DataType::Float32 => Float32Chunked)
74    }
75
76    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Float64`]
77    pub fn try_f64(&self) -> Option<&Float64Chunked> {
78        try_unpack_chunked!(self, DataType::Float64 => Float64Chunked)
79    }
80
81    /// Unpack to [`ChunkedArray`] of dtype [`DataType::UInt8`]
82    pub fn try_u8(&self) -> Option<&UInt8Chunked> {
83        try_unpack_chunked!(self, DataType::UInt8 => UInt8Chunked)
84    }
85
86    /// Unpack to [`ChunkedArray`] of dtype [`DataType::UInt16`]
87    pub fn try_u16(&self) -> Option<&UInt16Chunked> {
88        try_unpack_chunked!(self, DataType::UInt16 => UInt16Chunked)
89    }
90
91    /// Unpack to [`ChunkedArray`] of dtype [`DataType::UInt32`]
92    pub fn try_u32(&self) -> Option<&UInt32Chunked> {
93        try_unpack_chunked!(self, DataType::UInt32 => UInt32Chunked)
94    }
95
96    /// Unpack to [`ChunkedArray`] of dtype [`DataType::UInt64`]
97    pub fn try_u64(&self) -> Option<&UInt64Chunked> {
98        try_unpack_chunked!(self, DataType::UInt64 => UInt64Chunked)
99    }
100
101    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Boolean`]
102    pub fn try_bool(&self) -> Option<&BooleanChunked> {
103        try_unpack_chunked!(self, DataType::Boolean => BooleanChunked)
104    }
105
106    /// Unpack to [`ChunkedArray`] of dtype [`DataType::String`]
107    pub fn try_str(&self) -> Option<&StringChunked> {
108        try_unpack_chunked!(self, DataType::String => StringChunked)
109    }
110
111    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Binary`]
112    pub fn try_binary(&self) -> Option<&BinaryChunked> {
113        try_unpack_chunked!(self, DataType::Binary => BinaryChunked)
114    }
115
116    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Binary`]
117    pub fn try_binary_offset(&self) -> Option<&BinaryOffsetChunked> {
118        try_unpack_chunked!(self, DataType::BinaryOffset => BinaryOffsetChunked)
119    }
120
121    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Time`]
122    #[cfg(feature = "dtype-time")]
123    pub fn try_time(&self) -> Option<&TimeChunked> {
124        try_unpack_chunked!(self, DataType::Time => TimeChunked)
125    }
126
127    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Date`]
128    #[cfg(feature = "dtype-date")]
129    pub fn try_date(&self) -> Option<&DateChunked> {
130        try_unpack_chunked!(self, DataType::Date => DateChunked)
131    }
132
133    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Datetime`]
134    #[cfg(feature = "dtype-datetime")]
135    pub fn try_datetime(&self) -> Option<&DatetimeChunked> {
136        try_unpack_chunked!(self, DataType::Datetime(_, _) => DatetimeChunked)
137    }
138
139    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Duration`]
140    #[cfg(feature = "dtype-duration")]
141    pub fn try_duration(&self) -> Option<&DurationChunked> {
142        try_unpack_chunked!(self, DataType::Duration(_) => DurationChunked)
143    }
144
145    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Decimal`]
146    #[cfg(feature = "dtype-decimal")]
147    pub fn try_decimal(&self) -> Option<&DecimalChunked> {
148        try_unpack_chunked!(self, DataType::Decimal(_, _) => DecimalChunked)
149    }
150
151    /// Unpack to [`ChunkedArray`] of dtype list
152    pub fn try_list(&self) -> Option<&ListChunked> {
153        try_unpack_chunked!(self, DataType::List(_) => ListChunked)
154    }
155
156    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Array`]
157    #[cfg(feature = "dtype-array")]
158    pub fn try_array(&self) -> Option<&ArrayChunked> {
159        try_unpack_chunked!(self, DataType::Array(_, _) => ArrayChunked)
160    }
161
162    #[cfg(feature = "dtype-categorical")]
163    pub fn try_cat<T: PolarsCategoricalType>(&self) -> Option<&CategoricalChunked<T>> {
164        try_unpack_chunked!(self, dt @ DataType::Enum(_, _) | dt @ DataType::Categorical(_, _) if dt.cat_physical().unwrap() == T::physical() => CategoricalChunked<T>)
165    }
166
167    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Categorical`] or [`DataType::Enum`] with a physical type of UInt8.
168    #[cfg(feature = "dtype-categorical")]
169    pub fn try_cat8(&self) -> Option<&Categorical8Chunked> {
170        self.try_cat::<Categorical8Type>()
171    }
172
173    #[cfg(feature = "dtype-categorical")]
174    pub fn try_cat16(&self) -> Option<&Categorical16Chunked> {
175        self.try_cat::<Categorical16Type>()
176    }
177
178    #[cfg(feature = "dtype-categorical")]
179    pub fn try_cat32(&self) -> Option<&Categorical32Chunked> {
180        self.try_cat::<Categorical32Type>()
181    }
182
183    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Struct`]
184    #[cfg(feature = "dtype-struct")]
185    pub fn try_struct(&self) -> Option<&StructChunked> {
186        #[cfg(debug_assertions)]
187        {
188            if let DataType::Struct(_) = self.dtype() {
189                let any = self.as_any();
190                assert!(any.is::<StructChunked>());
191            }
192        }
193        try_unpack_chunked!(self, DataType::Struct(_) => StructChunked)
194    }
195
196    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Null`]
197    pub fn try_null(&self) -> Option<&NullChunked> {
198        try_unpack_chunked!(self, DataType::Null => NullChunked)
199    }
200    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Int8`]
201    pub fn i8(&self) -> PolarsResult<&Int8Chunked> {
202        self.try_i8()
203            .ok_or_else(|| unpack_chunked_err!(self => "Int8"))
204    }
205
206    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Int16`]
207    pub fn i16(&self) -> PolarsResult<&Int16Chunked> {
208        self.try_i16()
209            .ok_or_else(|| unpack_chunked_err!(self => "Int16"))
210    }
211
212    /// Unpack to [`ChunkedArray`]
213    /// ```
214    /// # use polars_core::prelude::*;
215    /// let s = Series::new("foo".into(), [1i32 ,2, 3]);
216    /// let s_squared: Series = s.i32()
217    ///     .unwrap()
218    ///     .into_iter()
219    ///     .map(|opt_v| {
220    ///         match opt_v {
221    ///             Some(v) => Some(v * v),
222    ///             None => None, // null value
223    ///         }
224    /// }).collect();
225    /// ```
226    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Int32`]
227    pub fn i32(&self) -> PolarsResult<&Int32Chunked> {
228        self.try_i32()
229            .ok_or_else(|| unpack_chunked_err!(self => "Int32"))
230    }
231
232    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Int64`]
233    pub fn i64(&self) -> PolarsResult<&Int64Chunked> {
234        self.try_i64()
235            .ok_or_else(|| unpack_chunked_err!(self => "Int64"))
236    }
237
238    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Int128`]
239    #[cfg(feature = "dtype-i128")]
240    pub fn i128(&self) -> PolarsResult<&Int128Chunked> {
241        self.try_i128()
242            .ok_or_else(|| unpack_chunked_err!(self => "Int128"))
243    }
244
245    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Float32`]
246    pub fn f32(&self) -> PolarsResult<&Float32Chunked> {
247        self.try_f32()
248            .ok_or_else(|| unpack_chunked_err!(self => "Float32"))
249    }
250
251    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Float64`]
252    pub fn f64(&self) -> PolarsResult<&Float64Chunked> {
253        self.try_f64()
254            .ok_or_else(|| unpack_chunked_err!(self => "Float64"))
255    }
256
257    /// Unpack to [`ChunkedArray`] of dtype [`DataType::UInt8`]
258    pub fn u8(&self) -> PolarsResult<&UInt8Chunked> {
259        self.try_u8()
260            .ok_or_else(|| unpack_chunked_err!(self => "UInt8"))
261    }
262
263    /// Unpack to [`ChunkedArray`] of dtype [`DataType::UInt16`]
264    pub fn u16(&self) -> PolarsResult<&UInt16Chunked> {
265        self.try_u16()
266            .ok_or_else(|| unpack_chunked_err!(self => "UInt16"))
267    }
268
269    /// Unpack to [`ChunkedArray`] of dtype [`DataType::UInt32`]
270    pub fn u32(&self) -> PolarsResult<&UInt32Chunked> {
271        self.try_u32()
272            .ok_or_else(|| unpack_chunked_err!(self => "UInt32"))
273    }
274
275    /// Unpack to [`ChunkedArray`] of dtype [`DataType::UInt64`]
276    pub fn u64(&self) -> PolarsResult<&UInt64Chunked> {
277        self.try_u64()
278            .ok_or_else(|| unpack_chunked_err!(self => "UInt64"))
279    }
280
281    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Boolean`]
282    pub fn bool(&self) -> PolarsResult<&BooleanChunked> {
283        self.try_bool()
284            .ok_or_else(|| unpack_chunked_err!(self => "Boolean"))
285    }
286
287    /// Unpack to [`ChunkedArray`] of dtype [`DataType::String`]
288    pub fn str(&self) -> PolarsResult<&StringChunked> {
289        self.try_str()
290            .ok_or_else(|| unpack_chunked_err!(self => "String"))
291    }
292
293    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Binary`]
294    pub fn binary(&self) -> PolarsResult<&BinaryChunked> {
295        self.try_binary()
296            .ok_or_else(|| unpack_chunked_err!(self => "Binary"))
297    }
298
299    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Binary`]
300    pub fn binary_offset(&self) -> PolarsResult<&BinaryOffsetChunked> {
301        self.try_binary_offset()
302            .ok_or_else(|| unpack_chunked_err!(self => "BinaryOffset"))
303    }
304
305    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Time`]
306    #[cfg(feature = "dtype-time")]
307    pub fn time(&self) -> PolarsResult<&TimeChunked> {
308        self.try_time()
309            .ok_or_else(|| unpack_chunked_err!(self => "Time"))
310    }
311
312    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Date`]
313    #[cfg(feature = "dtype-date")]
314    pub fn date(&self) -> PolarsResult<&DateChunked> {
315        self.try_date()
316            .ok_or_else(|| unpack_chunked_err!(self => "Date"))
317    }
318
319    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Datetime`]
320    #[cfg(feature = "dtype-datetime")]
321    pub fn datetime(&self) -> PolarsResult<&DatetimeChunked> {
322        self.try_datetime()
323            .ok_or_else(|| unpack_chunked_err!(self => "Datetime"))
324    }
325
326    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Duration`]
327    #[cfg(feature = "dtype-duration")]
328    pub fn duration(&self) -> PolarsResult<&DurationChunked> {
329        self.try_duration()
330            .ok_or_else(|| unpack_chunked_err!(self => "Duration"))
331    }
332
333    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Decimal`]
334    #[cfg(feature = "dtype-decimal")]
335    pub fn decimal(&self) -> PolarsResult<&DecimalChunked> {
336        self.try_decimal()
337            .ok_or_else(|| unpack_chunked_err!(self => "Decimal"))
338    }
339
340    /// Unpack to [`ChunkedArray`] of dtype list
341    pub fn list(&self) -> PolarsResult<&ListChunked> {
342        self.try_list()
343            .ok_or_else(|| unpack_chunked_err!(self => "List"))
344    }
345
346    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Array`]
347    #[cfg(feature = "dtype-array")]
348    pub fn array(&self) -> PolarsResult<&ArrayChunked> {
349        self.try_array()
350            .ok_or_else(|| unpack_chunked_err!(self => "Array"))
351    }
352
353    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Categorical`] or [`DataType::Enum`].
354    #[cfg(feature = "dtype-categorical")]
355    pub fn cat<T: PolarsCategoricalType>(&self) -> PolarsResult<&CategoricalChunked<T>> {
356        self.try_cat::<T>()
357            .ok_or_else(|| unpack_chunked_err!(self => "Enum | Categorical"))
358    }
359
360    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Categorical`] or [`DataType::Enum`] with a physical type of UInt8.
361    #[cfg(feature = "dtype-categorical")]
362    pub fn cat8(&self) -> PolarsResult<&CategoricalChunked<Categorical8Type>> {
363        self.try_cat8()
364            .ok_or_else(|| unpack_chunked_err!(self => "Enum8 | Categorical8"))
365    }
366
367    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Categorical`] or [`DataType::Enum`] with a physical type of UInt16.
368    #[cfg(feature = "dtype-categorical")]
369    pub fn cat16(&self) -> PolarsResult<&CategoricalChunked<Categorical16Type>> {
370        self.try_cat16()
371            .ok_or_else(|| unpack_chunked_err!(self => "Enum16 | Categorical16"))
372    }
373
374    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Categorical`] or [`DataType::Enum`] with a physical type of UInt32.
375    #[cfg(feature = "dtype-categorical")]
376    pub fn cat32(&self) -> PolarsResult<&CategoricalChunked<Categorical32Type>> {
377        self.try_cat32()
378            .ok_or_else(|| unpack_chunked_err!(self => "Enum32 | Categorical32"))
379    }
380
381    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Struct`]
382    #[cfg(feature = "dtype-struct")]
383    pub fn struct_(&self) -> PolarsResult<&StructChunked> {
384        #[cfg(debug_assertions)]
385        {
386            if let DataType::Struct(_) = self.dtype() {
387                let any = self.as_any();
388                assert!(any.is::<StructChunked>());
389            }
390        }
391
392        self.try_struct()
393            .ok_or_else(|| unpack_chunked_err!(self => "Struct"))
394    }
395
396    /// Unpack to [`ChunkedArray`] of dtype [`DataType::Null`]
397    pub fn null(&self) -> PolarsResult<&NullChunked> {
398        self.try_null()
399            .ok_or_else(|| unpack_chunked_err!(self => "Null"))
400    }
401}