polars_core/series/ops/
downcast.rs

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