polars_core/frame/column/
scalar.rs1use std::sync::OnceLock;
2
3use polars_error::PolarsResult;
4use polars_utils::broadcast::BroadcastLength;
5use polars_utils::pl_str::PlSmallStr;
6
7use super::{AnyValue, Column, DataType, IntoColumn, Scalar, Series};
8use crate::chunked_array::cast::CastOptions;
9
10#[derive(Debug, Clone)]
14pub struct ScalarColumn {
15 name: PlSmallStr,
16 scalar: Scalar,
18 length: usize,
19
20 materialized: OnceLock<Series>,
27}
28
29impl ScalarColumn {
30 #[inline]
31 pub fn new(name: PlSmallStr, scalar: Scalar, length: usize) -> Self {
32 Self {
33 name,
34 scalar,
35 length,
36
37 materialized: OnceLock::new(),
38 }
39 }
40
41 #[inline]
42 pub fn new_empty(name: PlSmallStr, dtype: DataType) -> Self {
43 Self {
44 name,
45 scalar: Scalar::new(dtype, AnyValue::Null),
46 length: 0,
47
48 materialized: OnceLock::new(),
49 }
50 }
51
52 pub fn full_null(name: PlSmallStr, length: usize, dtype: DataType) -> Self {
53 Self::new(name, Scalar::null(dtype), length)
54 }
55
56 pub fn name(&self) -> &PlSmallStr {
57 &self.name
58 }
59
60 pub fn scalar(&self) -> &Scalar {
61 &self.scalar
62 }
63
64 pub fn dtype(&self) -> &DataType {
65 self.scalar.dtype()
66 }
67
68 #[inline]
69 pub fn len(&self) -> usize {
70 self.length
71 }
72
73 pub fn is_empty(&self) -> bool {
74 self.length == 0
75 }
76
77 pub fn is_full_null(&self) -> bool {
78 self.scalar.is_null()
79 }
80
81 fn _to_series(name: PlSmallStr, value: Scalar, length: usize) -> Series {
82 let series = if length == 0 {
83 Series::new_empty(name, value.dtype())
84 } else {
85 value.into_series(name).new_from_index(0, length)
86 };
87
88 debug_assert_eq!(series.len(), length);
89
90 series
91 }
92
93 pub fn to_series(&self) -> Series {
95 Self::_to_series(self.name.clone(), self.scalar.clone(), self.length)
96 }
97
98 pub fn lazy_as_materialized_series(&self) -> Option<&Series> {
100 self.materialized.get()
101 }
102
103 pub fn as_materialized_series(&self) -> &Series {
107 self.materialized.get_or_init(|| self.to_series())
108 }
109
110 pub fn take_materialized_series(self) -> Series {
112 self.materialized
113 .into_inner()
114 .unwrap_or_else(|| Self::_to_series(self.name, self.scalar, self.length))
115 }
116
117 pub fn as_single_value_series(&self) -> Series {
121 self.as_n_values_series(1)
122 }
123
124 pub fn as_n_values_series(&self, n: usize) -> Series {
128 let length = usize::min(n, self.length);
129
130 match self.materialized.get() {
131 Some(s) if length == self.length || length > 1 => s.head(Some(length)),
134 _ => Self::_to_series(self.name.clone(), self.scalar.clone(), length),
135 }
136 }
137
138 #[inline]
142 pub fn unit_scalar_from_series(series: Series) -> Self {
143 assert_eq!(series.len(), 1);
144 let value = unsafe { series.get_unchecked(0) };
146 let value = value.into_static();
147 let value = Scalar::new(series.dtype().clone(), value);
148 let mut sc = ScalarColumn::new(series.name().clone(), value, 1);
149 sc.materialized = OnceLock::from(series);
150 sc
151 }
152
153 pub fn from_single_value_series(series: Series, length: usize) -> Self {
159 debug_assert!(series.len() <= 1);
160
161 let value = if series.is_empty() {
162 AnyValue::Null
163 } else {
164 unsafe { series.get_unchecked(0) }.into_static()
165 };
166 let value = Scalar::new(series.dtype().clone(), value);
167 ScalarColumn::new(series.name().clone(), value, length)
168 }
169
170 pub fn resize(&self, length: usize) -> ScalarColumn {
174 if self.length == length {
175 return self.clone();
176 }
177
178 debug_assert!(length == 0 || self.length > 0);
181
182 let mut resized = Self {
183 name: self.name.clone(),
184 scalar: self.scalar.clone(),
185 length,
186 materialized: OnceLock::new(),
187 };
188
189 if length == self.length || (length < self.length && length > 1) {
190 if let Some(materialized) = self.materialized.get() {
191 resized.materialized = OnceLock::from(materialized.head(Some(length)));
192 debug_assert_eq!(resized.materialized.get().unwrap().len(), length);
193 }
194 }
195
196 resized
197 }
198
199 pub fn cast_with_options(&self, dtype: &DataType, options: CastOptions) -> PolarsResult<Self> {
200 match self.materialized.get() {
205 Some(s) => {
206 let materialized = s.cast_with_options(dtype, options)?;
207 assert_eq!(self.length, materialized.len());
208
209 let mut casted = if materialized.is_empty() {
210 Self::new_empty(materialized.name().clone(), materialized.dtype().clone())
211 } else {
212 let scalar = unsafe { materialized.get_unchecked(0) }.into_static();
214 Self::new(
215 materialized.name().clone(),
216 Scalar::new(materialized.dtype().clone(), scalar),
217 self.length,
218 )
219 };
220 casted.materialized = OnceLock::from(materialized);
221 Ok(casted)
222 },
223 None => {
224 let s = self
225 .as_single_value_series()
226 .cast_with_options(dtype, options)?;
227
228 if self.length == 0 {
229 Ok(Self::new_empty(s.name().clone(), s.dtype().clone()))
230 } else {
231 assert_eq!(1, s.len());
232 Ok(Self::from_single_value_series(s, self.length))
233 }
234 },
235 }
236 }
237
238 pub fn strict_cast(&self, dtype: &DataType) -> PolarsResult<Self> {
239 self.cast_with_options(dtype, CastOptions::Strict)
240 }
241 pub fn cast(&self, dtype: &DataType) -> PolarsResult<Self> {
242 self.cast_with_options(dtype, CastOptions::NonStrict)
243 }
244 pub unsafe fn cast_unchecked(&self, dtype: &DataType) -> PolarsResult<Self> {
248 match self.materialized.get() {
253 Some(s) => {
254 let materialized = s.cast_unchecked(dtype)?;
255 assert_eq!(self.length, materialized.len());
256
257 let mut casted = if materialized.is_empty() {
258 Self::new_empty(materialized.name().clone(), materialized.dtype().clone())
259 } else {
260 let scalar = unsafe { materialized.get_unchecked(0) }.into_static();
262 Self::new(
263 materialized.name().clone(),
264 Scalar::new(materialized.dtype().clone(), scalar),
265 self.length,
266 )
267 };
268 casted.materialized = OnceLock::from(materialized);
269 Ok(casted)
270 },
271 None => {
272 let s = self.as_single_value_series().cast_unchecked(dtype)?;
273 assert_eq!(1, s.len());
274
275 if self.length == 0 {
276 Ok(Self::new_empty(s.name().clone(), s.dtype().clone()))
277 } else {
278 Ok(Self::from_single_value_series(s, self.length))
279 }
280 },
281 }
282 }
283
284 pub fn rename(&mut self, name: PlSmallStr) -> &mut Self {
285 if let Some(series) = self.materialized.get_mut() {
286 series.rename(name.clone());
287 }
288
289 self.name = name;
290 self
291 }
292
293 pub fn has_nulls(&self) -> bool {
294 self.length != 0 && self.scalar.is_null()
295 }
296
297 pub fn drop_nulls(&self) -> Self {
298 if self.scalar.is_null() {
299 self.resize(0)
300 } else {
301 self.clone()
302 }
303 }
304
305 pub fn into_nulls(mut self) -> Self {
306 self.scalar.update(AnyValue::Null);
307 self
308 }
309
310 pub fn to_unit_list(&self) -> Self {
312 let mut slf = self.clone();
313 slf.map_scalar(|s| Scalar::new_list(s.into_series(PlSmallStr::EMPTY)));
314 slf
315 }
316
317 pub fn map_scalar(&mut self, map_scalar: impl Fn(Scalar) -> Scalar) {
318 self.scalar = map_scalar(std::mem::take(&mut self.scalar));
319 self.materialized.take();
320 }
321 pub fn with_value(&mut self, value: AnyValue<'static>) -> &mut Self {
322 self.scalar.update(value);
323 self.materialized.take();
324 self
325 }
326}
327
328impl IntoColumn for ScalarColumn {
329 #[inline(always)]
330 fn into_column(self) -> Column {
331 self.into()
332 }
333}
334
335impl From<ScalarColumn> for Column {
336 #[inline]
337 fn from(value: ScalarColumn) -> Self {
338 Self::Scalar(value)
339 }
340}
341
342impl BroadcastLength for ScalarColumn {
343 fn _broadcast_len(&self) -> usize {
344 self.len()
345 }
346
347 fn _column_name(&self) -> Option<&str> {
348 Some(self.name())
349 }
350}
351
352#[cfg(feature = "dsl-schema")]
353impl schemars::JsonSchema for ScalarColumn {
354 fn schema_name() -> std::borrow::Cow<'static, str> {
355 "ScalarColumn".into()
356 }
357
358 fn schema_id() -> std::borrow::Cow<'static, str> {
359 std::borrow::Cow::Borrowed(concat!(module_path!(), "::", "ScalarColumn"))
360 }
361
362 fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
363 serde_impl::SerializeWrap::json_schema(generator)
364 }
365}
366
367#[cfg(feature = "serde")]
368mod serde_impl {
369 use std::sync::OnceLock;
370
371 use polars_error::PolarsError;
372 use polars_utils::pl_str::PlSmallStr;
373
374 use super::ScalarColumn;
375 use crate::frame::{Scalar, Series};
376
377 #[derive(serde::Serialize, serde::Deserialize)]
378 #[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]
379 pub struct SerializeWrap {
380 name: PlSmallStr,
381 unit_series: Series,
383 length: usize,
384 }
385
386 impl From<&ScalarColumn> for SerializeWrap {
387 fn from(value: &ScalarColumn) -> Self {
388 Self {
389 name: value.name.clone(),
390 unit_series: value.scalar.clone().into_series(PlSmallStr::EMPTY),
391 length: value.length,
392 }
393 }
394 }
395
396 impl TryFrom<SerializeWrap> for ScalarColumn {
397 type Error = PolarsError;
398
399 fn try_from(value: SerializeWrap) -> Result<Self, Self::Error> {
400 let slf = Self {
401 name: value.name,
402 scalar: Scalar::new(
403 value.unit_series.dtype().clone(),
404 value.unit_series.get(0)?.into_static(),
405 ),
406 length: value.length,
407 materialized: OnceLock::new(),
408 };
409
410 Ok(slf)
411 }
412 }
413
414 impl serde::ser::Serialize for ScalarColumn {
415 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
416 where
417 S: serde::Serializer,
418 {
419 SerializeWrap::from(self).serialize(serializer)
420 }
421 }
422
423 impl<'de> serde::de::Deserialize<'de> for ScalarColumn {
424 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
425 where
426 D: serde::Deserializer<'de>,
427 {
428 use serde::de::Error;
429
430 SerializeWrap::deserialize(deserializer)
431 .and_then(|x| ScalarColumn::try_from(x).map_err(D::Error::custom))
432 }
433 }
434}