def test_bus_to_hierarchy_b(self) -> None: class CustomError(Exception): pass tree1 = dict(a_I=Index((1,2,3)), a_II=Index((1,2,3))) tree2 = dict(b_I=Index((1,2,3)), b_II=Index((1,2,3))) tree3 = dict(c_I=Index((1,2,3)), c_II=Index((1,2,3))) index1 = IndexHierarchy.from_tree(tree1) index2 = IndexHierarchy.from_tree(tree2) index3 = IndexHierarchy.from_tree(tree3) values = np.arange(36).reshape(6,6) # Align all the frames on columns! f1 = Frame(values, index=index1, columns=index1, name='f1') f2 = Frame(values, index=index2, columns=index1, name='f2') f3 = Frame(values, index=index3, columns=index1, name='f3') b1 = Bus.from_frames((f1, f2, f3)) def test_assertions(hierarchy: IndexHierarchy, opposite: Index) -> None: expected_tree = dict(f1=tree1, f2=tree2, f3=tree3) self.compare_trees(hierarchy.to_tree(), expected_tree) self.assertTrue(index1.equals(opposite)) test_assertions(*bus_to_hierarchy(b1, axis=0, deepcopy_from_bus=False, init_exception_cls=CustomError)) test_assertions(*bus_to_hierarchy(b1, axis=0, deepcopy_from_bus=True, init_exception_cls=CustomError)) # Cannot do this since the frames do not share the same index with self.assertRaises(CustomError): bus_to_hierarchy(b1, axis=1, deepcopy_from_bus=False, init_exception_cls=CustomError) with self.assertRaises(CustomError): bus_to_hierarchy(b1, axis=1, deepcopy_from_bus=True, init_exception_cls=CustomError) # Align all the frames on index! f1 = Frame(values, index=index1, columns=index1, name='f1') f2 = Frame(values, index=index1, columns=index2, name='f2') f3 = Frame(values, index=index1, columns=index3, name='f3') b1 = Bus.from_frames((f1, f2, f3)) test_assertions(*bus_to_hierarchy(b1, axis=1, deepcopy_from_bus=False, init_exception_cls=CustomError)) test_assertions(*bus_to_hierarchy(b1, axis=1, deepcopy_from_bus=True, init_exception_cls=CustomError)) # Cannot do this since the frames do not share the same columns with self.assertRaises(CustomError): bus_to_hierarchy(b1, axis=0, deepcopy_from_bus=False, init_exception_cls=CustomError) with self.assertRaises(CustomError): bus_to_hierarchy(b1, axis=0, deepcopy_from_bus=True, init_exception_cls=CustomError)
def buses_to_hierarchy( buses: tp.Iterable[Bus], labels: tp.Iterable[tp.Hashable], deepcopy_from_bus: bool, init_exception_cls: tp.Type[Exception], ) -> IndexHierarchy: ''' Given an iterable of named :obj:`Bus` derive a :obj:`Series` with an :obj:`IndexHierarchy`. ''' # NOTE: for now, the Returned Series will have bus Names as values; this requires the Yarn to store a dict, not a list extractor = get_extractor(deepcopy_from_bus, is_array=False, memo_active=False) tree = {} for label, bus in zip(labels, buses): if not isinstance(bus, Bus): raise init_exception_cls(f'Must provide an interable of Bus.') if label in tree: raise init_exception_cls( f'Bus names must be unique: {label} duplicated') tree[label] = extractor(bus._index) return IndexHierarchy.from_tree(tree)
def bus_to_hierarchy( bus: tp.Union[Bus, 'Yarn'], axis: int, deepcopy_from_bus: bool, init_exception_cls: tp.Type[Exception], ) -> tp.Tuple[IndexHierarchy, IndexBase]: ''' Given a :obj:`Bus` and an axis, derive a :obj:`IndexHierarchy`; also return and validate the :obj:`Index` of the opposite axis. ''' # NOTE: need to extract just axis labels, not the full Frame; need new Store/Bus loaders just for label data extractor = get_extractor(deepcopy_from_bus, is_array=False, memo_active=False) def tree_extractor(index: IndexBase) -> tp.Union[IndexBase, TreeNodeT]: index = extractor(index) if isinstance(index, IndexHierarchy): return index.to_tree() return index tree: TreeNodeT = {} opposite: tp.Optional[IndexBase] = None for label, f in bus.items(): if axis == 0: tree[label] = tree_extractor(f.index) if opposite is None: opposite = extractor(f.columns) else: if not opposite.equals(f.columns): raise init_exception_cls( 'opposite axis must have equivalent indices') elif axis == 1: tree[label] = tree_extractor(f.columns) if opposite is None: opposite = extractor(f.index) else: if not opposite.equals(f.index): raise init_exception_cls( 'opposite axis must have equivalent indices') else: raise AxisInvalid(f'invalid axis {axis}') # NOTE: we could try to collect index constructors by using the index of the Bus and observing the inidices of the contained Frames, but it is not clear that will be better then using IndexAutoConstructorFactory return IndexHierarchy.from_tree( tree, index_constructors=IndexAutoConstructorFactory ), opposite # type: ignore
def from_frame(cls, frame: Frame, *, chunksize: int, retain_labels: bool, axis: int = 0, name: NameType = None, label_extractor: tp.Optional[tp.Callable[[IndexBase], tp.Hashable]] = None, config: StoreConfigMapInitializer = None, deepcopy_from_bus: bool = False, ) -> 'Quilt': ''' Given a :obj:`Frame`, create a :obj:`Quilt` by partitioning it along the specified ``axis`` in units of ``chunksize``, where ``axis`` 0 partitions vertically (retaining aligned columns) and 1 partions horizontally (retaining aligned index). Args: label_extractor: Function that, given the partitioned index component along the specified axis, returns a string label for that chunk. ''' vector = frame._index if axis == 0 else frame._columns vector_len = len(vector) starts = range(0, vector_len, chunksize) if len(starts) == 1: ends: tp.Iterable[int] = (vector_len,) else: ends = range(starts[1], vector_len, chunksize) if label_extractor is None: label_extractor = lambda x: x.iloc[0] #type: ignore axis_map_components: tp.Dict[tp.Hashable, IndexBase] = {} opposite = None def values() -> tp.Iterator[Frame]: nonlocal opposite for start, end in zip_longest(starts, ends, fillvalue=vector_len): if axis == 0: # along rows f = frame.iloc[start:end] label = label_extractor(f.index) #type: ignore axis_map_components[label] = f.index if opposite is None: opposite = f.columns elif axis == 1: # along columns f = frame.iloc[:, start:end] label = label_extractor(f.columns) #type: ignore axis_map_components[label] = f.columns if opposite is None: opposite = f.index else: raise AxisInvalid(f'invalid axis {axis}') yield f.rename(label) name = name if name else frame.name bus = Bus.from_frames(values(), config=config, name=name) axis_hierarchy = IndexHierarchy.from_tree(axis_map_components) return cls(bus, axis=axis, axis_hierarchy=axis_hierarchy, axis_opposite=opposite, retain_labels=retain_labels, deepcopy_from_bus=deepcopy_from_bus, )