use arrow2::{
array::{new_empty_array, Array, BooleanArray, ListArray, UInt64Array, Utf8Array},
bitmap::Bitmap,
compute::concatenate::concatenate,
offset::Offsets,
};
use nohash_hasher::IntMap;
use polars_core::{functions::diag_concat_df, prelude::*};
use re_log_types::ComponentName;
use crate::{
store::SecondaryIndex, ArrayExt, DataStore, DataStoreConfig, IndexBucket, IndexBucketIndices,
PersistentIndexTable, RowIndex,
};
impl DataStore {
pub fn to_dataframe(&self) -> DataFrame {
crate::profile_function!();
const IS_TIMELESS_COL: &str = "_is_timeless";
let timeless_dfs = self.timeless_indices.values().map(|index| {
let ent_path = index.ent_path.clone();
let mut df = index.to_dataframe(self, &self.config);
let num_rows = df.get_columns()[0].len();
let timeless = {
let timeless = BooleanArray::from(vec![Some(true); num_rows]).boxed();
new_infallible_series(IS_TIMELESS_COL, timeless.as_ref(), num_rows)
};
let df = df.with_column(timeless).unwrap();
(ent_path, df.clone())
});
let temporal_dfs = self.indices.values().map(|index| {
let dfs: Vec<_> = index
.buckets
.values()
.map(|bucket| (index.ent_path.clone(), bucket))
.map(|(ent_path, bucket)| {
let mut df = bucket.to_dataframe(self, &self.config);
let num_rows = df.get_columns()[0].len();
let entities = {
let ent_path = ent_path.to_string();
let ent_path = Some(ent_path.as_str());
let entities = Utf8Array::<i32>::from(vec![ent_path; num_rows]).boxed();
new_infallible_series("entity", entities.as_ref(), num_rows)
};
let df = df.with_column(entities).unwrap();
df.clone()
})
.collect();
let df = diag_concat_df(dfs.as_slice())
.unwrap();
(index.ent_path.clone(), df)
});
let dfs: Vec<_> = timeless_dfs
.chain(temporal_dfs)
.map(|(ent_path, mut df)| {
let num_rows = df.get_columns()[0].len();
let entities = {
let ent_path = ent_path.to_string();
let ent_path = Some(ent_path.as_str());
let entities = Utf8Array::<i32>::from(vec![ent_path; num_rows]).boxed();
new_infallible_series("entity", entities.as_ref(), num_rows)
};
df.with_column(entities).unwrap().clone() })
.collect();
let df = diag_concat_df(dfs.as_slice())
.unwrap();
let df = sort_df_columns(&df, self.config.store_insert_ids);
let has_insert_ids = df.column(DataStore::insert_id_key().as_str()).is_ok();
let has_timeless = df.column(IS_TIMELESS_COL).is_ok();
let (sort_cols, sort_orders): (Vec<_>, Vec<_>) = [
has_timeless.then_some((IS_TIMELESS_COL, true)),
has_insert_ids.then_some((DataStore::insert_id_key().as_str(), false)),
]
.into_iter()
.flatten()
.unzip();
let df = if !sort_cols.is_empty() {
df.sort(sort_cols, sort_orders).unwrap()
} else {
df
};
if has_timeless {
df.drop(IS_TIMELESS_COL).unwrap()
} else {
df
}
}
}
impl PersistentIndexTable {
pub fn to_dataframe(&self, store: &DataStore, config: &DataStoreConfig) -> DataFrame {
crate::profile_function!();
let Self {
ent_path: _,
cluster_key: _,
num_rows,
indices,
all_components: _,
} = self;
let insert_ids = config
.store_insert_ids
.then(|| insert_ids_as_series(*num_rows as usize, indices))
.flatten();
let comp_series =
std::iter::once(insert_ids)
.flatten() .chain(indices.iter().filter_map(|(component, comp_row_nrs)| {
let datatype = find_component_datatype(store, component)?;
component_as_series(store, *num_rows as usize, datatype, *component, comp_row_nrs).into()
}));
DataFrame::new(comp_series.collect::<Vec<_>>())
.unwrap()
}
}
impl IndexBucket {
pub fn to_dataframe(&self, store: &DataStore, config: &DataStoreConfig) -> DataFrame {
crate::profile_function!();
let (_, times) = self.times();
let num_rows = times.len();
let IndexBucketIndices {
is_sorted: _,
time_range: _,
times: _,
indices,
} = &*self.indices.read();
let insert_ids = config
.store_insert_ids
.then(|| insert_ids_as_series(num_rows, indices))
.flatten();
let comp_series = [
insert_ids,
Some(new_infallible_series(
self.timeline.name().as_str(),
×,
num_rows,
)),
]
.into_iter()
.flatten() .chain(indices.iter().filter_map(|(component, comp_row_nrs)| {
let datatype = find_component_datatype(store, component)?;
component_as_series(store, num_rows, datatype, *component, comp_row_nrs).into()
}));
DataFrame::new(comp_series.collect::<Vec<_>>())
.unwrap()
}
}
fn insert_ids_as_series(
num_rows: usize,
indices: &IntMap<ComponentName, SecondaryIndex>,
) -> Option<Series> {
crate::profile_function!();
indices.get(&DataStore::insert_id_key()).map(|insert_ids| {
let insert_ids = insert_ids
.iter()
.map(|id| id.map(|id| id.0.get()))
.collect::<Vec<_>>();
let insert_ids = UInt64Array::from(insert_ids);
new_infallible_series(DataStore::insert_id_key().as_str(), &insert_ids, num_rows)
})
}
fn find_component_datatype(
store: &DataStore,
component: &ComponentName,
) -> Option<arrow2::datatypes::DataType> {
crate::profile_function!();
let timeless = store
.timeless_components
.get(component)
.map(|table| table.datatype.clone());
let temporal = store
.components
.get(component)
.map(|table| table.datatype.clone());
timeless.or(temporal)
}
fn component_as_series(
store: &DataStore,
num_rows: usize,
datatype: arrow2::datatypes::DataType,
component: ComponentName,
comp_row_nrs: &[Option<RowIndex>],
) -> Series {
crate::profile_function!();
let components = &[component];
let comp_rows: Vec<Option<_>> = comp_row_nrs
.iter()
.cloned()
.map(|comp_row_nr| store.get(components, &[comp_row_nr])[0].clone())
.collect();
let comp_validity: Vec<_> = comp_rows.iter().map(|row| row.is_some()).collect();
let comp_lengths = comp_rows
.iter()
.map(|row| row.as_ref().map_or(0, |row| row.len()));
let comp_values: Vec<_> = comp_rows.iter().flatten().map(|row| row.as_ref()).collect();
let comp_values = ListArray::<i32>::new(
ListArray::<i32>::default_datatype(datatype.clone()),
Offsets::try_from_lengths(comp_lengths).unwrap().into(),
if comp_values.is_empty() {
new_empty_array(datatype)
} else {
concatenate(comp_values.as_slice()).unwrap().to_boxed()
},
Some(Bitmap::from(comp_validity)),
);
new_infallible_series(component.as_str(), &comp_values, num_rows)
}
fn new_infallible_series(name: &str, data: &dyn Array, len: usize) -> Series {
crate::profile_function!();
Series::try_from((name, data.as_ref().clean_for_polars())).unwrap_or_else(|_| {
let errs = Utf8Array::<i32>::from(vec![Some("<ERR>"); len]);
Series::try_from((name, errs.boxed())).unwrap() })
}
fn sort_df_columns(df: &DataFrame, store_insert_ids: bool) -> DataFrame {
crate::profile_function!();
let columns: Vec<_> = {
let mut all = df.get_column_names();
all.sort();
all.remove(all.binary_search(&"entity").expect("has to exist"));
if store_insert_ids {
all.remove(
all.binary_search(&DataStore::insert_id_key().as_str())
.expect("has to exist"),
);
}
let timelines = all
.iter()
.copied()
.filter(|name| !name.starts_with("rerun."))
.map(Some)
.collect::<Vec<_>>();
let components = all
.iter()
.copied()
.filter(|name| name.starts_with("rerun."))
.map(Some)
.collect::<Vec<_>>();
[
vec![store_insert_ids.then(|| DataStore::insert_id_key().as_str())],
timelines,
vec![Some("entity")],
components,
]
.into_iter()
.flatten() .flatten() .collect()
};
df.select(columns).unwrap()
}