Data encoder

def encode(obj):
    """
    Data encoder
    """
    tobj = type(obj)
    if isinstance(obj, Index):
        if isinstance(obj, RangeIndex):
            return {'typ': 'range_index',
                    'klass': obj.__class__.__name__,
                    'name': getattr(obj, 'name', None),
                    'start': getattr(obj, '_start', None),
                    'stop': getattr(obj, '_stop', None),
                    'step': getattr(obj, '_step', None)}
        elif isinstance(obj, PeriodIndex):
            return {'typ': 'period_index',
                    'klass': obj.__class__.__name__,
                    'name': getattr(obj, 'name', None),
                    'freq': getattr(obj, 'freqstr', None),
                    'dtype': obj.dtype.name,
                    'data': convert(obj.asi8),
                    'compress': compressor}
        elif isinstance(obj, DatetimeIndex):
            tz = getattr(obj, 'tz', None)

            # store tz info and data as UTC
            if tz is not None:
                tz = tz.zone
                obj = obj.tz_convert('UTC')
            return {'typ': 'datetime_index',
                    'klass': obj.__class__.__name__,
                    'name': getattr(obj, 'name', None),
                    'dtype': obj.dtype.name,
                    'data': convert(obj.asi8),
                    'freq': getattr(obj, 'freqstr', None),
                    'tz': tz,
                    'compress': compressor}
        elif isinstance(obj, (IntervalIndex, IntervalArray)):
            if isinstance(obj, IntervalIndex):
                typ = 'interval_index'
            else:
                typ = 'interval_array'
            return {'typ': typ,
                    'klass': obj.__class__.__name__,
                    'name': getattr(obj, 'name', None),
                    'left': getattr(obj, 'left', None),
                    'right': getattr(obj, 'right', None),
                    'closed': getattr(obj, 'closed', None)}
        elif isinstance(obj, MultiIndex):
            return {'typ': 'multi_index',
                    'klass': obj.__class__.__name__,
                    'names': getattr(obj, 'names', None),
                    'dtype': obj.dtype.name,
                    'data': convert(obj.values),
                    'compress': compressor}
        else:
            return {'typ': 'index',
                    'klass': obj.__class__.__name__,
                    'name': getattr(obj, 'name', None),
                    'dtype': obj.dtype.name,
                    'data': convert(obj.values),
                    'compress': compressor}

    elif isinstance(obj, Categorical):
        return {'typ': 'category',
                'klass': obj.__class__.__name__,
                'name': getattr(obj, 'name', None),
                'codes': obj.codes,
                'categories': obj.categories,
                'ordered': obj.ordered,
                'compress': compressor}

    elif isinstance(obj, Series):
        if isinstance(obj, SparseSeries):
            raise NotImplementedError(
                'msgpack sparse series is not implemented'
            )
            # d = {'typ': 'sparse_series',
            #     'klass': obj.__class__.__name__,
            #     'dtype': obj.dtype.name,
            #     'index': obj.index,
            #     'sp_index': obj.sp_index,
            #     'sp_values': convert(obj.sp_values),
            #     'compress': compressor}
            # for f in ['name', 'fill_value', 'kind']:
            #    d[f] = getattr(obj, f, None)
            # return d
        else:
            return {'typ': 'series',
                    'klass': obj.__class__.__name__,
                    'name': getattr(obj, 'name', None),
                    'index': obj.index,
                    'dtype': obj.dtype.name,
                    'data': convert(obj.values),
                    'compress': compressor}
    elif issubclass(tobj, NDFrame):
        if isinstance(obj, SparseDataFrame):
            raise NotImplementedError(
                'msgpack sparse frame is not implemented'
            )
            # d = {'typ': 'sparse_dataframe',
            #     'klass': obj.__class__.__name__,
            #     'columns': obj.columns}
            # for f in ['default_fill_value', 'default_kind']:
            #    d[f] = getattr(obj, f, None)
            # d['data'] = dict([(name, ss)
            #                 for name, ss in obj.items()])
            # return d
        else:

            data = obj._data
            if not data.is_consolidated():
                data = data.consolidate()

            # the block manager
            return {'typ': 'block_manager',
                    'klass': obj.__class__.__name__,
                    'axes': data.axes,
                    'blocks': [{'locs': b.mgr_locs.as_array,
                                'values': convert(b.values),
                                'shape': b.values.shape,
                                'dtype': b.dtype.name,
                                'klass': b.__class__.__name__,
                                'compress': compressor} for b in data.blocks]
                    }

    elif isinstance(obj, (datetime, date, np.datetime64, timedelta,
                          np.timedelta64)) or obj is NaT:
        if isinstance(obj, Timestamp):
            tz = obj.tzinfo
            if tz is not None:
                tz = tz.zone
            freq = obj.freq
            if freq is not None:
                freq = freq.freqstr
            return {'typ': 'timestamp',
                    'value': obj.value,
                    'freq': freq,
                    'tz': tz}
        if obj is NaT:
            return {'typ': 'nat'}
        elif isinstance(obj, np.timedelta64):
            return {'typ': 'timedelta64',
                    'data': obj.view('i8')}
        elif isinstance(obj, timedelta):
            return {'typ': 'timedelta',
                    'data': (obj.days, obj.seconds, obj.microseconds)}
        elif isinstance(obj, np.datetime64):
            return {'typ': 'datetime64',
                    'data': str(obj)}
        elif isinstance(obj, datetime):
            return {'typ': 'datetime',
                    'data': obj.isoformat()}
        elif isinstance(obj, date):
            return {'typ': 'date',
                    'data': obj.isoformat()}
        raise Exception(
            "cannot encode this datetimelike object: {obj}".format(obj=obj))
    elif isinstance(obj, Period):
        return {'typ': 'period',
                'ordinal': obj.ordinal,
                'freq': obj.freqstr}
    elif isinstance(obj, Interval):
        return {'typ': 'interval',
                'left': obj.left,
                'right': obj.right,
                'closed': obj.closed}
    elif isinstance(obj, BlockIndex):
        return {'typ': 'block_index',
                'klass': obj.__class__.__name__,
                'blocs': obj.blocs,
                'blengths': obj.blengths,
                'length': obj.length}
    elif isinstance(obj, IntIndex):
        return {'typ': 'int_index',
                'klass': obj.__class__.__name__,
                'indices': obj.indices,
                'length': obj.length}
    elif isinstance(obj, np.ndarray):
        return {'typ': 'ndarray',
                'shape': obj.shape,
                'ndim': obj.ndim,
                'dtype': obj.dtype.name,
                'data': convert(obj),
                'compress': compressor}
    elif isinstance(obj, np.number):
        if np.iscomplexobj(obj):
            return {'typ': 'np_scalar',
                    'sub_typ': 'np_complex',
                    'dtype': obj.dtype.name,
                    'real': obj.real.__repr__(),
                    'imag': obj.imag.__repr__()}
        else:
            return {'typ': 'np_scalar',
                    'dtype': obj.dtype.name,
                    'data': obj.__repr__()}
    elif isinstance(obj, complex):
        return {'typ': 'np_complex',
                'real': obj.real.__repr__(),
                'imag': obj.imag.__repr__()}

    return obj