mod multi_index;
pub use multi_index::{MultiIndex, StringMultiIndex};
use crate::error::{PandRSError, Result};
use crate::temporal::Temporal;
use chrono::{NaiveDate, NaiveDateTime};
use std::collections::HashMap;
use std::fmt::{Debug, Display};
use std::hash::Hash;
use std::ops::Range;
#[derive(Debug, Clone)]
pub struct Index<T>
where
T: Debug + Clone + Eq + Hash + Display,
{
values: Vec<T>,
map: HashMap<T, usize>,
name: Option<String>,
}
impl Default for Index<String> {
fn default() -> Self {
let values: Vec<String> = Vec::new();
let map: HashMap<String, usize> = HashMap::new();
Self {
values,
map,
name: None,
}
}
}
impl<T> Index<T>
where
T: Debug + Clone + Eq + Hash + Display,
{
pub fn new(values: Vec<T>) -> Result<Self> {
Self::with_name(values, None)
}
pub fn with_name(values: Vec<T>, name: Option<String>) -> Result<Self> {
let mut map = HashMap::with_capacity(values.len());
for (i, value) in values.iter().enumerate() {
if map.insert(value.clone(), i).is_some() {
return Err(PandRSError::Index(format!(
"Index value '{}' is duplicated",
value
)));
}
}
Ok(Index { values, map, name })
}
pub fn from_range(range: Range<usize>) -> Result<Index<usize>> {
let values: Vec<usize> = range.collect();
Index::<usize>::new(values)
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn get_loc(&self, key: &T) -> Option<usize> {
self.map.get(key).copied()
}
pub fn get_value(&self, pos: usize) -> Option<&T> {
self.values.get(pos)
}
pub fn values(&self) -> &[T] {
&self.values
}
pub fn name(&self) -> Option<&String> {
self.name.as_ref()
}
pub fn set_name(&mut self, name: Option<String>) {
self.name = name;
}
pub fn rename(&self, name: Option<String>) -> Self {
let mut new_index = self.clone();
new_index.name = name;
new_index
}
}
pub trait IndexTrait {
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<T> IndexTrait for Index<T>
where
T: Debug + Clone + Eq + Hash + Display,
{
fn len(&self) -> usize {
self.len()
}
}
impl<T> IndexTrait for MultiIndex<T>
where
T: Debug + Clone + Eq + Hash + Display,
{
fn len(&self) -> usize {
self.len()
}
}
#[derive(Debug, Clone)]
pub enum DataFrameIndex<T>
where
T: Debug + Clone + Eq + Hash + Display,
{
Simple(Index<T>),
Multi(MultiIndex<T>),
}
impl<T> IndexTrait for DataFrameIndex<T>
where
T: Debug + Clone + Eq + Hash + Display,
{
fn len(&self) -> usize {
match self {
DataFrameIndex::Simple(idx) => idx.len(),
DataFrameIndex::Multi(idx) => idx.len(),
}
}
}
impl<T> DataFrameIndex<T>
where
T: Debug + Clone + Eq + Hash + Display,
{
pub fn from_simple(index: Index<T>) -> Self {
DataFrameIndex::Simple(index)
}
pub fn from_multi(index: MultiIndex<T>) -> Self {
DataFrameIndex::Multi(index)
}
pub fn default_with_len(len: usize) -> Result<DataFrameIndex<String>> {
let range_idx = Index::<usize>::from_range(0..len)?;
let string_values: Vec<String> = range_idx.values().iter().map(|i| i.to_string()).collect();
let string_idx = Index::<String>::new(string_values)?;
Ok(DataFrameIndex::Simple(string_idx))
}
pub fn is_multi(&self) -> bool {
matches!(self, DataFrameIndex::Multi(_))
}
}
pub type RangeIndex = Index<usize>;
pub type StringIndex = Index<String>;
impl StringIndex {
pub fn to_datetime_vec(&self) -> Result<Vec<NaiveDate>> {
let mut result = Vec::with_capacity(self.len());
for value in &self.values {
match NaiveDate::parse_from_str(value, "%Y-%m-%d") {
Ok(date) => result.push(date),
Err(_) => {
result.push(
NaiveDate::parse_from_str("2023-01-01", "%Y-%m-%d")
.unwrap_or_else(|_| NaiveDate::now()),
);
}
}
}
Ok(result)
}
}
impl DataFrameIndex<String> {
pub fn to_datetime_vec(&self) -> Result<Vec<NaiveDate>> {
match self {
DataFrameIndex::Simple(idx) => idx.to_datetime_vec(),
DataFrameIndex::Multi(_) => {
Err(PandRSError::NotImplemented(
"Date/time conversion for multi-index is not currently supported".to_string(),
))
}
}
}
pub fn string_values(&self) -> Option<Vec<String>> {
match self {
DataFrameIndex::Simple(idx) => Some(idx.values().iter().map(|v| v.clone()).collect()),
DataFrameIndex::Multi(multi_idx) => {
Some(
multi_idx
.tuples()
.iter()
.map(|tuple| tuple.join(", "))
.collect(),
)
}
}
}
}