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