use std::sync::Arc;
use arrow_array::{ArrayRef, RecordBatch};
use arrow_schema::{DataType, Field, Schema};
use itertools::izip;
use serde_json::Value;
use super::tensor::*;
use crate::client::*;
#[derive(Default)]
pub struct DataFrame {
meta: ObjectMeta,
names: Vec<String>,
columns: Vec<Box<dyn Tensor>>,
}
impl_typename!(DataFrame, "vineyard::DataFrame");
impl Object for DataFrame {
fn construct(&mut self, meta: ObjectMeta) -> Result<()> {
vineyard_assert_typename(typename::<Self>(), meta.get_typename()?)?;
let size = meta.get_usize("__values_-size")?;
self.names = Vec::with_capacity(size);
self.columns = Vec::with_capacity(size);
for i in 0..size {
let name = meta.get_value(&format!("__values_-key-{}", i))?;
let name = match name {
Value::String(name) => name,
_ => name.to_string(),
};
self.names.push(name);
let column = meta.get_member_untyped(&format!("__values_-value-{}", i))?;
self.columns.push(downcast_to_tensor(column)?);
}
return Ok(());
}
}
register_vineyard_object!(DataFrame);
impl DataFrame {
pub fn new_boxed(meta: ObjectMeta) -> Result<Box<dyn Object>> {
let mut object = Box::<Self>::default();
object.construct(meta)?;
Ok(object)
}
pub fn num_columns(&self) -> usize {
self.columns.len()
}
pub fn names(&self) -> &[String] {
&self.names
}
pub fn name(&self, index: usize) -> &str {
&self.names[index]
}
pub fn columns(&self) -> &[Box<dyn Tensor>] {
&self.columns
}
pub fn column(&self, index: usize) -> ArrayRef {
self.columns[index].array()
}
pub fn recordbatch(&self) -> Result<RecordBatch> {
let mut columns = Vec::with_capacity(self.columns.len());
for column in &self.columns {
columns.push(column.array());
}
let types: Vec<DataType> = columns
.iter()
.map(|column| column.data_type().clone())
.collect();
let batch = RecordBatch::try_new(
Arc::new(Schema::new(
izip!(self.names.clone(), types)
.map(|(name, datatype)| Field::new(name, datatype, true))
.collect::<Vec<Field>>(),
)),
columns,
)?;
return Ok(batch);
}
}
pub struct DataFrameBuilder {
sealed: bool,
names: Vec<String>,
columns: Vec<Box<dyn Object>>,
}
impl ObjectBuilder for DataFrameBuilder {
fn sealed(&self) -> bool {
self.sealed
}
fn set_sealed(&mut self, sealed: bool) {
self.sealed = sealed;
}
}
impl ObjectBase for DataFrameBuilder {
fn build(&mut self, _client: &mut IPCClient) -> Result<()> {
if self.sealed {
return Ok(());
}
self.set_sealed(true);
return Ok(());
}
fn seal(mut self, client: &mut IPCClient) -> Result<Box<dyn Object>> {
self.build(client)?;
let mut meta = ObjectMeta::new_from_typename(typename::<DataFrame>());
meta.add_usize("__values_-size", self.names.len());
meta.add_isize("partition_index_row_", -1);
meta.add_isize("partition_index_column_", -1);
meta.add_isize("row_batch_index_", -1);
for (index, (name, column)) in self.names.iter().zip(self.columns).enumerate() {
meta.add_value(
&format!("__values_-key-{}", index),
Value::String(name.into()),
);
meta.add_member(&format!("__values_-value-{}", index), column)?;
}
let metadata = client.create_metadata(&meta)?;
return DataFrame::new_boxed(metadata);
}
}
impl DataFrameBuilder {
pub fn new(
client: &mut IPCClient,
names: Vec<String>,
arrays: Vec<arrow_array::ArrayRef>,
) -> Result<Self> {
let mut columns = Vec::with_capacity(arrays.len());
for array in arrays {
columns.push(build_tensor(client, array)?);
}
return Ok(DataFrameBuilder {
sealed: false,
names,
columns: columns,
});
}
pub fn new_from_columns(names: Vec<String>, columns: Vec<Box<dyn Object>>) -> Result<Self> {
return Ok(DataFrameBuilder {
sealed: false,
names,
columns,
});
}
}