"""
Utilities for uniform treatment of pandas tables and numpy arrays for functions which can deal with them both.
"""
from ..utils.general import AccessIterator
from ..utils.array_utils import get_shape
from ..utils.indexing import ListIndex
import numpy as np
import pandas as pd
[docs]
class IGenWrapper:
"""
The interface for a wrapper that gives a uniform access to basic methods of wrapped objects'.
"""
def __init__(self, container):
self.cont=container
def __getitem__(self, idx):
return self.cont[idx]
def __setitem__(self, idx, val):
self.cont[idx]=val
def __iter__(self):
return self.cont.__iter__()
[docs]
def get_type(self):
"""Get a string representing the wrapped object type"""
raise NotImplementedError("IGenWrapper.get_type")
[docs]
def copy(self, wrapped=False):
"""
Copy the object.
If ``wrapped==True``, return a new wrapper containing the object copy; otherwise, just return the copy.
"""
raise NotImplementedError("IGenWrapper.copy")
[docs]
def ndim(self):
return self.cont.ndim
[docs]
def shape(self):
return self.cont.shape
def __len__(self):
return self.shape()[0]
[docs]
class I1DWrapper(IGenWrapper):
"""
A wrapper containing a 1D object (a 1D numpy array or a pandas Series object).
Provides a uniform access to basic methods of a wrapped object.
"""
def __init__(self, container):
IGenWrapper.__init__(self,container)
self.r=self.Accessor(self)
self.t=self.Accessor(self)
[docs]
class Accessor:
"""
An accessor: creates a simple uniform interface to treat the wrapped object element-wise (get/set/iterate over elements).
Generated automatically for each table on creation, doesn't need to be created explicitly.
"""
def __init__(self, wrapper):
self._wrapper=wrapper
def __iter__(self):
return self._wrapper.__iter__()
def __getitem__(self, idx):
return self._wrapper.subcolumn(idx)
def __setitem__(self, idx, val):
self._wrapper[idx]=val
[docs]
def subcolumn(self, idx, wrapped=False):
"""
Return a subcolumn at index `idx`.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
raise NotImplementedError("I1DWrapper.subtable")
[docs]
@staticmethod
def from_array(array, index=None, force_copy=False, wrapped=False):
"""
Build a new object of the type corresponding to the wrapper from the supplied `array` (a 1D numpy array or a list).
If ``force_copy==True``, make a copy of supplied array.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
raise NotImplementedError("I1DWrapper.from_array")
[docs]
@classmethod
def from_columns(cls, columns, column_names=None, index=None, wrapped=False): # pylint: disable=unused-argument
"""
Build a new object of the type corresponding to the wrapper from the supplied `columns` (a list of columns; only length-1 lists is supported).
`column_names` parameter is ignored.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
if len(columns)!=1:
raise ValueError("Array1DWrapper only supports single columns, got {} columns".format(len(columns)))
return cls.from_array(columns[0],index=index,wrapped=wrapped)
[docs]
def array_replaced(self, array, force_copy=False, preserve_index=False, wrapped=False): # pylint: disable=unused-argument
"""
Return a copy of the column with the data replaced by `array`.
All of the parameters are the same as in :meth:`from_array`.
"""
return self.from_array(array,index=self.get_index() if preserve_index else None,force_copy=force_copy,wrapped=wrapped)
[docs]
def get_index(self):
"""Get index of the given 1D trace, or ``None`` if none is available"""
return None
[docs]
def get_type(self):
"""Get a string representing the wrapped object type"""
raise NotImplementedError("I1DWrapper.get_type")
[docs]
def copy(self, wrapped=False):
"""
Copy the object.
If ``wrapped==True``, return a new wrapper containing the object copy; otherwise, just return the copy.
"""
raise NotImplementedError("I1DWrapper.copy")
[docs]
def ndim(self):
return 1
[docs]
class Array1DWrapper(I1DWrapper):
"""
A wrapper for a 1D numpy array.
Provides a uniform access to basic methods of a wrapped object.
"""
def __init__(self, container):
container=np.asarray(container)
if container.ndim!=1:
raise ValueError("Array1DWrapper only supports 1D arrays, got {}D array".format(container.ndim))
I1DWrapper.__init__(self,container)
[docs]
def get_deleted(self, idx, wrapped=False):
"""
Return a copy of the column with the data at index `idx` deleted.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
new_cont=np.delete(self.cont,idx,axis=0)
return Array1DWrapper(new_cont) if wrapped else new_cont
def __delitem__(self, idx):
self.cont=self.get_deleted(idx)
[docs]
def get_inserted(self, idx, val, wrapped=False):
"""
Return a copy of the column with the data `val` added at index `idx`.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
new_cont=np.insert(self.cont,idx,val,axis=0)
return Array1DWrapper(new_cont) if wrapped else new_cont
[docs]
def insert(self, idx, val):
"""Add data `val` to index `idx`"""
self.cont=self.get_inserted(idx,val)
[docs]
def get_appended(self, val, wrapped=False):
"""
Return a copy of the column with the data `val` appended at the end.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
new_cont=np.append(self.cont,val,axis=0)
return Array1DWrapper(new_cont) if wrapped else new_cont
[docs]
def append(self, val):
"""Append data `val` to the end"""
self.cont=self.get_appended(val)
[docs]
def subcolumn(self, idx, wrapped=False):
"""
Return a subcolumn at index `idx`.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
return Array1DWrapper(self.cont[idx]) if wrapped else self.cont[idx]
[docs]
@staticmethod
def from_array(array, index=None, force_copy=False, wrapped=False):
"""
Build a new object of the type corresponding to the wrapper from the supplied `array` (a 1D numpy array or a list).
If ``force_copy==True``, make a copy of supplied array.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
new_cont=np.array(array) if force_copy else np.asarray(array)
return Array1DWrapper(new_cont) if wrapped else new_cont
[docs]
def get_type(self):
"""Get a string representing the wrapped object type"""
return "1d.array"
[docs]
def copy(self, wrapped=False):
"""
Copy the object.
If ``wrapped==True``, return a new wrapper containing the object copy; otherwise, just return the copy.
"""
new_cont=self.cont.copy()
return Array1DWrapper(new_cont) if wrapped else new_cont
[docs]
class Series1DWrapper(I1DWrapper):
"""
A wrapper for a pandas Series object.
Provides a uniform access to basic methods of a wrapped object.
"""
def __init__(self, container):
if not isinstance(container,pd.Series):
container=pd.Series(container)
I1DWrapper.__init__(self,container)
def __getitem__(self, idx):
return self.cont.iloc[idx]
def __setitem__(self, idx, val):
self.cont.iloc[idx]=val
[docs]
def get_deleted(self, idx, wrapped=False):
"""
Return a copy of the column with the data at index `idx` deleted.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
new_cont=self.cont.drop(self.cont.index[idx])
return Series1DWrapper(new_cont) if wrapped else new_cont
def __delitem__(self, idx):
self.cont.drop(self.cont.index[idx],inplace=True)
[docs]
def get_inserted(self, idx, val, wrapped=False):
"""
Return a copy of the column with the data `val` added at index `idx`.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
new_cont=pd.concat([self.cont.iloc[:idx],pd.Series(val,copy=False),self.cont.iloc[idx:]])
return Series1DWrapper(new_cont) if wrapped else new_cont
[docs]
def get_appended(self, val, wrapped=False):
"""
Return a copy of the column with the data `val` appended at the end.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
new_cont=self.cont.append(val)
return Series1DWrapper(new_cont) if wrapped else new_cont
[docs]
def subcolumn(self, idx, wrapped=False):
"""
Return a subcolumn at index `idx`.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
return Series1DWrapper(self.cont.iloc[idx]) if wrapped else self.cont.iloc[idx]
[docs]
@staticmethod
def from_array(array, index=None, force_copy=False, wrapped=False):
"""
Build a new object of the type corresponding to the wrapper from the supplied `array` (a 1D numpy array or a list).
If ``force_copy==True``, make a copy of supplied array.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
new_cont=pd.Series(array,index=index,copy=force_copy)
return Series1DWrapper(new_cont) if wrapped else new_cont
[docs]
def get_index(self):
"""Get index of the given 1D trace, or ``None`` if none is available"""
return self.cont.index
[docs]
def get_type(self):
"""Get a string representing the wrapped object type"""
return "1d.series"
[docs]
def copy(self, wrapped=False):
"""
Copy the object.
If ``wrapped==True``, return a new wrapper containing the object copy; otherwise, just return the copy.
"""
new_cont=self.cont.copy()
return Series1DWrapper(new_cont) if wrapped else new_cont
[docs]
class I2DWrapper(IGenWrapper):
"""
A wrapper containing a 2D object (a 2D numpy array or a pandas DataFrame object).
Provides a uniform access to basic methods of a wrapped object.
"""
def __init__(self, container, r=None, c=None, t=None):
IGenWrapper.__init__(self,container)
self.r=r
self.c=c
self.t=t
[docs]
@classmethod
def from_columns(cls, columns, column_names=None, index=None, wrapped=False):
"""
Build a new object of the type corresponding to the wrapper from the supplied `columns` (a list of columns).
`column_names` supplies names of the columns (only relevant for :class:`DataFrame2DWrapper`).
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
raise NotImplementedError("I2DWrapper.from_columns")
[docs]
def columns_replaced(self, columns, preserve_index=False, wrapped=False):
"""
Return copy of the object with the data replaced by `columns`.
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
return self.from_columns(columns,self.c.get_names(),index=self.get_index() if preserve_index else None,wrapped=wrapped)
[docs]
@staticmethod
def from_array(array, column_names=None, index=None, force_copy=False, wrapped=False):
"""
Build a new object of the type corresponding to the wrapper from the supplied `array` (a list of rows or a 2D numpy array).
`column_names` supplies names of the columns (only relevant for :class:`DataFrame2DWrapper`).
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
raise NotImplementedError("I2DWrapper.from_array")
[docs]
def array_replaced(self, array, preserve_index=None, force_copy=False, wrapped=False):
"""
Return a copy of the column with the data replaced by `array`.
All of the parameters are the same as in :meth:`from_array`.
"""
return self.from_array(array,self.c.get_names(),index=self.get_index() if preserve_index else None,force_copy=force_copy,wrapped=wrapped)
[docs]
def get_index(self):
"""Get index of the given 2D table, or ``None`` if none is available"""
return None
[docs]
def get_type(self):
"""Get a string representing the wrapped object type"""
raise NotImplementedError("I2DWrapper.get_type")
[docs]
def copy(self, wrapped=False):
"""
Copy the object.
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
raise NotImplementedError("I2DWrapper.copy")
[docs]
def column(self, idx, wrapped=False):
"""
Get a column at index `idx`.
Return a 1D numpy array for a 2D numpy array object, and an Series object for a pandas DataFrame.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
raise NotImplementedError("I2DWrapper.column")
[docs]
def subtable(self, idx, wrapped=False):
"""
Return a subtable at index `idx`.
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
raise NotImplementedError("I2DWrapper.subtable")
[docs]
def ndim(self):
return 2
[docs]
class Array2DWrapper(I2DWrapper):
"""
A wrapper for a 2D numpy array.
Provides a uniform access to basic methods of a wrapped object.
"""
def __init__(self, container):
container=np.asarray(container)
if container.ndim!=2:
raise ValueError("Array2DWrapper only supports 2D arrays, got {}D array".format(container.ndim))
I2DWrapper.__init__(self,container,
self.RowAccessor(self,container),self.ColumnAccessor(self,container),self.TableAccessor(container))
[docs]
def set_container(self,cont):
self.cont=cont
self.r._storage=cont
self.c._storage=cont
self.t._storage=cont
[docs]
class RowAccessor:
"""
A row accessor: creates a simple uniform interface to treat the wrapped object row-wise (append/insert/delete/iterate over rows).
Generated automatically for each table on creation, doesn't need to be created explicitly.
"""
def __init__(self, wrapper, storage):
self._wrapper=wrapper
self._storage=storage
def __iter__(self):
return AccessIterator(self._storage,lambda obj,idx: obj[idx])
def __getitem__(self, idx):
return self._storage[idx]
def __setitem__(self, idx, val):
self._storage[idx]=val
[docs]
def get_deleted(self, idx, wrapped=False):
"""
Return a new table with the rows at `idx` deleted.
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
new_cont=np.delete(self._storage,idx,axis=0)
return Array2DWrapper(new_cont) if wrapped else new_cont
def __delitem__(self, idx):
self._wrapper.set_container(self.get_deleted(idx))
[docs]
def get_inserted(self, idx, val, wrapped=False):
"""
Return a new table with new rows given by `val` inserted at `idx`.
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
new_cont=np.insert(self._storage,idx,val,axis=0)
return Array2DWrapper(new_cont) if wrapped else new_cont
[docs]
def insert(self, idx, val):
"""
Insert new rows given by `val` at index `idx`.
"""
self._wrapper.set_container(self.get_inserted(idx,val))
[docs]
def get_appended(self, val, wrapped=False):
"""
Return a new table with new rows given by `val` appended to the end of the table.
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
new_cont=np.append(self._storage,val,axis=0)
return Array2DWrapper(new_cont) if wrapped else new_cont
[docs]
def append(self, val):
"""Insert new rows given by `val` to the end of the table"""
self._wrapper.set_container(self.get_appended(val))
[docs]
class ColumnAccessor:
"""
A column accessor: creates a simple uniform interface to treat the wrapped object column-wise (append/insert/delete/iterate over columns).
Generated automatically for each table on creation, doesn't need to be created explicitly.
"""
def __init__(self, wrapper, storage):
self._wrapper=wrapper
self._storage=storage
def __iter__(self):
return AccessIterator(self._storage,lambda obj,idx: obj[:,idx])
def __getitem__(self, idx):
return self._storage[:,idx]
def __setitem__(self, idx, val):
self._storage[:,idx]=val
[docs]
def get_deleted(self, idx, wrapped=False):
"""
Return a new table with the columns at `idx` deleted.
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
new_cont=np.delete(self._storage,idx,axis=1)
return Array2DWrapper(new_cont) if wrapped else new_cont
def __delitem__(self, idx):
self._wrapper.set_container(self.get_deleted(idx))
[docs]
def get_inserted(self, idx, val, wrapped=False):
"""
Return a new table with new columns given by `val` inserted at `idx`.
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
new_cont=np.insert(self._storage,idx,val,axis=1)
return Array2DWrapper(new_cont) if wrapped else new_cont
[docs]
def insert(self, idx, val):
"""
Insert new columns given by `val` at index `idx`.
"""
self._wrapper.set_container(self.get_inserted(idx,val))
[docs]
def get_appended(self, val, wrapped=False):
"""
Return a new table with new columns given by `val` appended to the end of the table.
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
new_cont=np.append(self._storage,val,axis=1)
return Array2DWrapper(new_cont) if wrapped else new_cont
[docs]
def append(self, val):
"""Insert new columns given by `val` to the end of the table"""
self._wrapper.set_container(self.get_appended(val))
[docs]
def set_names(self, names):
"""Set column names (does nothing)"""
[docs]
def get_names(self):
"""Get column names (all names are ``None``)"""
return [None]*self._storage.shape[1]
[docs]
def get_column_index(self, idx):
"""Get number index for a given column index"""
return (idx if idx>=0 else self._storage.shape[1]-idx)
[docs]
class TableAccessor:
"""
A table accessor: accessing the table data through this interface returns an object of the appropriate type
(numpy array for numpy wrapped object, and a DataFrame for a pandas DataFrame wrapped object).
Generated automatically for each table on creation, doesn't need to be created explicitly.
"""
def __init__(self, storage):
self._storage=storage
def __iter__(self):
return AccessIterator(self)
def __getitem__(self, idx):
return self._storage[idx]
def __setitem__(self, idx, val):
self._storage[idx]=val
[docs]
def subtable(self, idx, wrapped=False):
"""
Return a subtable at index `idx` of the appropriate type (2D numpy array).
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
return Array2DWrapper(self.cont[idx]) if wrapped else self.cont[idx]
[docs]
def column(self, idx, wrapped=False):
"""
Get a column at index `idx` as a 1D numpy array.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
return Array1DWrapper(self.cont[:,idx]) if wrapped else self.cont[:,idx]
[docs]
@classmethod
def from_columns(cls, columns, column_names=None, index=None, wrapped=False):
"""
Build a new object of the type corresponding to the wrapper from the supplied `columns` (a list of columns).
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
`column_names` parameter is ignored.
"""
new_cont=np.column_stack(columns)
return Array2DWrapper(new_cont) if wrapped else new_cont
[docs]
@staticmethod
def from_array(array, column_names=None, index=None, force_copy=False, wrapped=False):
"""
Build a new object of the type corresponding to the wrapper from the supplied `array` (a list of rows or a 2D numpy array).
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
`column_names` parameter is ignored.
"""
new_cont=np.array(array) if force_copy else np.asarray(array)
return Array2DWrapper(new_cont) if wrapped else new_cont
[docs]
def get_type(self):
"""Get a string representing the wrapped object type"""
return "2d.array"
[docs]
def copy(self, wrapped=False):
"""
Copy the object.
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
new_cont=self.cont.copy()
return Array2DWrapper(new_cont) if wrapped else new_cont
[docs]
class DataFrame2DWrapper(I2DWrapper):
"""
A wrapper for a pandas DataFrame object.
Provides a uniform access to basic methods of a wrapped object.
"""
def __init__(self, container):
if not isinstance(container,pd.DataFrame):
container=pd.DataFrame(container,copy=False)
if container.ndim!=2:
raise ValueError("DataFrame2DWrapper only supports 2D arrays, got {}D array".format(container.ndim))
I2DWrapper.__init__(self,container,
self.RowAccessor(self,container),self.ColumnAccessor(self,container),self.TableAccessor(container))
def __getitem__(self, idx):
res=self.cont.iloc[idx]
return np.asarray(res) if len(get_shape(res))>0 else res
def __setitem__(self, idx, val):
self.cont.iloc[idx]=val
[docs]
class RowAccessor:
"""
A row accessor: creates a simple uniform interface to treat the wrapped object row-wise (append/insert/delete/iterate over rows).
Generated automatically for each table on creation, doesn't need to be created explicitly.
"""
def __init__(self, wrapper, storage):
self._wrapper=wrapper
self._storage=storage
def __iter__(self):
return self._storage.__iter__()
def __getitem__(self, idx):
return self._storage.iloc[idx]
def __setitem__(self, idx, val):
self._storage.iloc[idx]=val
[docs]
def get_deleted(self, idx, wrapped=False):
"""
Return a copy of the column with the data at index `idx` deleted.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
new_cont=self._storage.drop(self._storage.index[idx])
return DataFrame2DWrapper(new_cont) if wrapped else new_cont
def __delitem__(self, idx):
self._storage.drop(self._storage.index[idx],inplace=True)
[docs]
def get_inserted(self, idx, val, wrapped=False):
"""
Return a new table with new rows given by `val` inserted at `idx`.
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
new_cont=pd.concat([self._storage.iloc[:idx],pd.DataFrame(val,copy=False),self._storage.iloc[idx:]])
return DataFrame2DWrapper(new_cont) if wrapped else new_cont
[docs]
def insert(self, idx, val):
"""
Insert new rows given by `val` at index `idx`.
"""
self._wrapper.set_container(self.get_inserted(idx,val))
[docs]
def get_appended(self, val, wrapped=False):
"""
Return a new table with new rows given by `val` appended to the end of the table.
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
new_cont=self._storage.append(val)
return DataFrame2DWrapper(new_cont) if wrapped else new_cont
[docs]
def append(self, val):
"""Insert new rows given by `val` to the end of the table"""
self._wrapper.set_container(self.get_appended(val))
[docs]
class ColumnAccessor:
"""
A column accessor: creates a simple uniform interface to treat the wrapped object column-wise (append/insert/delete/iterate over columns).
Generated automatically for each table on creation, doesn't need to be created explicitly.
"""
def __init__(self, wrapper, storage):
self._wrapper=wrapper
self._storage=storage
def __iter__(self):
for _,c in self._storage.items():
yield c
def __getitem__(self, idx):
return self._storage.iloc[:,idx]
def __setitem__(self, idx, val):
self._storage.iloc[:,idx]=val
[docs]
def get_deleted(self, idx, wrapped=False):
"""
Return a new table with the columns at `idx` deleted.
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
try:
new_cont=self._storage.drop(idx,axis="columns")
except (TypeError,KeyError):
new_cont=self._storage.drop(self._storage.columns[idx],axis="columns")
return DataFrame2DWrapper(new_cont) if wrapped else new_cont
def __delitem__(self, idx):
try:
self._storage.drop(idx,axis="columns",inplace=True)
except (TypeError,KeyError):
self._storage.drop(self._storage.columns[idx],axis="columns",inplace=True)
[docs]
def get_inserted(self, idx, val, column_name=None, wrapped=False):
"""
Return a new table with new columns given by `val` inserted at `idx`.
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
new_wrap=self._wrapper.copy()
new_wrap.insert(idx,val,column_name=column_name)
return new_wrap if wrapped else new_wrap.cont
[docs]
def insert(self, idx, val, column_name=None):
"""Insert new columns given by `val` at index `idx`"""
if column_name is None:
column_name=self._storage.shape[1]
while column_name in self._storage.columns:
column_name+=1
self._storage.insert(idx,column_name,val)
[docs]
def get_appended(self, val, column_name=None, wrapped=False):
"""
Return a new table with new columns given by `val` appended to the end of the table.
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
return self.get_inserted(-1,val,column_name=column_name,wrapped=wrapped)
[docs]
def append(self, val, column_name=None):
"""Insert new columns given by `val` to the end of the table"""
return self.insert(-1,val,column_name=column_name)
[docs]
def set_names(self, names):
"""Set column names"""
self._storage.columns=names
[docs]
def get_names(self):
"""Get column names"""
return list(self._storage.columns)
[docs]
def get_column_index(self, idx):
"""Get number index for a given column index"""
idx=ListIndex(idx,self.get_names()).idx
return (idx if idx>=0 else self._storage.shape[1]-idx)
[docs]
class TableAccessor:
"""
A table accessor: accessing the table data through this interface returns an object of the appropriate type
(numpy array for numpy wrapped object, and a DataFrame for a pandas DataFrame wrapped object).
Generated automatically for each table on creation, doesn't need to be created explicitly.
"""
def __init__(self, storage):
self._storage=storage
def __iter__(self):
return self._storage.iloc.__iter__()
def __getitem__(self, idx):
return self._storage.iloc[idx]
def __setitem__(self, idx, val):
self._storage.iloc[idx]=val
def _get_df_columns(self, idx):
try:
return self.cont.loc(axis="columns")[idx]
except (TypeError,KeyError):
return self.cont.iloc(axis="columns")[idx]
[docs]
def subtable(self, idx, wrapped=False):
"""
Return a subtable at index `idx` of the appropriate type (pandas DataFrame).
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
return DataFrame2DWrapper(self._get_df_columns(idx)) if wrapped else self._get_df_columns(idx)
[docs]
def column(self, idx, wrapped=False):
"""
Get a column at index `idx` as a pandas Series object.
If ``wrapped==True``, return a new wrapper containing the column; otherwise, just return the column.
"""
return Series1DWrapper(self._get_df_columns(idx)) if wrapped else self._get_df_columns(idx)
[docs]
@classmethod
def from_columns(cls, columns, column_names=None, index=None, wrapped=False):
"""
Build a new object of the type corresponding to the wrapper from the supplied `columns` (a list of columns).
`column_names` supplies names of the columns (only relevant for :class:`DataFrame2DWrapper`).
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
if column_names is None:
column_names=list(range(len(columns)))
new_cont=pd.DataFrame(dict(zip(column_names,columns)),columns=column_names,index=index)
return DataFrame2DWrapper(new_cont) if wrapped else new_cont
[docs]
@staticmethod
def from_array(array, column_names=None, index=None, force_copy=False, wrapped=False):
"""
Build a new object of the type corresponding to the wrapper from the supplied `array` (a list of rows or a 2D numpy array).
`column_names` supplies names of the columns (only relevant for :class:`DataFrame2DWrapper`).
If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table.
"""
new_cont=pd.DataFrame(array,columns=column_names,index=index,copy=force_copy)
return DataFrame2DWrapper(new_cont) if wrapped else new_cont
[docs]
def get_index(self):
"""Get index of the given 2D table, or ``None`` if none is available"""
return self.cont.index
[docs]
def get_type(self):
"""Get a string representing the wrapped object type"""
return "2d.pandas"
[docs]
def copy(self, wrapped=False):
"""Copy the object. If ``wrapped==True``, return a new wrapper containing the table; otherwise, just return the table"""
new_cont=self.cont.copy()
return DataFrame2DWrapper(new_cont) if wrapped else new_cont
[docs]
def wrap1d(container):
"""Wrap a 1D container (a 1D numpy array or or a pandas Series) into an appropriate wrapper"""
if isinstance(container,pd.Series):
return Series1DWrapper(container)
return Array1DWrapper(container)
[docs]
def wrap2d(container):
"""Wrap a 2D container (a 2D numpy array or a pandas DataFrame) into an appropriate wrapper"""
if isinstance(container,pd.DataFrame):
return DataFrame2DWrapper(container)
return Array2DWrapper(container)
[docs]
def wrap(container):
"""Wrap container (a numpy array, a pandas Series or a pandas DataFrame) into an appropriate wrapper"""
if isinstance(container,IGenWrapper):
return container
ndim=len(get_shape(container))
if ndim==1:
return wrap1d(container)
elif ndim==2:
return wrap2d(container)
else:
raise ValueError("wrap only supports 1D and 2D arrays, got {}D".format(ndim))