polars_core/scalar/
mod.rs1mod 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 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 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}