polars_core/frame/column/
scalar.rsuse std::sync::OnceLock;
use polars_error::PolarsResult;
use polars_utils::pl_str::PlSmallStr;
use super::{AnyValue, Column, DataType, IntoColumn, Scalar, Series};
use crate::chunked_array::cast::CastOptions;
#[derive(Debug, Clone)]
pub struct ScalarColumn {
name: PlSmallStr,
scalar: Scalar,
length: usize,
materialized: OnceLock<Series>,
}
impl ScalarColumn {
#[inline]
pub fn new(name: PlSmallStr, scalar: Scalar, length: usize) -> Self {
Self {
name,
scalar,
length,
materialized: OnceLock::new(),
}
}
#[inline]
pub fn new_empty(name: PlSmallStr, dtype: DataType) -> Self {
Self {
name,
scalar: Scalar::new(dtype, AnyValue::Null),
length: 0,
materialized: OnceLock::new(),
}
}
pub fn name(&self) -> &PlSmallStr {
&self.name
}
pub fn scalar(&self) -> &Scalar {
&self.scalar
}
pub fn dtype(&self) -> &DataType {
self.scalar.dtype()
}
pub fn len(&self) -> usize {
self.length
}
pub fn is_empty(&self) -> bool {
self.length == 0
}
fn _to_series(name: PlSmallStr, value: Scalar, length: usize) -> Series {
let series = if length == 0 {
Series::new_empty(name, value.dtype())
} else {
value.into_series(name).new_from_index(0, length)
};
debug_assert_eq!(series.len(), length);
series
}
pub fn to_series(&self) -> Series {
Self::_to_series(self.name.clone(), self.scalar.clone(), self.length)
}
pub fn lazy_as_materialized_series(&self) -> Option<&Series> {
self.materialized.get()
}
pub fn as_materialized_series(&self) -> &Series {
self.materialized.get_or_init(|| self.to_series())
}
pub fn take_materialized_series(self) -> Series {
self.materialized
.into_inner()
.unwrap_or_else(|| Self::_to_series(self.name, self.scalar, self.length))
}
pub fn as_single_value_series(&self) -> Series {
match self.materialized.get() {
Some(s) => s.head(Some(1)),
None => Self::_to_series(
self.name.clone(),
self.scalar.clone(),
usize::min(1, self.length),
),
}
}
#[inline]
pub fn unit_scalar_from_series(series: Series) -> Self {
assert_eq!(series.len(), 1);
let value = unsafe { series.get_unchecked(0) };
let value = value.into_static();
let value = Scalar::new(series.dtype().clone(), value);
let mut sc = ScalarColumn::new(series.name().clone(), value, 1);
sc.materialized = OnceLock::from(series);
sc
}
pub fn from_single_value_series(series: Series, length: usize) -> Self {
debug_assert_eq!(series.len(), 1);
let value = series.get(0).unwrap();
let value = value.into_static();
let value = Scalar::new(series.dtype().clone(), value);
ScalarColumn::new(series.name().clone(), value, length)
}
pub fn resize(&self, length: usize) -> ScalarColumn {
if self.length == length {
return self.clone();
}
debug_assert!(length == 0 || self.length > 0);
let mut resized = Self {
name: self.name.clone(),
scalar: self.scalar.clone(),
length,
materialized: OnceLock::new(),
};
if self.length >= length {
if let Some(materialized) = self.materialized.get() {
resized.materialized = OnceLock::from(materialized.head(Some(length)));
debug_assert_eq!(resized.materialized.get().unwrap().len(), length);
}
}
resized
}
pub fn cast_with_options(&self, dtype: &DataType, options: CastOptions) -> PolarsResult<Self> {
match self.materialized.get() {
Some(s) => {
let materialized = s.cast_with_options(dtype, options)?;
assert_eq!(self.length, materialized.len());
let mut casted = if materialized.len() == 0 {
Self::new_empty(materialized.name().clone(), materialized.dtype().clone())
} else {
let scalar = unsafe { materialized.get_unchecked(0) }.into_static();
Self::new(
materialized.name().clone(),
Scalar::new(materialized.dtype().clone(), scalar),
self.length,
)
};
casted.materialized = OnceLock::from(materialized);
Ok(casted)
},
None => {
let s = self
.as_single_value_series()
.cast_with_options(dtype, options)?;
if self.length == 0 {
Ok(Self::new_empty(s.name().clone(), s.dtype().clone()))
} else {
assert_eq!(1, s.len());
Ok(Self::from_single_value_series(s, self.length))
}
},
}
}
pub fn strict_cast(&self, dtype: &DataType) -> PolarsResult<Self> {
self.cast_with_options(dtype, CastOptions::Strict)
}
pub fn cast(&self, dtype: &DataType) -> PolarsResult<Self> {
self.cast_with_options(dtype, CastOptions::NonStrict)
}
pub unsafe fn cast_unchecked(&self, dtype: &DataType) -> PolarsResult<Self> {
match self.materialized.get() {
Some(s) => {
let materialized = s.cast_unchecked(dtype)?;
assert_eq!(self.length, materialized.len());
let mut casted = if materialized.len() == 0 {
Self::new_empty(materialized.name().clone(), materialized.dtype().clone())
} else {
let scalar = unsafe { materialized.get_unchecked(0) }.into_static();
Self::new(
materialized.name().clone(),
Scalar::new(materialized.dtype().clone(), scalar),
self.length,
)
};
casted.materialized = OnceLock::from(materialized);
Ok(casted)
},
None => {
let s = self.as_single_value_series().cast_unchecked(dtype)?;
assert_eq!(1, s.len());
if self.length == 0 {
Ok(Self::new_empty(s.name().clone(), s.dtype().clone()))
} else {
Ok(Self::from_single_value_series(s, self.length))
}
},
}
}
pub fn rename(&mut self, name: PlSmallStr) -> &mut Self {
if let Some(series) = self.materialized.get_mut() {
series.rename(name.clone());
}
self.name = name;
self
}
pub fn has_nulls(&self) -> bool {
self.length != 0 && self.scalar.is_null()
}
}
impl IntoColumn for ScalarColumn {
#[inline(always)]
fn into_column(self) -> Column {
self.into()
}
}
impl From<ScalarColumn> for Column {
#[inline]
fn from(value: ScalarColumn) -> Self {
Self::Scalar(value)
}
}