254 lines
8.3 KiB
Python
254 lines
8.3 KiB
Python
from system import logger
|
|
from scipy import atleast_2d,asarray,ArrayType
|
|
|
|
|
|
class Dataset:
|
|
"""The Dataset base class.
|
|
|
|
A Dataset is an n-way array with defined string identifiers across
|
|
all dimensions.
|
|
|
|
example of use:
|
|
|
|
---
|
|
dim_name_rows = 'rows'
|
|
names_rows = ('row_a','row_b')
|
|
ids_1 = [dim_name_rows, names_rows]
|
|
|
|
dim_name_cols = 'cols'
|
|
names_cols = ('col_a','col_b','col_c','col_d')
|
|
ids_2 = [dim_name_cols, names_cols]
|
|
|
|
Array_X = rand(2,4)
|
|
data = Dataset(Array_X,(ids_1,ids_2),name="Testing")
|
|
|
|
dim_names = [dim for dim in data]
|
|
|
|
column_identifiers = [id for id in data['cols'].keys()]
|
|
column_index = [index for index in data['cols'].values()]
|
|
|
|
'cols' in data -> True
|
|
|
|
---
|
|
|
|
data = Dataset(rand(10,20)) (generates dims and ids (no links))
|
|
"""
|
|
def __init__(self,array=None,identifiers=None,shape=None,all_dims=[],**kwds):
|
|
self._name = kwds.get("name","Unnamed data")
|
|
self._dims = [] #existing dimensions in this dataset
|
|
self._map = {} # internal mapping for dataset: identifier <--> index
|
|
self.has_array = False
|
|
self.shape = None
|
|
|
|
if array==None:
|
|
if shape == None:
|
|
raise ValueError, "Must define shape if array is None"
|
|
else:
|
|
self.shape = shape
|
|
if identifiers!=None:
|
|
self._set_identifiers(identifiers,all_dims)
|
|
else:
|
|
ids = self._create_identifiers(shape,all_dims)
|
|
self._set_identifiers(ids,all_dims)
|
|
elif isinstance(array,ArrayType):
|
|
array = atleast_2d(asarray(array))
|
|
self.shape = array.shape
|
|
if shape != None:
|
|
if self.shape!=shape:
|
|
#logger.log("debug","Dataset and input shape mismatch")
|
|
raise ValueError
|
|
if identifiers!=None:
|
|
self._set_identifiers(identifiers,all_dims)
|
|
else:
|
|
ids = self._create_identifiers(self.shape,all_dims)
|
|
self._set_identifiers(ids,all_dims)
|
|
|
|
self._array = array
|
|
self.has_array = True
|
|
|
|
else:
|
|
raise ValueError, "array input must be of ArrayType or None"
|
|
|
|
self._all_dims = all_dims
|
|
|
|
def __str__self(self):
|
|
return self._name
|
|
|
|
def __iter__(self):
|
|
"""Returns an iterator over dimensions of dataset."""
|
|
return self._dims.__iter__()
|
|
|
|
def __contains__(self,dim):
|
|
"""Returns True if dim is a dimension name in dataset."""
|
|
# return self._dims.__contains__(dim)
|
|
return self._map.__contains__(dim)
|
|
|
|
def __len__(self):
|
|
"""Returns the number of dimensions in the dataset"""
|
|
return len(self._map)
|
|
|
|
def __getitem__(self,dim):
|
|
"""Return the identifers along the dimension dim."""
|
|
return self._map[dim]
|
|
|
|
def _create_identifiers(self,shape,all_dims):
|
|
"""Creates dimension names and identifier names, and returns
|
|
identifiers."""
|
|
|
|
dim_names = ['rows','cols']
|
|
ids = []
|
|
for axis,n in enumerate(shape):
|
|
if axis<2:
|
|
dim_suggestion = dim_names[axis]
|
|
else:
|
|
dim_suggestion = 'dim'
|
|
while dim_suggestion in all_dims:
|
|
dim_suggestion = self._suggest_dim_name(dim_suggestion,all_dims)
|
|
identifier_creation = [str(axis) + "_" + i for i in map(str,range(n))]
|
|
ids.append((dim_suggestion,identifier_creation))
|
|
all_dims.append(dim_suggestion)
|
|
return ids
|
|
|
|
def _set_identifiers(self,identifiers,all_dims):
|
|
"""Creates internal mapping of identifiers structure."""
|
|
for dim,ids in identifiers:
|
|
pos_map={}
|
|
if dim not in self._dims:
|
|
self._dims.append(dim)
|
|
all_dims.append(dim)
|
|
else:
|
|
raise ValueError, "Dimension names must be unique"
|
|
|
|
for pos,id in enumerate(ids):
|
|
pos_map[id] = pos
|
|
self._map[dim] = pos_map
|
|
shape_chk = [len(i) for j,i in identifiers]
|
|
if shape_chk != list(self.shape):
|
|
raise ValueError, "Shape input: %s and array: %s mismatch" %(shape_chk,self.shape)
|
|
|
|
def _suggest_dim_name(self,dim_name,all_dims):
|
|
"""Suggests a unique name for dim and returns it"""
|
|
c = 0
|
|
while dim_name in all_dims:
|
|
dim_name = dim_name + "_" + str(c)
|
|
c+=1
|
|
return dim_name
|
|
|
|
def asarray(self):
|
|
"""Returns the numeric array (data) of dataset"""
|
|
if not self.has_array:
|
|
raise ValueError, "Dataset is empty"
|
|
else:
|
|
return self._array
|
|
|
|
def add_array(self,array):
|
|
"""Adds array as an ArrayType object.
|
|
A one-dim array is transformed to a two-dim array (row-vector)
|
|
"""
|
|
|
|
if self.has_array:
|
|
raise ValueError, "Dataset has array"
|
|
else:
|
|
if (len(self._map)!=len(array.shape)):
|
|
raise ValueError, "range(array_dims) and range(dim_names) mismatch"
|
|
if self.shape!=array.shape:
|
|
raise ValueError, "Input array must be of similar dimensions as dataset"
|
|
self._array = atleast_2d(asarray(array))
|
|
self.has_array = True
|
|
|
|
def get_name(self):
|
|
"""Returns dataset name"""
|
|
return self._name
|
|
|
|
def get_matrix(self):
|
|
"""Returns internal numeric matrix for dataset."""
|
|
return self._array
|
|
|
|
def get_all_dims(self):
|
|
"""Returns all dimensions in project"""
|
|
return self._all_dims
|
|
|
|
def get_dim_names(self):
|
|
"""Returns dim names"""
|
|
return [dim for dim in self._dims]
|
|
|
|
def get_identifiers(self, dim, indices=None):
|
|
"""Returns identifiers along dim, sorted by position (index).
|
|
|
|
You can optionally provide a list of indices to get only the
|
|
identifiers of a given position.
|
|
"""
|
|
items = self._map[dim].items()
|
|
backitems=[ [v[1],v[0]] for v in items]
|
|
backitems.sort()
|
|
sorted_ids=[ backitems[i][1] for i in range(0,len(backitems))]
|
|
|
|
# we use id as scipy-arrays return a new array on boolean
|
|
# operations
|
|
if id(indices) != id(None):
|
|
return [sorted_ids[index] for index in indices]
|
|
else:
|
|
return sorted_ids
|
|
|
|
def get_indices(self, dim, idents):
|
|
"""Get indices for identifiers along dimension."""
|
|
reverse = {}
|
|
for key, value in self._map[dim].items():
|
|
reverse[value] = key
|
|
return [self._map[dim][key] for key in idents]
|
|
|
|
|
|
class CategoryDataset(Dataset):
|
|
"""The category dataset class.
|
|
|
|
A dataset for representing class information as binary
|
|
matrices (0/1-matrices).
|
|
|
|
There is support for using a less memory demanding, and
|
|
fast intersection look-ups by representing the binary matrix as a
|
|
dictionary in each dimension.
|
|
"""
|
|
|
|
def __init__(self):
|
|
Dataset.__init__(self)
|
|
self.has_collection = False
|
|
|
|
def as_array(self):
|
|
"""Returns data as binary matrix"""
|
|
if not self.has_array and self.has_collection:
|
|
#build numeric array
|
|
pass
|
|
|
|
def as_collection(self,dim):
|
|
"""Returns data as collection along dim"""
|
|
pass
|
|
|
|
def add_collection(self,input_dict):
|
|
"""Adds a category data as collection.
|
|
|
|
A collection is a datastructure that contains a dictionary for
|
|
each pair of dimension in dataset, keyed by identifiers and
|
|
values is a set of identifiers in the other dimension
|
|
"""
|
|
#build category data as double dicts
|
|
pass
|
|
|
|
|
|
class GraphDataset(Dataset):
|
|
"""The graph dataset class.
|
|
|
|
A dataset class for representing graphs using an adjacency matrix
|
|
(aka. restricted to square symmetric signed integers matrices)
|
|
|
|
If the library NetworkX is installed, there is support for
|
|
representing the graph as a NetworkX.Graph, or NetworkX.XGraph structure.
|
|
"""
|
|
def __init__(self):
|
|
Dataset.__init(self)
|
|
self.has_graph = False
|
|
|
|
class Selection:
|
|
"""Handles selected identifiers along each dimension of a dataset"""
|
|
def __init__(self):
|
|
self.current_selection={}
|