polars_core/scalar/
mod.rs

1mod from;
2mod new;
3#[cfg(any(feature = "serde", feature = "dsl-schema"))]
4mod serde;
5
6use std::hash::Hash;
7
8use polars_error::PolarsResult;
9use polars_utils::IdxSize;
10use polars_utils::pl_str::PlSmallStr;
11
12use crate::chunked_array::cast::CastOptions;
13use crate::datatypes::{AnyValue, DataType};
14use crate::prelude::{Column, Series};
15
16#[derive(Clone, Debug, PartialEq)]
17pub struct Scalar {
18    dtype: DataType,
19    value: AnyValue<'static>,
20}
21
22impl Hash for Scalar {
23    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
24        self.dtype.hash(state);
25        self.value.hash_impl(state, true);
26    }
27}
28
29impl Default for Scalar {
30    fn default() -> Self {
31        Self {
32            dtype: DataType::Null,
33            value: AnyValue::Null,
34        }
35    }
36}
37
38impl Scalar {
39    #[inline(always)]
40    pub const fn new(dtype: DataType, value: AnyValue<'static>) -> Self {
41        Self { dtype, value }
42    }
43
44    pub const fn null(dtype: DataType) -> Self {
45        Self::new(dtype, AnyValue::Null)
46    }
47
48    pub fn new_idxsize(value: IdxSize) -> Self {
49        value.into()
50    }
51
52    pub fn cast_with_options(self, dtype: &DataType, options: CastOptions) -> PolarsResult<Self> {
53        if self.dtype() == dtype {
54            return Ok(self);
55        }
56
57        // @Optimize: If we have fully fleshed out casting semantics, we could just specify the
58        // cast on AnyValue.
59        let s = self
60            .into_series(PlSmallStr::from_static("scalar"))
61            .cast_with_options(dtype, options)?;
62        let value = s.get(0).unwrap();
63        Ok(Self::new(s.dtype().clone(), value.into_static()))
64    }
65
66    #[inline(always)]
67    pub fn is_null(&self) -> bool {
68        self.value.is_null()
69    }
70
71    #[inline(always)]
72    pub fn is_nan(&self) -> bool {
73        self.value.is_nan()
74    }
75
76    #[inline(always)]
77    pub fn into_value(self) -> AnyValue<'static> {
78        self.value
79    }
80
81    #[inline(always)]
82    pub fn value(&self) -> &AnyValue<'static> {
83        &self.value
84    }
85
86    pub fn as_any_value(&self) -> AnyValue<'_> {
87        self.value.clone()
88    }
89
90    pub fn into_series(self, name: PlSmallStr) -> Series {
91        Series::from_any_values_and_dtype(name, &[self.as_any_value()], &self.dtype, true).unwrap()
92    }
93
94    /// Turn a scalar into a column with `length=1`.
95    pub fn into_column(self, name: PlSmallStr) -> Column {
96        Column::new_scalar(name, self, 1)
97    }
98
99    #[inline(always)]
100    pub fn dtype(&self) -> &DataType {
101        &self.dtype
102    }
103
104    #[inline(always)]
105    pub fn update(&mut self, value: AnyValue<'static>) {
106        self.value = value;
107    }
108
109    #[inline(always)]
110    pub fn with_value(mut self, value: AnyValue<'static>) -> Self {
111        self.update(value);
112        self
113    }
114
115    #[inline(always)]
116    pub fn any_value_mut(&mut self) -> &mut AnyValue<'static> {
117        &mut self.value
118    }
119
120    pub fn to_physical(mut self) -> Scalar {
121        self.dtype = self.dtype.to_physical();
122        self.value = self.value.to_physical();
123        self
124    }
125}
126
127#[cfg(all(test, feature = "proptest", not(miri)))]
128mod tests {
129    use std::rc::Rc;
130
131    use proptest::prelude::*;
132
133    use crate::chunked_array::cast::CastOptions;
134    use crate::datatypes::proptest::{
135        AnyValueArbitraryOptions, AnyValueArbitrarySelection, DataTypeArbitraryOptions,
136        DataTypeArbitrarySelection, anyvalue_strategy, dtypes_strategy,
137    };
138    use crate::scalar::Scalar;
139
140    fn test_anyvalue_options() -> AnyValueArbitraryOptions {
141        AnyValueArbitraryOptions {
142            allowed_dtypes: AnyValueArbitrarySelection::all()
143                & !AnyValueArbitrarySelection::CATEGORICAL
144                & !AnyValueArbitrarySelection::CATEGORICAL_OWNED
145                & !AnyValueArbitrarySelection::ENUM
146                & !AnyValueArbitrarySelection::ENUM_OWNED
147                & !AnyValueArbitrarySelection::OBJECT
148                & !AnyValueArbitrarySelection::OBJECT_OWNED
149                & !AnyValueArbitrarySelection::LIST
150                & !AnyValueArbitrarySelection::ARRAY
151                & !AnyValueArbitrarySelection::STRUCT
152                & !AnyValueArbitrarySelection::STRUCT_OWNED
153                & !AnyValueArbitrarySelection::DATETIME
154                & !AnyValueArbitrarySelection::DATETIME_OWNED
155                & !AnyValueArbitrarySelection::DATE
156                & !AnyValueArbitrarySelection::TIME
157                & !AnyValueArbitrarySelection::DURATION
158                & !AnyValueArbitrarySelection::BINARY_OWNED,
159            categories_range: 1..=3,
160            ..Default::default()
161        }
162    }
163
164    fn test_dtype_options() -> DataTypeArbitraryOptions {
165        DataTypeArbitraryOptions {
166            allowed_dtypes: DataTypeArbitrarySelection::all()
167                & !DataTypeArbitrarySelection::CATEGORICAL
168                & !DataTypeArbitrarySelection::ENUM
169                & !DataTypeArbitrarySelection::OBJECT
170                & !DataTypeArbitrarySelection::LIST
171                & !DataTypeArbitrarySelection::ARRAY
172                & !DataTypeArbitrarySelection::STRUCT
173                & !DataTypeArbitrarySelection::DATETIME
174                & !DataTypeArbitrarySelection::DATE
175                & !DataTypeArbitrarySelection::TIME
176                & !DataTypeArbitrarySelection::DURATION
177                & !DataTypeArbitrarySelection::BINARY_OFFSET,
178            categories_range: 1..=3,
179            ..Default::default()
180        }
181    }
182
183    proptest! {
184        #![proptest_config(ProptestConfig::with_cases(100))]
185        #[test]
186        fn test_scalar_cast_with_options(
187            source_value in anyvalue_strategy(Rc::new(test_anyvalue_options()), 0),
188            target_dtype in dtypes_strategy(Rc::new(test_dtype_options()), 0),
189        ) {
190            let source_dtype = source_value.dtype();
191            let scalar = Scalar::new(source_dtype.clone(), source_value.clone());
192
193            let cast_result = scalar.cast_with_options(&target_dtype, CastOptions::default());
194
195            if let Ok(casted_scalar) = cast_result {
196                prop_assert_eq!(casted_scalar.dtype(), &target_dtype);
197            }
198        }
199    }
200
201    proptest! {
202        #![proptest_config(ProptestConfig::with_cases(100))]
203        #[test]
204        fn test_scalar_cast_identity(
205            source_value in anyvalue_strategy(Rc::new(test_anyvalue_options()), 0),
206        ) {
207            let source_dtype = source_value.dtype();
208            let scalar = Scalar::new(source_dtype.clone(), source_value);
209
210            let result = scalar.clone().cast_with_options(&source_dtype, CastOptions::default());
211
212            prop_assert!(result.is_ok());
213            if let Ok(casted) = result {
214                prop_assert_eq!(casted.dtype(), scalar.dtype());
215            }
216        }
217    }
218}