use itertools::izip;
use polars::{
frame::DataFrame,
prelude::{self as pl, DataType},
series::Series,
};
use crate::{
io::polars::{PolarsError, f64_slice, u64_slice},
photometry::Filter,
};
pub(crate) type BaseRow<'a> = (
&'a u64,
&'a f64,
&'a f64,
&'a f64,
&'a f64,
&'a f64,
&'a f64,
&'a f64,
Filter,
);
pub(crate) fn base_fields() -> impl Iterator<Item = (pl::PlSmallStr, pl::DataType)> {
[
("id".into(), pl::DataType::UInt64),
("ra".into(), pl::DataType::Float64),
("ra_err".into(), pl::DataType::Float64),
("dec".into(), pl::DataType::Float64),
("dec_err".into(), pl::DataType::Float64),
("magnitude".into(), pl::DataType::Float64),
("mag_err".into(), pl::DataType::Float64),
("filter".into(), pl::DataType::String),
("mjd_tt".into(), pl::DataType::Float64),
]
.into_iter()
}
enum FilterData {
Str(Series),
Int(Series),
}
pub(crate) struct BaseFields<'a> {
pub(crate) ids: &'a [u64],
pub(crate) ra: &'a [f64],
pub(crate) ra_err: &'a [f64],
pub(crate) dec: &'a [f64],
pub(crate) dec_err: &'a [f64],
pub(crate) magnitude: &'a [f64],
pub(crate) mag_err: &'a [f64],
pub(crate) mjd_tt: &'a [f64],
filter: FilterData,
}
impl<'a> BaseFields<'a> {
pub(crate) fn materialize_fields(df: &'a DataFrame) -> Result<Self, PolarsError> {
let mut ids_slot: Option<&'a [u64]> = None;
let mut f64_slices: Vec<&'a [f64]> = Vec::new();
let mut filter_slot: Option<FilterData> = None;
for (name, dtype) in base_fields() {
let col = df.column(&name)?;
if name == "filter" {
let series = col.as_materialized_series().clone();
let filter_data = match col.dtype() {
DataType::String => FilterData::Str(series),
DataType::UInt32 => FilterData::Int(series),
DataType::UInt8 | DataType::UInt16 => {
FilterData::Int(series.cast(&DataType::UInt32)?)
}
other => {
return Err(PolarsError::FilterColumnTypeError(other.to_string()));
}
};
filter_slot = Some(filter_data);
} else {
match dtype {
DataType::UInt64 => ids_slot = Some(u64_slice(col)?),
DataType::Float64 => f64_slices.push(f64_slice(col)?),
_ => {}
}
}
}
let ids = ids_slot.ok_or_else(|| PolarsError::MissingColumnError("id".into()))?;
let filter = filter_slot
.ok_or_else(|| PolarsError::FilterColumnTypeError("missing filter column".into()))?;
let [ra, ra_err, dec, dec_err, magnitude, mag_err, mjd_tt]: [&'a [f64]; 7] = f64_slices
.try_into()
.map_err(|_| PolarsError::Float64ColumnCountError)?;
Ok(BaseFields {
ids,
ra,
ra_err,
dec,
dec_err,
magnitude,
mag_err,
mjd_tt,
filter,
})
}
pub(crate) fn iter_base_fields(
&self,
) -> Result<impl Iterator<Item = BaseRow<'_>> + '_, PolarsError> {
let numeric = izip!(
self.ids.iter(),
self.ra.iter(),
self.ra_err.iter(),
self.dec.iter(),
self.dec_err.iter(),
self.magnitude.iter(),
self.mag_err.iter(),
self.mjd_tt.iter(),
);
match &self.filter {
FilterData::Str(series) => {
let ca = series.str()?;
let filter_iter = ca
.iter()
.map(|opt| Filter::String(opt.unwrap_or_default().to_owned()));
Ok(itertools::Either::Left(izip!(numeric, filter_iter).map(
|((id, ra, ra_err, dec, dec_err, mag, mag_err, mjd), f)| {
(id, ra, ra_err, dec, dec_err, mag, mag_err, mjd, f)
},
)))
}
FilterData::Int(series) => {
let ca = series.u32()?;
let filter_iter = ca.iter().map(|opt| Filter::Int(opt.unwrap_or(0)));
Ok(itertools::Either::Right(izip!(numeric, filter_iter).map(
|((id, ra, ra_err, dec, dec_err, mag, mag_err, mjd), f)| {
(id, ra, ra_err, dec, dec_err, mag, mag_err, mjd, f)
},
)))
}
}
}
}