# Path: trtools/core/columns.py
# class MultiIndexGetter(IndexGetter):
#     """
#     Handles MultiIndex. 
#     Requires that that the levels be named.
#     """
#     def sub_column(self, name):
#         return self._index.get_level_values(name)
# 
#     def level_name(self, name):
#         """
#         Get the .levels value by name
#         """
#         ind = self._index.names.index(name)
#         return self._index.levels[ind]
# 
#     @property
#     def names(self):
#         """
#         Complete based off of MultiIndex.names
#         """
#         return [c for c in self._index.names]
# 
# class ObjectIndexGetter(IndexGetter):
#     """
#     Handles an Index of objects and treats the attributes like un-ordered levels. 
#     """
#     def sub_column(self, name):
#         return pd.Index([_get_val(col, name) for col in self._index])
# 
#     def level_name(self, name):
#         """
#         Note that the only way to get the equivalent of MultiIndex.levels is to get all 
#         values and then run unique. There should be caching done somewhere here
#         """
#         vals = self.sub_column(name)
#         ind = vals.unique()
#         ind.sort()
#         ind = pd.Index(ind)
#         return ind
# 
#     @property
#     def names(self):
#         """
#         Try to grab the proper attrs for the Columns
#         Best case is that the object has a keys method.
#         """
#         test = self._index[0]
#         try:
#             names = list(test.keys())
#             return names
#         except:
#             names = list(test.__dict__.keys())
#             return names
# 
# class LevelWrapper(object):
#     def __init__(self, name, getter):
#         self.name = name
#         self.getter = getter
# 
#     def __getitem__(self, key):
#         # get level value by .levels which are the distinct monotonic values
#         if isinstance(key, int):
#             return self.labels[key]
#         raise KeyError(key)
# 
#     @property
#     def labels(self):
#         """
#         Return the acutal labels. Equivalent of MultiIndex.levels[x]
#         """
#         return self.getter.level_name(self.name)
# 
#     @property
#     def values(self):
#         """ Returns the actual values """
#         vals = self.getter.sub_column(self.name)
#         return vals
# 
#     def __array__(self):
#         # TODO
#         # Reset dtypes to proper dtype. Other than the numerics, things like bools
#         # come out of here as dtype == object.
#         vals = self.values
#         # vals is pd.Index
#         # pd.Index promotes flaots to objects, we demote to float if it's numeric
#         if vals.is_numeric() and not isinstance(vals, pd.Int64Index):
#             vals = vals.values.astype(float)
#         else:
#             # always return an np.ndarray and not index
#             vals = vals.values
#         return vals
# 
#     #----------------------------------------------------------------------
#     #   Arithmetic operators
# 
#     __add__ = _sub_method(operator.add, '__add__')
#     __sub__ = _sub_method(operator.sub, '__sub__')
#     __mul__ = _sub_method(operator.mul, '__mul__')
#     __truediv__ = _sub_method(operator.truediv, '__truediv__')
#     __floordiv__ = _sub_method(operator.floordiv, '__floordiv__')
#     __pow__ = _sub_method(operator.pow, '__pow__')
# 
#     #__radd__ = _sub_method(_radd_compat, '__add__')
#     __rmul__ = _sub_method(operator.mul, '__mul__')
#     __rsub__ = _sub_method(lambda x, y: y - x, '__sub__')
#     __rtruediv__ = _sub_method(lambda x, y: y / x, '__truediv__')
#     __rfloordiv__ = _sub_method(lambda x, y: y // x, '__floordiv__')
#     __rpow__ = _sub_method(lambda x, y: y ** x, '__pow__')
# 
#     # comparisons
#     __gt__ = _sub_method(operator.gt, '__gt__')
#     __ge__ = _sub_method(operator.ge, '__ge__')
#     __lt__ = _sub_method(operator.lt, '__lt__')
#     __le__ = _sub_method(operator.le, '__le__')
# 
#     def __eq__(self, other):
#         vals = np.array(self)
#         try:
#             return np.isclose(vals, other)
#         except TypeError:
#             # likely here because vals/other is not numeric
#             return operator.eq(vals, other)
# 
#     __ne__ = _sub_method(operator.ne, '__ne__')
# 
#     # Python 2 division operators
#     if not compat.PY3:
#         __div__ = _sub_method(operator.div, '__div__')
#         __rdiv__ = _sub_method(lambda x, y: y / x, '__div__')
#         __idiv__ = __div__


from unittest import TestCase
from trtools.core.columns import MultiIndexGetter, ObjectIndexGetter, LevelWrapper
import itertools
import pandas as pd
import numpy as np
import trtools.util.testing as tm
    import nose                                                                      
        tm.assert_almost_equal(test, correct)

        test = lw > 1
        correct = mi.get_level_values('limit') > 1
        tm.assert_almost_equal(test, correct)

        # test getitem
        assert lw[0] == 0
        assert lw[1] == 1

    def test_level_wrapper_oi(self):
        """
        Test level wrapper against Object Index
        """
        class ObjectCol(object):
            def __init__(self, bob, frank):
                self.bob = bob
                self.frank = frank

            def __repr__(self):
                return "bob={bob},frank={frank}".format(**self.__dict__)

        bobs = [3,4,2,1,2,2,1,]
        franks = list(range(len(bobs)))

        objects = [ObjectCol(bob, frank) for bob, frank in zip(bobs, franks)]
        df = pd.DataFrame({'test'+str(i) : np.random.randn(len(objects)) for i in range(len(objects))})
        df.columns = objects
        assert isinstance(df.columns , pd.Index)


# Path: trtools/core/columns.py
# class MultiIndexGetter(IndexGetter):
#     """
#     Handles MultiIndex. 
#     Requires that that the levels be named.
#     """
#     def sub_column(self, name):
#         return self._index.get_level_values(name)
# 
#     def level_name(self, name):
#         """
#         Get the .levels value by name
#         """
#         ind = self._index.names.index(name)
#         return self._index.levels[ind]
# 
#     @property
#     def names(self):
#         """
#         Complete based off of MultiIndex.names
#         """
#         return [c for c in self._index.names]
# 
# class ObjectIndexGetter(IndexGetter):
#     """
#     Handles an Index of objects and treats the attributes like un-ordered levels. 
#     """
#     def sub_column(self, name):
#         return pd.Index([_get_val(col, name) for col in self._index])
# 
#     def level_name(self, name):
#         """
#         Note that the only way to get the equivalent of MultiIndex.levels is to get all 
#         values and then run unique. There should be caching done somewhere here
#         """
#         vals = self.sub_column(name)
#         ind = vals.unique()
#         ind.sort()
#         ind = pd.Index(ind)
#         return ind
# 
#     @property
#     def names(self):
#         """
#         Try to grab the proper attrs for the Columns
#         Best case is that the object has a keys method.
#         """
#         test = self._index[0]
#         try:
#             names = list(test.keys())
#             return names
#         except:
#             names = list(test.__dict__.keys())
#             return names
# 
# class LevelWrapper(object):
#     def __init__(self, name, getter):
#         self.name = name
#         self.getter = getter
# 
#     def __getitem__(self, key):
#         # get level value by .levels which are the distinct monotonic values
#         if isinstance(key, int):
#             return self.labels[key]
#         raise KeyError(key)
# 
#     @property
#     def labels(self):
#         """
#         Return the acutal labels. Equivalent of MultiIndex.levels[x]
#         """
#         return self.getter.level_name(self.name)
# 
#     @property
#     def values(self):
#         """ Returns the actual values """
#         vals = self.getter.sub_column(self.name)
#         return vals
# 
#     def __array__(self):
#         # TODO
#         # Reset dtypes to proper dtype. Other than the numerics, things like bools
#         # come out of here as dtype == object.
#         vals = self.values
#         # vals is pd.Index
#         # pd.Index promotes flaots to objects, we demote to float if it's numeric
#         if vals.is_numeric() and not isinstance(vals, pd.Int64Index):
#             vals = vals.values.astype(float)
#         else:
#             # always return an np.ndarray and not index
#             vals = vals.values
#         return vals
# 
#     #----------------------------------------------------------------------
#     #   Arithmetic operators
# 
#     __add__ = _sub_method(operator.add, '__add__')
#     __sub__ = _sub_method(operator.sub, '__sub__')
#     __mul__ = _sub_method(operator.mul, '__mul__')
#     __truediv__ = _sub_method(operator.truediv, '__truediv__')
#     __floordiv__ = _sub_method(operator.floordiv, '__floordiv__')
#     __pow__ = _sub_method(operator.pow, '__pow__')
# 
#     #__radd__ = _sub_method(_radd_compat, '__add__')
#     __rmul__ = _sub_method(operator.mul, '__mul__')
#     __rsub__ = _sub_method(lambda x, y: y - x, '__sub__')
#     __rtruediv__ = _sub_method(lambda x, y: y / x, '__truediv__')
#     __rfloordiv__ = _sub_method(lambda x, y: y // x, '__floordiv__')
#     __rpow__ = _sub_method(lambda x, y: y ** x, '__pow__')
# 
#     # comparisons
#     __gt__ = _sub_method(operator.gt, '__gt__')
#     __ge__ = _sub_method(operator.ge, '__ge__')
#     __lt__ = _sub_method(operator.lt, '__lt__')
#     __le__ = _sub_method(operator.le, '__le__')
# 
#     def __eq__(self, other):
#         vals = np.array(self)
#         try:
#             return np.isclose(vals, other)
#         except TypeError:
#             # likely here because vals/other is not numeric
#             return operator.eq(vals, other)
# 
#     __ne__ = _sub_method(operator.ne, '__ne__')
# 
#     # Python 2 division operators
#     if not compat.PY3:
#         __div__ = _sub_method(operator.div, '__div__')
#         __rdiv__ = _sub_method(lambda x, y: y / x, '__div__')
#         __idiv__ = __div__


# Path: trtools/core/tests/test_columns.py
from unittest import TestCase
from trtools.core.columns import MultiIndexGetter, ObjectIndexGetter, LevelWrapper
import itertools
import pandas as pd
import numpy as np
import trtools.util.testing as tm
    import nose                                                                      

        tm.assert_almost_equal(test, correct)

        test = lw > 1
        correct = mi.get_level_values('limit') > 1
        tm.assert_almost_equal(test, correct)

        # test getitem
        assert lw[0] == 0
        assert lw[1] == 1

    def test_level_wrapper_oi(self):
        """
        Test level wrapper against Object Index
        """
        class ObjectCol(object):
            def __init__(self, bob, frank):
                self.bob = bob
                self.frank = frank

            def __repr__(self):
                return "bob={bob},frank={frank}".format(**self.__dict__)

        bobs = [3,4,2,1,2,2,1,]
        franks = list(range(len(bobs)))

        objects = [ObjectCol(bob, frank) for bob, frank in zip(bobs, franks)]
        df = pd.DataFrame({'test'+str(i) : np.random.randn(len(objects)) for i in range(len(objects))})
        df.columns = objects
        assert isinstance(df.columns , pd.Index)


        assert isinstance(df.col.bob, LevelWrapper)