Source code for pyphi.serialize.frames

"""Exact serialization of pandas DataFrames as embedded parquet.

The frame is written with its named index levels reset to columns, so any
index shape that occurs on PyPhi result tables round-trips exactly —
including levels whose entries are tuples. Object columns holding tuples
are recorded by name at encode time; parquet represents them as lists, and
the recorded names make the restoration exact rather than heuristic. NaN,
None cells, and column dtypes survive bit-exactly via pyarrow.
"""

import io

import numpy as np
import pandas as pd

from pyphi.exceptions import MissingOptionalDependenciesError

from . import schema


[docs] def dataframe_to_schema(df: pd.DataFrame) -> schema.DataFrameSchema: names = list(df.index.names) if names == [None]: if not isinstance(df.index, pd.RangeIndex): raise ValueError( "cannot serialize a DataFrame with an unnamed, non-default index" ) reset = df index_columns: tuple[str, ...] = () else: if any(name is None for name in names): raise ValueError("cannot serialize a DataFrame with unnamed index levels") reset = df.reset_index() index_columns = tuple(str(name) for name in names) tuple_columns = tuple( str(column) for column in reset.columns if reset[column].dtype == object and any(isinstance(value, tuple) for value in reset[column]) ) require_pyarrow() buffer = io.BytesIO() reset.to_parquet(buffer, engine="pyarrow", index=False) return schema.DataFrameSchema( parquet=buffer.getvalue(), index_columns=index_columns, tuple_columns=tuple_columns, )
[docs] def require_pyarrow(): """Return the ``pyarrow`` module, which reads and writes parquet. Raises ------ MissingOptionalDependenciesError If ``pyarrow`` is not installed. """ try: import pyarrow except ModuleNotFoundError as error: raise MissingOptionalDependenciesError( MissingOptionalDependenciesError.MSG.format(dependencies="parquet") ) from error return pyarrow
def _as_tuple(value): if value is None: return None return tuple(x.item() if isinstance(x, np.generic) else x for x in value)
[docs] def schema_to_dataframe(struct: schema.DataFrameSchema) -> pd.DataFrame: require_pyarrow() df = pd.read_parquet(io.BytesIO(struct.parquet), engine="pyarrow") for column in struct.tuple_columns: df[column] = [_as_tuple(value) for value in df[column]] if struct.index_columns: df = df.set_index(list(struct.index_columns)) if len(struct.index_columns) == 1: df.index = pd.Index( df.index, name=struct.index_columns[0], tupleize_cols=False ) return df