use crate::{MattenError, Tensor};
#[cfg(feature = "dynamic")]
impl Tensor {
pub fn from_elements(data: Vec<crate::dynamic::Element>, shape: &[usize]) -> Tensor {
Self::try_from_elements(data, shape).unwrap_or_else(|e| panic!("{e}"))
}
pub fn try_from_elements(
data: Vec<crate::dynamic::Element>,
shape: &[usize],
) -> Result<Tensor, MattenError> {
let expected = crate::shape::validate_shape(shape, "try_from_elements")?;
if data.len() != expected {
return Err(MattenError::Shape {
operation: "try_from_elements",
message: format!(
"data length {} does not match shape {shape:?}, which requires {expected} elements",
data.len()
),
});
}
let dyn_tensor = crate::dynamic::storage::DynamicTensor::from_vec(data, shape.to_vec());
Ok(Tensor {
data: Vec::new(),
shape: shape.to_vec(),
dynamic: Some(Box::new(dyn_tensor)),
})
}
pub fn get_element(&self, coord: &[usize]) -> Option<crate::dynamic::Element> {
let flat = crate::shape::coord_to_flat(coord, &self.shape)?;
self.dynamic.as_ref()?.get_flat(flat).cloned()
}
pub fn is_dynamic(&self) -> bool {
self.dynamic.is_some()
}
#[cfg(feature = "json")]
pub fn from_json_dynamic(input: &str) -> Result<Tensor, MattenError> {
crate::dynamic::parse::json::from_json_dynamic(input)
}
#[cfg(feature = "csv")]
pub fn from_csv_dynamic(input: &str) -> Result<Tensor, MattenError> {
crate::dynamic::parse::csv::from_csv_dynamic(input)
}
pub fn to_elements(&self) -> Vec<crate::dynamic::Element> {
self.dynamic
.as_ref()
.expect("to_elements called on a non-dynamic tensor")
.to_vec()
}
pub fn fill_none(&self, value: impl Into<crate::dynamic::Element>) -> Tensor {
let fill = value.into();
let dyn_t = self
.dynamic
.as_ref()
.expect("fill_none called on a non-dynamic tensor");
let new_data: Vec<crate::dynamic::Element> = dyn_t
.to_vec()
.into_iter()
.map(|e| {
if e == crate::dynamic::Element::None {
fill.clone()
} else {
e
}
})
.collect();
let new_dyn =
crate::dynamic::storage::DynamicTensor::from_vec(new_data, dyn_t.shape.clone());
Tensor {
data: Vec::new(),
shape: dyn_t.shape.clone(),
#[cfg(feature = "dynamic")]
dynamic: Some(Box::new(new_dyn)),
}
}
pub fn try_numeric(&self) -> Result<Tensor, MattenError> {
let dyn_t = self
.dynamic
.as_ref()
.expect("try_numeric called on a non-dynamic tensor");
let mut floats = Vec::with_capacity(dyn_t.len);
for i in 0..dyn_t.len {
let elem = dyn_t.get_flat(i).unwrap_or(&crate::dynamic::Element::None);
match elem.try_as_f64() {
Some(v) => floats.push(v),
None => {
return Err(MattenError::Unsupported {
operation: "try_numeric",
message: format!(
"element at position {i} is {elem:?} and cannot be coerced to f64; \
use fill_none or explicit conversion first"
),
});
}
}
}
Tensor::try_new(floats, &dyn_t.shape)
}
}
#[cfg(feature = "dynamic")]
impl Tensor {
#[cfg(feature = "dynamic")]
pub fn none_mask(&self) -> Tensor {
let dyn_t = self
.dynamic
.as_ref()
.expect("none_mask called on a non-dynamic tensor");
let data: Vec<f64> = dyn_t
.to_vec()
.iter()
.map(|e| if e.is_none() { 1.0 } else { 0.0 })
.collect();
Tensor::new(data, &dyn_t.shape)
}
#[cfg(feature = "dynamic")]
pub fn is_none_mask(&self) -> Tensor {
self.none_mask()
}
#[cfg(feature = "dynamic")]
pub fn count_none(&self) -> usize {
let dyn_t = self
.dynamic
.as_ref()
.expect("count_none called on a non-dynamic tensor");
dyn_t.to_vec().iter().filter(|e| e.is_none()).count()
}
#[cfg(feature = "dynamic")]
pub fn forward_fill_none(&self, fallback: impl Into<crate::dynamic::Element>) -> Tensor {
use crate::dynamic::Element;
let fallback = fallback.into();
let dyn_t = self
.dynamic
.as_ref()
.expect("forward_fill_none called on a non-dynamic tensor");
let mut last: Element = fallback;
let new_data: Vec<Element> = dyn_t
.to_vec()
.into_iter()
.map(|e| {
if e == Element::None {
last.clone()
} else {
last = e.clone();
e
}
})
.collect();
let shape = dyn_t.shape.clone();
let new_dyn = crate::dynamic::storage::DynamicTensor::from_vec(new_data, shape.clone());
Tensor {
data: Vec::new(),
shape,
dynamic: Some(Box::new(new_dyn)),
}
}
#[cfg(feature = "dynamic")]
pub fn sum_skip_none(&self) -> f64 {
let dyn_t = self
.dynamic
.as_ref()
.expect("sum_skip_none called on a non-dynamic tensor");
let mut acc = 0.0f64;
for e in dyn_t.to_vec() {
if e.is_none() {
continue;
}
acc += e.try_as_f64().unwrap_or_else(|| {
panic!("sum_skip_none: non-numeric element {e:?}; use fill_none first")
});
}
acc
}
}