From 524cd3ee0f3cca650578e871e043698248e9f17a Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Tue, 13 Nov 2012 23:13:23 -0500 Subject: [PATCH 1/3] Panel4D is like a Panel object, but provides 4 dimensions: labels, items, major_axis, minor_axis instead of using a dict of Panels to hold data, the Panel4D provides a convenient represenation in pandas space with named dimensions to allow easy axis swapping and slicing testing ------- tests/test_panel4d.py provides a similar methodology to test_panel.py Panel4D required an overhall of many methods in panel.py and one change in core/index.py (regarding multi-indexing) almost all methods in a Panel are extended to Panel4D (with the exception in that Panel now allows a multi-axis on axis 0) docstrings need to be refreshed a bit and made a bit more general all tests that are not skipped pass (tested with 0.9rc1) join is a work in progress further ------- panelnd.py provides a factory function for creation of generic panel-like ND structures with custom named dimensions (this works, but not fully tested - examples are in the docstring) --- pandas/core/api.py | 1 + pandas/core/indexing.py | 5 + pandas/core/panel.py | 452 +++++++------ pandas/core/panel4d.py | 111 ++++ pandas/core/panelnd.py | 124 ++++ pandas/tests/test_panel.py | 1 + pandas/tests/test_panel4d.py | 1048 ++++++++++++++++++++++++++++++ pandas/tools/tests/test_merge.py | 0 pandas/util/testing.py | 20 + 9 files changed, 1558 insertions(+), 204 deletions(-) mode change 100644 => 100755 pandas/core/api.py create mode 100755 pandas/core/panel4d.py create mode 100644 pandas/core/panelnd.py mode change 100644 => 100755 pandas/tests/test_panel.py create mode 100755 pandas/tests/test_panel4d.py mode change 100644 => 100755 pandas/tools/tests/test_merge.py diff --git a/pandas/core/api.py b/pandas/core/api.py old mode 100644 new mode 100755 index 8cf3b7f4cbda4..6679afcff6ca8 --- a/pandas/core/api.py +++ b/pandas/core/api.py @@ -14,6 +14,7 @@ from pandas.core.series import Series, TimeSeries from pandas.core.frame import DataFrame from pandas.core.panel import Panel +from pandas.core.panel4d import Panel4D from pandas.core.groupby import groupby from pandas.core.reshape import (pivot_simple as pivot, get_dummies, lreshape) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 0cfb4004708fa..fe44cfaa21107 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -240,11 +240,16 @@ def _multi_take_opportunity(self, tup): def _multi_take(self, tup): from pandas.core.frame import DataFrame from pandas.core.panel import Panel + from pandas.core.panel4d import Panel4D if isinstance(self.obj, DataFrame): index = self._convert_for_reindex(tup[0], axis=0) columns = self._convert_for_reindex(tup[1], axis=1) return self.obj.reindex(index=index, columns=columns) + elif isinstance(self.obj, Panel4D): + conv = [self._convert_for_reindex(x, axis=i) + for i, x in enumerate(tup)] + return self.obj.reindex(labels=tup[0],items=tup[1], major=tup[2], minor=tup[3]) elif isinstance(self.obj, Panel): conv = [self._convert_for_reindex(x, axis=i) for i, x in enumerate(tup)] diff --git a/pandas/core/panel.py b/pandas/core/panel.py index 42adf0420db0d..697566e8f2404 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -147,31 +147,45 @@ def f(self, other, axis='items'): class Panel(NDFrame): - _AXIS_NUMBERS = { - 'items': 0, - 'major_axis': 1, - 'minor_axis': 2 - } - - _AXIS_ALIASES = { - 'major': 'major_axis', - 'minor': 'minor_axis' - } - - _AXIS_NAMES = { - 0: 'items', - 1: 'major_axis', - 2: 'minor_axis' + _AXIS_ORDERS = ['items','major_axis','minor_axis'] + _AXIS_NUMBERS = dict([ (a,i) for i, a in enumerate(_AXIS_ORDERS) ]) + _AXIS_ALIASES = { + 'major' : 'major_axis', + 'minor' : 'minor_axis' } + _AXIS_NAMES = dict([ (i,a) for i, a in enumerate(_AXIS_ORDERS) ]) + _AXIS_SLICEMAP = { + 'major_axis' : 'index', + 'minor_axis' : 'columns' + } + _AXIS_LEN = len(_AXIS_ORDERS) # major _default_stat_axis = 1 - _het_axis = 0 + + # info axis + _het_axis = 0 + _info_axis = _AXIS_ORDERS[_het_axis] items = lib.AxisProperty(0) major_axis = lib.AxisProperty(1) minor_axis = lib.AxisProperty(2) + @property + def _constructor(self): + return type(self) + + # return the type of the slice constructor + _constructor_sliced = DataFrame + + def _construct_axes_dict(self, axes = None): + """ return an axes dictionary for myself """ + return dict([ (a,getattr(self,a)) for a in (axes or self._AXIS_ORDERS) ]) + + def _construct_axes_dict_for_slice(self, axes = None): + """ return an axes dictionary for myself """ + return dict([ (self._AXIS_SLICEMAP[a],getattr(self,a)) for a in (axes or self._AXIS_ORDERS) ]) + __add__ = _arith_method(operator.add, '__add__') __sub__ = _arith_method(operator.sub, '__sub__') __truediv__ = _arith_method(operator.truediv, '__truediv__') @@ -209,10 +223,15 @@ def __init__(self, data=None, items=None, major_axis=None, minor_axis=None, copy : boolean, default False Copy data from inputs. Only affects DataFrame / 2d ndarray input """ + self._init_data( data=data, items=items, major_axis=major_axis, minor_axis=minor_axis, + copy=copy, dtype=dtype) + + def _init_data(self, data, copy, dtype, **kwargs): + """ generate ND initialization; axes are passed as required objects to __init__ """ if data is None: data = {} - passed_axes = [items, major_axis, minor_axis] + passed_axes = [ kwargs.get(a) for a in self._AXIS_ORDERS ] axes = None if isinstance(data, BlockManager): if any(x is not None for x in passed_axes): @@ -238,48 +257,47 @@ def _from_axes(cls, data, axes): if isinstance(data, BlockManager): return cls(data) else: - items, major, minor = axes - return cls(data, items=items, major_axis=major, - minor_axis=minor, copy=False) + d = dict([ (i, a) for i, a in zip(cls._AXIS_ORDERS,axes) ]) + d['copy'] = False + return cls(data, **d) def _init_dict(self, data, axes, dtype=None): - items, major, minor = axes + haxis = axes.pop(self._het_axis) - # prefilter if items passed - if items is not None: - items = _ensure_index(items) - data = dict((k, v) for k, v in data.iteritems() if k in items) + # prefilter if haxis passed + if haxis is not None: + haxis = _ensure_index(haxis) + data = dict((k, v) for k, v in data.iteritems() if k in haxis) else: - items = Index(_try_sort(data.keys())) + haxis = Index(_try_sort(data.keys())) for k, v in data.iteritems(): if isinstance(v, dict): - data[k] = DataFrame(v) - - if major is None: - major = _extract_axis(data, axis=0) + data[k] = self._constructor_sliced(v) - if minor is None: - minor = _extract_axis(data, axis=1) + # extract axis for remaining axes & create the slicemap + raxes = [ self._extract_axis(self, data, axis=i) if a is None else a for i, a in enumerate(axes) ] + raxes_sm = self._extract_axes_for_slice(self, raxes) - axes = [items, major, minor] + # shallow copy arrays = [] - - item_shape = len(major), len(minor) - for item in items: - v = values = data.get(item) + reshaped_data = data.copy() + haxis_shape = [ len(a) for a in raxes ] + for h in haxis: + v = values = data.get(h) if v is None: - values = np.empty(item_shape, dtype=dtype) + values = np.empty(haxis_shape, dtype=dtype) values.fill(np.nan) - elif isinstance(v, DataFrame): - v = v.reindex(index=major, columns=minor, copy=False) + elif isinstance(v, self._constructor_sliced): + d = raxes_sm.copy() + d['copy'] = False + v = v.reindex(**d) if dtype is not None: v = v.astype(dtype) values = v.values - arrays.append(values) - return self._init_arrays(arrays, items, axes) + return self._init_arrays(arrays, haxis, [ haxis ] + raxes) def _init_arrays(self, arrays, arr_names, axes): # segregates dtypes and forms blocks matching to columns @@ -289,7 +307,7 @@ def _init_arrays(self, arrays, arr_names, axes): @property def shape(self): - return len(self.items), len(self.major_axis), len(self.minor_axis) + return [ len(getattr(self,a)) for a in self._AXIS_ORDERS ] @classmethod def from_dict(cls, data, intersect=False, orient='items', dtype=None): @@ -326,32 +344,33 @@ def from_dict(cls, data, intersect=False, orient='items', dtype=None): elif orient != 'items': # pragma: no cover raise ValueError('only recognize items or minor for orientation') - data, index, columns = _homogenize_dict(data, intersect=intersect, - dtype=dtype) - items = Index(sorted(data.keys())) - return cls(data, items, index, columns) + d = cls._homogenize_dict(cls, data, intersect=intersect, dtype=dtype) + d[cls._info_axis] = Index(sorted(d['data'].keys())) + return cls(**d) def __getitem__(self, key): - if isinstance(self.items, MultiIndex): + if isinstance(getattr(self,self._info_axis), MultiIndex): return self._getitem_multilevel(key) return super(Panel, self).__getitem__(key) def _getitem_multilevel(self, key): - loc = self.items.get_loc(key) + info = getattr(self,self._info_axis) + loc = info.get_loc(key) if isinstance(loc, (slice, np.ndarray)): - new_index = self.items[loc] + new_index = info[loc] result_index = _maybe_droplevels(new_index, key) - new_values = self.values[loc, :, :] - result = Panel(new_values, - items=result_index, - major_axis=self.major_axis, - minor_axis=self.minor_axis) + slices = [loc] + [slice(None) for x in range(self._AXIS_LEN-1)] + new_values = self.values[slices] + + d = self._construct_axes_dict(self._AXIS_ORDERS[1:]) + d[self._info_axis] = result_index + result = self._constructor(new_values, **d) return result else: return self._get_item_cache(key) def _init_matrix(self, data, axes, dtype=None, copy=False): - values = _prep_ndarray(data, copy=copy) + values = self._prep_ndarray(self, data, copy=copy) if dtype is not None: try: @@ -379,9 +398,9 @@ def __array__(self, dtype=None): return self.values def __array_wrap__(self, result): - return self._constructor(result, items=self.items, - major_axis=self.major_axis, - minor_axis=self.minor_axis, copy=False) + d = self._construct_axes_dict(self._AXIS_ORDERS) + d['copy'] = False + return self._constructor(result, **d) #---------------------------------------------------------------------- # Magic methods @@ -389,37 +408,26 @@ def __array_wrap__(self, result): def __repr__(self): class_name = str(self.__class__) - I, N, K = len(self.items), len(self.major_axis), len(self.minor_axis) - - dims = 'Dimensions: %d (items) x %d (major) x %d (minor)' % (I, N, K) + shape = self.shape + dims = 'Dimensions: %s' % ' x '.join([ "%d (%s)" % (s, a) for a,s in zip(self._AXIS_ORDERS,shape) ]) - if len(self.major_axis) > 0: - major = 'Major axis: %s to %s' % (self.major_axis[0], - self.major_axis[-1]) - else: - major = 'Major axis: None' - - if len(self.minor_axis) > 0: - minor = 'Minor axis: %s to %s' % (self.minor_axis[0], - self.minor_axis[-1]) - else: - minor = 'Minor axis: None' - - if len(self.items) > 0: - items = 'Items: %s to %s' % (self.items[0], self.items[-1]) - else: - items = 'Items: None' - - output = '%s\n%s\n%s\n%s\n%s' % (class_name, dims, items, major, minor) + def axis_pretty(a): + v = getattr(self,a) + if len(v) > 0: + return '%s axis: %s to %s' % (a.capitalize(),v[0],v[-1]) + else: + return '%s axis: None' % a.capitalize() + + output = '\n'.join([class_name, dims] + [axis_pretty(a) for a in self._AXIS_ORDERS]) return output def __iter__(self): - return iter(self.items) + return iter(getattr(self,self._info_axis)) def iteritems(self): - for item in self.items: - yield item, self[item] + for h in getattr(self,self._info_axis): + yield h, self[h] # Name that won't get automatically converted to items by 2to3. items is # already in use for the first axis. @@ -443,10 +451,6 @@ def _get_plane_axes(self, axis): return index, columns - @property - def _constructor(self): - return type(self) - # Fancy indexing _ix = None @@ -516,7 +520,7 @@ def _get_values(self): #---------------------------------------------------------------------- # Getting and setting elements - def get_value(self, item, major, minor): + def get_value(self, *args): """ Quickly retrieve single value at (item, major, minor) location @@ -530,11 +534,14 @@ def get_value(self, item, major, minor): ------- value : scalar value """ + # require an arg for each axis + assert(len(args) == self._AXIS_LEN) + # hm, two layers to the onion - frame = self._get_item_cache(item) - return frame.get_value(major, minor) + frame = self._get_item_cache(args[0]) + return frame.get_value(*args[1:]) - def set_value(self, item, major, minor, value): + def set_value(self, *args): """ Quickly set single value at (item, major, minor) location @@ -551,30 +558,35 @@ def set_value(self, item, major, minor, value): If label combo is contained, will be reference to calling Panel, otherwise a new object """ + # require an arg for each axis and the value + assert(len(args) == self._AXIS_LEN+1) + try: - frame = self._get_item_cache(item) - frame.set_value(major, minor, value) + frame = self._get_item_cache(args[0]) + frame.set_value(*args[1:]) return self except KeyError: - ax1, ax2, ax3 = self._expand_axes((item, major, minor)) - result = self.reindex(items=ax1, major=ax2, minor=ax3, copy=False) + axes = self._expand_axes(args) + d = dict([ (a,ax) for a,ax in zip(self._AXIS_ORDERS,axes) ]) + d['copy'] = False + result = self.reindex(**d) - likely_dtype = com._infer_dtype(value) - made_bigger = not np.array_equal(ax1, self.items) + likely_dtype = com._infer_dtype(args[-1]) + made_bigger = not np.array_equal(axes[0], getattr(self,self._info_axis)) # how to make this logic simpler? if made_bigger: - com._possibly_cast_item(result, item, likely_dtype) + com._possibly_cast_item(result, args[0], likely_dtype) - return result.set_value(item, major, minor, value) + return result.set_value(*args) def _box_item_values(self, key, values): - return DataFrame(values, index=self.major_axis, - columns=self.minor_axis) + d = self._construct_axes_dict_for_slice(self._AXIS_ORDERS[1:]) + return self._constructor_sliced(values, **d) def __getattr__(self, name): """After regular attribute access, try looking up the name of an item. This allows simpler access to items for interactive use.""" - if name in self.items: + if name in getattr(self,self._info_axis): return self[name] raise AttributeError("'%s' object has no attribute '%s'" % (type(self).__name__, name)) @@ -584,22 +596,21 @@ def _slice(self, slobj, axis=0): return self._constructor(new_data) def __setitem__(self, key, value): - _, N, K = self.shape - if isinstance(value, DataFrame): - value = value.reindex(index=self.major_axis, - columns=self.minor_axis) + shape = tuple(self.shape) + if isinstance(value, self._constructor_sliced): + value = value.reindex(**self._construct_axes_dict_for_slice(self._AXIS_ORDERS[1:])) mat = value.values elif isinstance(value, np.ndarray): - assert(value.shape == (N, K)) + assert(value.shape == shape[1:]) mat = np.asarray(value) elif np.isscalar(value): dtype = _infer_dtype(value) - mat = np.empty((N, K), dtype=dtype) + mat = np.empty(shape[1:], dtype=dtype) mat.fill(value) else: raise TypeError('Cannot set item of type: %s' % str(type(value))) - mat = mat.reshape((1, N, K)) + mat = mat.reshape(tuple([1]) + shape[1:]) NDFrame._set_item(self, key, mat) def pop(self, item): @@ -660,11 +671,11 @@ def conform(self, frame, axis='items'): ------- DataFrame """ - index, columns = self._get_plane_axes(axis) - return frame.reindex(index=index, columns=columns) + axes = self._get_plane_axes(axis) + return frame.reindex(**self._extract_axes_for_slice(self, axes)) - def reindex(self, major=None, items=None, minor=None, method=None, - major_axis=None, minor_axis=None, copy=True): + def reindex(self, major=None, minor=None, method=None, + major_axis=None, minor_axis=None, copy=True, **kwargs): """ Conform panel to new axis or axes @@ -691,19 +702,24 @@ def reindex(self, major=None, items=None, minor=None, method=None, major = _mut_exclusive(major, major_axis) minor = _mut_exclusive(minor, minor_axis) + al = self._AXIS_LEN - if (method is None and not self._is_mixed_type and - com._count_not_none(items, major, minor) == 3): - return self._reindex_multi(items, major, minor) + # only allowing multi-index on Panel (and not > dims) + if (method is None and not self._is_mixed_type and al <= 3): + items = kwargs.get('items') + if com._count_not_none(items, major, minor) == 3: + return self._reindex_multi(items, major, minor) if major is not None: - result = result._reindex_axis(major, method, 1, copy) + result = result._reindex_axis(major, method, al-2, copy) if minor is not None: - result = result._reindex_axis(minor, method, 2, copy) + result = result._reindex_axis(minor, method, al-1, copy) - if items is not None: - result = result._reindex_axis(items, method, 0, copy) + for i, a in enumerate(self._AXIS_ORDERS[0:al-2]): + a = kwargs.get(a) + if a is not None: + result = result._reindex_axis(a, method, i, copy) if result is self and copy: raise ValueError('Must specify at least one axis') @@ -768,8 +784,7 @@ def reindex_axis(self, labels, axis=0, method=None, level=None, copy=True): return self._reindex_axis(labels, method, axis, copy) def reindex_like(self, other, method=None): - """ - Reindex Panel to match indices of another Panel + """ return an object with matching indicies to myself Parameters ---------- @@ -780,9 +795,9 @@ def reindex_like(self, other, method=None): ------- reindexed : Panel """ - # todo: object columns - return self.reindex(major=other.major_axis, items=other.items, - minor=other.minor_axis, method=method) + d = other._construct_axes_dict() + d['method'] = method + return self.reindex(**d) def dropna(self, axis=0, how='any'): """ @@ -826,8 +841,8 @@ def _combine(self, other, func, axis=0): return self._combine_frame(other, func, axis=axis) elif np.isscalar(other): new_values = func(self.values, other) - return self._constructor(new_values, self.items, self.major_axis, - self.minor_axis) + d = self._construct_axes_dict() + return self._constructor(new_values, **d) def __neg__(self): return -1 * self @@ -924,7 +939,7 @@ def major_xs(self, key, copy=True): y : DataFrame index -> minor axis, columns -> items """ - return self.xs(key, axis=1, copy=copy) + return self.xs(key, axis=self._AXIS_LEN-2, copy=copy) def minor_xs(self, key, copy=True): """ @@ -942,7 +957,7 @@ def minor_xs(self, key, copy=True): y : DataFrame index -> major axis, columns -> items """ - return self.xs(key, axis=2, copy=copy) + return self.xs(key, axis=self._AXIS_LEN-1, copy=copy) def xs(self, key, axis=1, copy=True): """ @@ -956,7 +971,7 @@ def xs(self, key, axis=1, copy=True): Returns ------- - y : DataFrame + y : ndim(self)-1 """ if axis == 0: data = self[key] @@ -967,7 +982,7 @@ def xs(self, key, axis=1, copy=True): self._consolidate_inplace() axis_number = self._get_axis_number(axis) new_data = self._data.xs(key, axis=axis_number, copy=copy) - return DataFrame(new_data) + return self._constructor_sliced(new_data) def _ixs(self, i, axis=0): # for compatibility with .ix indexing @@ -1010,15 +1025,14 @@ def swapaxes(self, axis1='major', axis2='minor', copy=True): mapping = {i: j, j: i} new_axes = (self._get_axis(mapping.get(k, k)) - for k in range(3)) + for k in range(self._AXIS_LEN)) new_values = self.values.swapaxes(i, j) if copy: new_values = new_values.copy() return self._constructor(new_values, *new_axes) - def transpose(self, items='items', major='major', minor='minor', - copy=False): + def transpose(self, *args, **kwargs): """ Permute the dimensions of the Panel @@ -1040,16 +1054,27 @@ def transpose(self, items='items', major='major', minor='minor', ------- y : Panel (new object) """ - i, j, k = [self._get_axis_number(x) for x in [items, major, minor]] - - if i == j or i == k or j == k: - raise ValueError('Must specify 3 unique axes') - new_axes = [self._get_axis(x) for x in [i, j, k]] - new_values = self.values.transpose((i, j, k)) - if copy: + # construct the args + args = list(args) + for a in self._AXIS_ORDERS: + if not a in kwargs: + try: + kwargs[a] = args.pop(0) + except (IndexError): + raise ValueError("not enough arguments specified to transpose!") + + axes = [self._get_axis_number(kwargs[a]) for a in self._AXIS_ORDERS] + + # we must have unique axes + if len(axes) != len(set(axes)): + raise ValueError('Must specify %s unique axes' % self._AXIS_LEN) + + new_axes = dict([ (a,self._get_axis(x)) for a, x in zip(self._AXIS_ORDERS,axes)]) + new_values = self.values.transpose(tuple(axes)) + if kwargs.get('copy') or (len(args) and args[-1]): new_values = new_values.copy() - return self._constructor(new_values, *new_axes) + return self._constructor(new_values, **new_axes) def to_frame(self, filter_observations=True): """ @@ -1140,20 +1165,24 @@ def _reduce(self, op, axis=0, skipna=True): result = f(self.values) - index, columns = self._get_plane_axes(axis_name) - if axis_name != 'items': + axes = self._get_plane_axes(axis_name) + if result.ndim == 2 and axis_name != self._info_axis: result = result.T - return DataFrame(result, index=index, columns=columns) + return self._constructor_sliced(result, **self._extract_axes_for_slice(self, axes)) def _wrap_result(self, result, axis): axis = self._get_axis_name(axis) - index, columns = self._get_plane_axes(axis) - - if axis != 'items': + axes = self._get_plane_axes(axis) + if result.ndim == 2 and axis != self._info_axis: result = result.T - return DataFrame(result, index=index, columns=columns) + # do we have reduced dimensionalility? + if self.ndim == result.ndim: + return self._constructor(result, **self._construct_axes_dict()) + elif self.ndim == result.ndim+1: + return self._constructor_sliced(result, **self._extract_axes_for_slice(self, axes)) + raise PandasError("invalid _wrap_result [self->%s] [result->%s]" % (self.ndim,result.ndim)) def count(self, axis='major'): """ @@ -1381,71 +1410,83 @@ def _get_join_index(self, other, how): join_minor = self.minor_axis.union(other.minor_axis) return join_major, join_minor -WidePanel = Panel -LongPanel = DataFrame - - -def _prep_ndarray(values, copy=True): - if not isinstance(values, np.ndarray): - values = np.asarray(values) - # NumPy strings are a pain, convert to object - if issubclass(values.dtype.type, basestring): - values = np.array(values, dtype=object, copy=True) - else: - if copy: - values = values.copy() - assert(values.ndim == 3) - return values - - -def _homogenize_dict(frames, intersect=True, dtype=None): - """ - Conform set of DataFrame-like objects to either an intersection - of indices / columns or a union. - - Parameters - ---------- - frames : dict - intersect : boolean, default True + # miscellaneous data creation + @staticmethod + def _extract_axes(self, data, axes, **kwargs): + """ return a list of the axis indicies """ + return [ self._extract_axis(self, data, axis=i, **kwargs) for i, a in enumerate(axes) ] + + @staticmethod + def _extract_axes_for_slice(self, axes): + """ return the slice dictionary for these axes """ + return dict([ (self._AXIS_SLICEMAP[i], a) for i, a in zip(self._AXIS_ORDERS[self._AXIS_LEN-len(axes):],axes) ]) + + @staticmethod + def _prep_ndarray(self, values, copy=True): + if not isinstance(values, np.ndarray): + values = np.asarray(values) + # NumPy strings are a pain, convert to object + if issubclass(values.dtype.type, basestring): + values = np.array(values, dtype=object, copy=True) + else: + if copy: + values = values.copy() + assert(values.ndim == self._AXIS_LEN) + return values - Returns - ------- - dict of aligned frames, index, columns - """ - result = {} + @staticmethod + def _homogenize_dict(self, frames, intersect=True, dtype=None): + """ + Conform set of _constructor_sliced-like objects to either an intersection + of indices / columns or a union. + + Parameters + ---------- + frames : dict + intersect : boolean, default True + + Returns + ------- + dict of aligned results & indicies + """ + result = {} - adj_frames = {} - for k, v in frames.iteritems(): - if isinstance(v, dict): - adj_frames[k] = DataFrame(v) - else: - adj_frames[k] = v + adj_frames = {} + for k, v in frames.iteritems(): + if isinstance(v, dict): + adj_frames[k] = self._constructor_sliced(v) + else: + adj_frames[k] = v - index = _extract_axis(adj_frames, axis=0, intersect=intersect) - columns = _extract_axis(adj_frames, axis=1, intersect=intersect) + axes = self._AXIS_ORDERS[1:] + axes_dict = dict([ (a,ax) for a,ax in zip(axes,self._extract_axes(self, adj_frames, axes, intersect=intersect)) ]) - for key, frame in adj_frames.iteritems(): - if frame is not None: - result[key] = frame.reindex(index=index, columns=columns, - copy=False) - else: - result[key] = None + reindex_dict = dict([ (self._AXIS_SLICEMAP[a],axes_dict[a]) for a in axes ]) + reindex_dict['copy'] = False + for key, frame in adj_frames.iteritems(): + if frame is not None: + result[key] = frame.reindex(**reindex_dict) + else: + result[key] = None - return result, index, columns + axes_dict['data'] = result + return axes_dict + @staticmethod + def _extract_axis(self, data, axis=0, intersect=False): -def _extract_axis(data, axis=0, intersect=False): - if len(data) == 0: - index = Index([]) - elif len(data) > 0: - raw_lengths = [] - indexes = [] index = None + if len(data) == 0: + index = Index([]) + elif len(data) > 0: + raw_lengths = [] + indexes = [] + have_raw_arrays = False have_frames = False for v in data.values(): - if isinstance(v, DataFrame): + if isinstance(v, self._constructor_sliced): have_frames = True indexes.append(v._get_axis(axis)) elif v is not None: @@ -1459,7 +1500,7 @@ def _extract_axis(data, axis=0, intersect=False): lengths = list(set(raw_lengths)) if len(lengths) > 1: raise ValueError('ndarrays must match shape on axis %d' % axis) - + if have_frames: assert(lengths[0] == len(index)) else: @@ -1468,7 +1509,10 @@ def _extract_axis(data, axis=0, intersect=False): if index is None: index = Index([]) - return _ensure_index(index) + return _ensure_index(index) + +WidePanel = Panel +LongPanel = DataFrame def _monotonic(arr): diff --git a/pandas/core/panel4d.py b/pandas/core/panel4d.py new file mode 100755 index 0000000000000..09858c0ff59b6 --- /dev/null +++ b/pandas/core/panel4d.py @@ -0,0 +1,111 @@ +""" Panel4D: a 4-d dict like collection of panels """ + +from pandas.core.panel import Panel +import pandas.lib as lib + +class Panel4D(Panel): + _AXIS_ORDERS = ['labels','items','major_axis','minor_axis'] + _AXIS_NUMBERS = dict([ (a,i) for i, a in enumerate(_AXIS_ORDERS) ]) + _AXIS_ALIASES = { + 'major' : 'major_axis', + 'minor' : 'minor_axis' + } + _AXIS_NAMES = dict([ (i,a) for i, a in enumerate(_AXIS_ORDERS) ]) + _AXIS_SLICEMAP = { + 'items' : 'items', + 'major_axis' : 'major_axis', + 'minor_axis' : 'minor_axis' + } + _AXIS_LEN = len(_AXIS_ORDERS) + + # major + _default_stat_axis = 2 + + # info axis + _het_axis = 0 + _info_axis = _AXIS_ORDERS[_het_axis] + + labels = lib.AxisProperty(0) + items = lib.AxisProperty(1) + major_axis = lib.AxisProperty(2) + minor_axis = lib.AxisProperty(3) + + _constructor_sliced = Panel + + def __init__(self, data=None, labels=None, items=None, major_axis=None, minor_axis=None, copy=False, dtype=None): + """ + Represents a 4 dimensonal structured + + Parameters + ---------- + data : ndarray (labels x items x major x minor), or dict of Panels + + labels : Index or array-like : axis=0 + items : Index or array-like : axis=1 + major_axis : Index or array-like: axis=2 + minor_axis : Index or array-like: axis=3 + + dtype : dtype, default None + Data type to force, otherwise infer + copy : boolean, default False + Copy data from inputs. Only affects DataFrame / 2d ndarray input + """ + self._init_data( data=data, labels=labels, items=items, major_axis=major_axis, minor_axis=minor_axis, + copy=copy, dtype=dtype) + + def _get_plane_axes(self, axis): + axis = self._get_axis_name(axis) + + if axis == 'major_axis': + items = self.labels + major = self.items + minor = self.minor_axis + elif axis == 'minor_axis': + items = self.labels + major = self.items + minor = self.major_axis + elif axis == 'items': + items = self.labels + major = self.major_axis + minor = self.minor_axis + elif axis == 'labels': + items = self.items + major = self.major_axis + minor = self.minor_axis + + return items, major, minor + + def _combine(self, other, func, axis=0): + if isinstance(other, Panel4D): + return self._combine_panel4d(other, func) + return super(Panel4D, self)._combine(other, func, axis=axis) + + def _combine_panel4d(self, other, func): + labels = self.labels + other.labels + items = self.items + other.items + major = self.major_axis + other.major_axis + minor = self.minor_axis + other.minor_axis + + # could check that everything's the same size, but forget it + this = self.reindex(labels=labels, items=items, major=major, minor=minor) + other = other.reindex(labels=labels, items=items, major=major, minor=minor) + + result_values = func(this.values, other.values) + + return self._constructor(result_values, labels, items, major, minor) + + def join(self, other, how='left', lsuffix='', rsuffix=''): + if isinstance(other, Panel4D): + join_major, join_minor = self._get_join_index(other, how) + this = self.reindex(major=join_major, minor=join_minor) + other = other.reindex(major=join_major, minor=join_minor) + merged_data = this._data.merge(other._data, lsuffix, rsuffix) + return self._constructor(merged_data) + return super(Panel4D, self).join(other=other,how=how,lsuffix=lsuffix,rsuffix=rsuffix) + + ### remove operations #### + def to_frame(self, *args, **kwargs): + raise NotImplementedError + def to_excel(self, *args, **kwargs): + raise NotImplementedError + diff --git a/pandas/core/panelnd.py b/pandas/core/panelnd.py new file mode 100644 index 0000000000000..3173b2d1c14e5 --- /dev/null +++ b/pandas/core/panelnd.py @@ -0,0 +1,124 @@ +""" Factory methods to create N-D panels """ + +import sys +sys.path.insert(0,'/home/jreback/pandas') +import pandas +from pandas.core.panel import Panel +import pandas.lib as lib + +def create_nd_panel_factory(klass_name, axis_orders, axis_slices, slicer, axis_aliases = None, stat_axis = 2): + """ manufacture a n-d class: + + parameters + ---------- + klass_name : the klass name + axis_orders : the names of the axes in order (highest to lowest) + axis_slices : a dictionary that defines how the axes map to the sliced axis + slicer : the class representing a slice of this panel + axis_aliases: a dictionary defining aliases for various axes + default = { major : major_axis, minor : minor_axis } + stat_axis : the default statistic axis + default = 2 + het_axis : the info axis + + + returns + ------- + a class object reprsenting this panel + + + """ + + # build the klass + klass = type(klass_name, (slicer,),{}) + + # add the class variables + klass._AXIS_ORDERS = axis_orders + klass._AXIS_NUMBERS = dict([ (a,i) for i, a in enumerate(axis_orders) ]) + klass._AXIS_ALIASES = axis_aliases or dict() + klass._AXIS_NAMES = dict([ (i,a) for i, a in enumerate(axis_orders) ]) + klass._AXIS_SLICEMAP = axis_slices + klass._AXIS_LEN = len(axis_orders) + klass._default_stat_axis = stat_axis + klass._het_axis = 0 + klass._info_axis = axis_orders[klass._het_axis] + klass._constructor_sliced = slicer + + # add the axes + for i, a in enumerate(axis_orders): + setattr(klass,a,lib.AxisProperty(i)) + + # define the __init__ + def __init__(self, *args, **kwargs): + if not (kwargs.get('data') or len(args)): + raise Exception("must supply at least a data argument to [%s]" % klass_name) + if 'copy' not in kwargs: + kwargs['copy'] = False + if 'dtype' not in kwargs: + kwargs['dtype'] = None + self._init_data( *args, **kwargs) + klass.__init__ = __init__ + + # define _get_place_axes + def _get_plane_axes(self, axis): + axis = self._get_axis_name(axis) + index = self._AXIS_ORDERS.index(axis) + + planes = [] + if index: + planes.extend(self._AXIS_ORDERS[0:index]) + if index != self._AXIS_LEN: + planes.extend(self._AXIS_ORDERS[index:]) + + return planes + klass._get_plane_axes + + # remove these operations + def to_frame(self, *args, **kwargs): + raise NotImplementedError + klass.to_frame = to_frame + def to_excel(self, *args, **kwargs): + raise NotImplementedError + klass.to_excel = to_excel + + return klass + + +if __name__ == '__main__': + + # create a sample + from pandas.util import testing + print pandas.__version__ + + # create a 4D + Panel4DNew = create_nd_panel_factory( + klass_name = 'Panel4DNew', + axis_orders = ['labels1','items1','major_axis','minor_axis'], + axis_slices = { 'items1' : 'items', 'major_axis' : 'major_axis', 'minor_axis' : 'minor_axis' }, + slicer = Panel, + axis_aliases = { 'major' : 'major_axis', 'minor' : 'minor_axis' }, + stat_axis = 2) + + p4dn = Panel4DNew(dict(L1 = testing.makePanel(), L2 = testing.makePanel())) + print "creating a 4-D Panel" + print p4dn, "\n" + + # create a 5D + Panel5DNew = create_nd_panel_factory( + klass_name = 'Panel5DNew', + axis_orders = [ 'cool1', 'labels1','items1','major_axis','minor_axis'], + axis_slices = { 'labels1' : 'labels1', 'items1' : 'items', 'major_axis' : 'major_axis', 'minor_axis' : 'minor_axis' }, + slicer = Panel4DNew, + axis_aliases = { 'major' : 'major_axis', 'minor' : 'minor_axis' }, + stat_axis = 2) + + p5dn = Panel5DNew(dict(C1 = p4dn)) + + print "creating a 5-D Panel" + print p5dn, "\n" + + print "Slicing p5dn" + print p5dn.ix['C1',:,:,0:3,:], "\n" + + print "Transposing p5dn" + print p5dn.transpose(1,2,3,4,0), "\n" diff --git a/pandas/tests/test_panel.py b/pandas/tests/test_panel.py old mode 100644 new mode 100755 index 82c6ea65d133a..d36e35e0e20fd --- a/pandas/tests/test_panel.py +++ b/pandas/tests/test_panel.py @@ -981,6 +981,7 @@ def test_swapaxes(self): self.assertRaises(Exception, self.panel.swapaxes, 'items', 'items') def test_transpose(self): + result = self.panel.transpose('minor', 'major', 'items') expected = self.panel.swapaxes('items', 'minor') assert_panel_equal(result, expected) diff --git a/pandas/tests/test_panel4d.py b/pandas/tests/test_panel4d.py new file mode 100755 index 0000000000000..4058739ba772b --- /dev/null +++ b/pandas/tests/test_panel4d.py @@ -0,0 +1,1048 @@ +from datetime import datetime +import os +import operator +import unittest +import nose + +import numpy as np + +from pandas import DataFrame, Index, isnull, notnull, pivot, MultiIndex +from pandas.core.datetools import bday +from pandas.core.frame import group_agg +from pandas.core.panel import Panel +from pandas.core.panel4d import Panel4D +from pandas.core.series import remove_na +import pandas.core.common as com +import pandas.core.panel as panelmod +from pandas.util import py3compat +from pandas.io.parsers import (ExcelFile, ExcelWriter) + +from pandas.util.testing import (assert_panel_equal, + assert_panel4d_equal, + assert_frame_equal, + assert_series_equal, + assert_almost_equal) +import pandas.util.testing as tm + +def add_nans(panel4d): + for l, label in enumerate(panel4d.labels): + panel = panel4d[label] + tm.add_nans(panel) + +class SafeForLongAndSparse(object): + + def test_repr(self): + foo = repr(self.panel4d) + + def test_iter(self): + tm.equalContents(list(self.panel4d), self.panel4d.labels) + + def test_count(self): + f = lambda s: notnull(s).sum() + self._check_stat_op('count', f, obj=self.panel4d, has_skipna=False) + + def test_sum(self): + self._check_stat_op('sum', np.sum) + + def test_mean(self): + self._check_stat_op('mean', np.mean) + + def test_prod(self): + self._check_stat_op('prod', np.prod) + + def test_median(self): + def wrapper(x): + if isnull(x).any(): + return np.nan + return np.median(x) + + self._check_stat_op('median', wrapper) + + def test_min(self): + self._check_stat_op('min', np.min) + + def test_max(self): + self._check_stat_op('max', np.max) + + def test_skew(self): + from scipy.stats import skew + def this_skew(x): + if len(x) < 3: + return np.nan + return skew(x, bias=False) + self._check_stat_op('skew', this_skew) + + # def test_mad(self): + # f = lambda x: np.abs(x - x.mean()).mean() + # self._check_stat_op('mad', f) + + def test_var(self): + def alt(x): + if len(x) < 2: + return np.nan + return np.var(x, ddof=1) + self._check_stat_op('var', alt) + + def test_std(self): + def alt(x): + if len(x) < 2: + return np.nan + return np.std(x, ddof=1) + self._check_stat_op('std', alt) + + # def test_skew(self): + # from scipy.stats import skew + + # def alt(x): + # if len(x) < 3: + # return np.nan + # return skew(x, bias=False) + + # self._check_stat_op('skew', alt) + + def _check_stat_op(self, name, alternative, obj=None, has_skipna=True): + if obj is None: + obj = self.panel4d + + # # set some NAs + # obj.ix[5:10] = np.nan + # obj.ix[15:20, -2:] = np.nan + + f = getattr(obj, name) + + if has_skipna: + def skipna_wrapper(x): + nona = remove_na(x) + if len(nona) == 0: + return np.nan + return alternative(nona) + + def wrapper(x): + return alternative(np.asarray(x)) + + for i in range(obj.ndim): + result = f(axis=i, skipna=False) + assert_panel_equal(result, obj.apply(wrapper, axis=i)) + else: + skipna_wrapper = alternative + wrapper = alternative + + for i in range(obj.ndim): + result = f(axis=i) + assert_panel_equal(result, obj.apply(skipna_wrapper, axis=i)) + + self.assertRaises(Exception, f, axis=obj.ndim) + +class SafeForSparse(object): + + @classmethod + def assert_panel_equal(cls, x, y): + assert_panel_equal(x, y) + + @classmethod + def assert_panel4d_equal(cls, x, y): + assert_panel4d_equal(x, y) + + def test_get_axis(self): + assert(self.panel4d._get_axis(0) is self.panel4d.labels) + assert(self.panel4d._get_axis(1) is self.panel4d.items) + assert(self.panel4d._get_axis(2) is self.panel4d.major_axis) + assert(self.panel4d._get_axis(3) is self.panel4d.minor_axis) + + def test_set_axis(self): + new_labels = Index(np.arange(len(self.panel4d.labels))) + new_items = Index(np.arange(len(self.panel4d.items))) + new_major = Index(np.arange(len(self.panel4d.major_axis))) + new_minor = Index(np.arange(len(self.panel4d.minor_axis))) + + # ensure propagate to potentially prior-cached items too + label = self.panel4d['l1'] + self.panel4d.labels = new_labels + + if hasattr(self.panel4d, '_item_cache'): + self.assert_('l1' not in self.panel4d._item_cache) + self.assert_(self.panel4d.labels is new_labels) + + self.panel4d.major_axis = new_major + self.assert_(self.panel4d[0].major_axis is new_major) + self.assert_(self.panel4d.major_axis is new_major) + + self.panel4d.minor_axis = new_minor + self.assert_(self.panel4d[0].minor_axis is new_minor) + self.assert_(self.panel4d.minor_axis is new_minor) + + def test_get_axis_number(self): + self.assertEqual(self.panel4d._get_axis_number('labels'), 0) + self.assertEqual(self.panel4d._get_axis_number('items'), 1) + self.assertEqual(self.panel4d._get_axis_number('major'), 2) + self.assertEqual(self.panel4d._get_axis_number('minor'), 3) + + def test_get_axis_name(self): + self.assertEqual(self.panel4d._get_axis_name(0), 'labels') + self.assertEqual(self.panel4d._get_axis_name(1), 'items') + self.assertEqual(self.panel4d._get_axis_name(2), 'major_axis') + self.assertEqual(self.panel4d._get_axis_name(3), 'minor_axis') + + #def test_get_plane_axes(self): + # # what to do here? + + # index, columns = self.panel._get_plane_axes('items') + # index, columns = self.panel._get_plane_axes('major_axis') + # index, columns = self.panel._get_plane_axes('minor_axis') + # index, columns = self.panel._get_plane_axes(0) + + def test_truncate(self): + raise nose.SkipTest + + #dates = self.panel.major_axis + #start, end = dates[1], dates[5] + + #trunced = self.panel.truncate(start, end, axis='major') + #expected = self.panel['ItemA'].truncate(start, end) + + #assert_frame_equal(trunced['ItemA'], expected) + + #trunced = self.panel.truncate(before=start, axis='major') + #expected = self.panel['ItemA'].truncate(before=start) + + #assert_frame_equal(trunced['ItemA'], expected) + + #trunced = self.panel.truncate(after=end, axis='major') + #expected = self.panel['ItemA'].truncate(after=end) + + #assert_frame_equal(trunced['ItemA'], expected) + + # XXX test other axes + + def test_arith(self): + self._test_op(self.panel4d, operator.add) + self._test_op(self.panel4d, operator.sub) + self._test_op(self.panel4d, operator.mul) + self._test_op(self.panel4d, operator.truediv) + self._test_op(self.panel4d, operator.floordiv) + self._test_op(self.panel4d, operator.pow) + + self._test_op(self.panel4d, lambda x, y: y + x) + self._test_op(self.panel4d, lambda x, y: y - x) + self._test_op(self.panel4d, lambda x, y: y * x) + self._test_op(self.panel4d, lambda x, y: y / x) + self._test_op(self.panel4d, lambda x, y: y ** x) + + self.assertRaises(Exception, self.panel4d.__add__, self.panel4d['l1']) + + @staticmethod + def _test_op(panel4d, op): + result = op(panel4d, 1) + assert_panel_equal(result['l1'], op(panel4d['l1'], 1)) + + def test_keys(self): + tm.equalContents(self.panel4d.keys(), self.panel4d.labels) + + def test_iteritems(self): + """Test panel4d.iteritems(), aka panel4d.iterkv()""" + # just test that it works + for k, v in self.panel4d.iterkv(): + pass + + self.assertEqual(len(list(self.panel4d.iterkv())), + len(self.panel4d.labels)) + + def test_combinePanel4d(self): + result = self.panel4d.add(self.panel4d) + self.assert_panel4d_equal(result, self.panel4d * 2) + + def test_neg(self): + self.assert_panel4d_equal(-self.panel4d, self.panel4d * -1) + + def test_select(self): + p = self.panel4d + + # select labels + result = p.select(lambda x: x in ('l1', 'l3'), axis='labels') + expected = p.reindex(labels=['l1','l3']) + self.assert_panel4d_equal(result, expected) + + # select items + result = p.select(lambda x: x in ('ItemA', 'ItemC'), axis='items') + expected = p.reindex(items=['ItemA', 'ItemC']) + self.assert_panel4d_equal(result, expected) + + # select major_axis + result = p.select(lambda x: x >= datetime(2000, 1, 15), axis='major') + new_major = p.major_axis[p.major_axis >= datetime(2000, 1, 15)] + expected = p.reindex(major=new_major) + self.assert_panel4d_equal(result, expected) + + # select minor_axis + result = p.select(lambda x: x in ('D', 'A'), axis=3) + expected = p.reindex(minor=['A', 'D']) + self.assert_panel4d_equal(result, expected) + + # corner case, empty thing + result = p.select(lambda x: x in ('foo',), axis='items') + self.assert_panel4d_equal(result, p.reindex(items=[])) + + def test_get_value(self): + for item in self.panel.items: + for mjr in self.panel.major_axis[::2]: + for mnr in self.panel.minor_axis: + result = self.panel.get_value(item, mjr, mnr) + expected = self.panel[item][mnr][mjr] + assert_almost_equal(result, expected) + + def test_abs(self): + result = self.panel4d.abs() + expected = np.abs(self.panel4d) + self.assert_panel4d_equal(result, expected) + + p = self.panel4d['l1'] + result = p.abs() + expected = np.abs(p) + assert_panel_equal(result, expected) + + df = p['ItemA'] + result = df.abs() + expected = np.abs(df) + assert_frame_equal(result, expected) + +class CheckIndexing(object): + + + def test_getitem(self): + self.assertRaises(Exception, self.panel4d.__getitem__, 'ItemQ') + + def test_delitem_and_pop(self): + expected = self.panel4d['l2'] + result = self.panel4d.pop('l2') + assert_panel_equal(expected, result) + self.assert_('l2' not in self.panel4d.labels) + + del self.panel4d['l3'] + self.assert_('l3' not in self.panel4d.labels) + self.assertRaises(Exception, self.panel4d.__delitem__, 'l3') + + values = np.empty((4, 4, 4, 4)) + values[0] = 0 + values[1] = 1 + values[2] = 2 + values[3] = 3 + + panel4d = Panel4D(values, range(4), range(4), range(4), range(4)) + + # did we delete the right row? + + panel4dc = panel4d.copy() + del panel4dc[0] + assert_panel_equal(panel4dc[1], panel4d[1]) + assert_panel_equal(panel4dc[2], panel4d[2]) + assert_panel_equal(panel4dc[3], panel4d[3]) + + panel4dc = panel4d.copy() + del panel4dc[1] + assert_panel_equal(panel4dc[0], panel4d[0]) + assert_panel_equal(panel4dc[2], panel4d[2]) + assert_panel_equal(panel4dc[3], panel4d[3]) + + panel4dc = panel4d.copy() + del panel4dc[2] + assert_panel_equal(panel4dc[1], panel4d[1]) + assert_panel_equal(panel4dc[0], panel4d[0]) + assert_panel_equal(panel4dc[3], panel4d[3]) + + panel4dc = panel4d.copy() + del panel4dc[3] + assert_panel_equal(panel4dc[1], panel4d[1]) + assert_panel_equal(panel4dc[2], panel4d[2]) + assert_panel_equal(panel4dc[0], panel4d[0]) + + def test_setitem(self): + ## LongPanel with one item + #lp = self.panel.filter(['ItemA', 'ItemB']).to_frame() + #self.assertRaises(Exception, self.panel.__setitem__, + # 'ItemE', lp) + + # Panel + p = Panel(dict(ItemA = self.panel4d['l1']['ItemA'][2:].filter(items=['A', 'B']))) + self.panel4d['l4'] = p + self.panel4d['l5'] = p + + p2 = self.panel4d['l4'] + + assert_panel_equal(p, p2.reindex(items = p.items, + major_axis = p.major_axis, + minor_axis = p.minor_axis)) + + # scalar + self.panel4d['lG'] = 1 + self.panel4d['lE'] = True + self.assert_(self.panel4d['lG'].values.dtype == np.int64) + self.assert_(self.panel4d['lE'].values.dtype == np.bool_) + + # object dtype + self.panel4d['lQ'] = 'foo' + self.assert_(self.panel4d['lQ'].values.dtype == np.object_) + + # boolean dtype + self.panel4d['lP'] = self.panel4d['l1'] > 0 + self.assert_(self.panel4d['lP'].values.dtype == np.bool_) + + def test_setitem_ndarray(self): + raise nose.SkipTest + # from pandas import DateRange, datetools + + # timeidx = DateRange(start=datetime(2009,1,1), + # end=datetime(2009,12,31), + # offset=datetools.MonthEnd()) + # lons_coarse = np.linspace(-177.5, 177.5, 72) + # lats_coarse = np.linspace(-87.5, 87.5, 36) + # P = Panel(items=timeidx, major_axis=lons_coarse, minor_axis=lats_coarse) + # data = np.random.randn(72*36).reshape((72,36)) + # key = datetime(2009,2,28) + # P[key] = data# + + # assert_almost_equal(P[key].values, data) + + def test_major_xs(self): + ref = self.panel4d['l1']['ItemA'] + + idx = self.panel4d.major_axis[5] + xs = self.panel4d.major_xs(idx) + + assert_series_equal(xs['l1'].T['ItemA'], ref.xs(idx)) + + # not contained + idx = self.panel4d.major_axis[0] - bday + self.assertRaises(Exception, self.panel4d.major_xs, idx) + + def test_major_xs_mixed(self): + self.panel4d['l4'] = 'foo' + xs = self.panel4d.major_xs(self.panel4d.major_axis[0]) + self.assert_(xs['l1']['A'].dtype == np.float64) + self.assert_(xs['l4']['A'].dtype == np.object_) + + def test_minor_xs(self): + ref = self.panel4d['l1']['ItemA'] + + idx = self.panel4d.minor_axis[1] + xs = self.panel4d.minor_xs(idx) + + assert_series_equal(xs['l1'].T['ItemA'], ref[idx]) + + # not contained + self.assertRaises(Exception, self.panel4d.minor_xs, 'E') + + def test_minor_xs_mixed(self): + self.panel4d['l4'] = 'foo' + + xs = self.panel4d.minor_xs('D') + self.assert_(xs['l1'].T['ItemA'].dtype == np.float64) + self.assert_(xs['l4'].T['ItemA'].dtype == np.object_) + + def test_xs(self): + l1 = self.panel4d.xs('l1', axis=0) + expected = self.panel4d['l1'] + assert_panel_equal(l1, expected) + + # not view by default + l1.values[:] = np.nan + self.assert_(not np.isnan(self.panel4d['l1'].values).all()) + + # but can get view + l1_view = self.panel4d.xs('l1', axis=0, copy=False) + l1_view.values[:] = np.nan + self.assert_(np.isnan(self.panel4d['l1'].values).all()) + + # mixed-type + self.panel4d['strings'] = 'foo' + self.assertRaises(Exception, self.panel4d.xs, 'D', axis=2, + copy=False) + + def test_getitem_fancy_labels(self): + panel4d = self.panel4d + + labels = panel4d.labels[[1, 0]] + items = panel4d.items[[1, 0]] + dates = panel4d.major_axis[::2] + cols = ['D', 'C', 'F'] + + # all 4 specified + assert_panel4d_equal(panel4d.ix[labels, items, dates, cols], + panel4d.reindex(labels=labels, items=items, major=dates, minor=cols)) + + # 3 specified + assert_panel4d_equal(panel4d.ix[:, items, dates, cols], + panel4d.reindex(items=items, major=dates, minor=cols)) + + # 2 specified + assert_panel4d_equal(panel4d.ix[:, :, dates, cols], + panel4d.reindex(major=dates, minor=cols)) + + assert_panel4d_equal(panel4d.ix[:, items, :, cols], + panel4d.reindex(items=items, minor=cols)) + + assert_panel4d_equal(panel4d.ix[:, items, dates, :], + panel4d.reindex(items=items, major=dates)) + + # only 1 + assert_panel4d_equal(panel4d.ix[:, items, :, :], + panel4d.reindex(items=items)) + + assert_panel4d_equal(panel4d.ix[:, :, dates, :], + panel4d.reindex(major=dates)) + + assert_panel4d_equal(panel4d.ix[:, :, :, cols], + panel4d.reindex(minor=cols)) + + def test_getitem_fancy_slice(self): + pass + + def test_getitem_fancy_ints(self): + pass + + def test_getitem_fancy_xs(self): + raise nose.SkipTest + #self.assertRaises(NotImplementedError, self.panel4d.major_xs) + #self.assertRaises(NotImplementedError, self.panel4d.minor_xs) + + def test_getitem_fancy_xs_check_view(self): + raise nose.SkipTest + # item = 'ItemB' + # date = self.panel.major_axis[5] + # col = 'C' + + # # make sure it's always a view + # NS = slice(None, None) + + # # DataFrames + # comp = assert_frame_equal + # self._check_view(item, comp) + # self._check_view((item, NS), comp) + # self._check_view((item, NS, NS), comp) + # self._check_view((NS, date), comp) + # self._check_view((NS, date, NS), comp) + # self._check_view((NS, NS, 'C'), comp) + + # # Series + # comp = assert_series_equal + # self._check_view((item, date), comp) + # self._check_view((item, date, NS), comp) + # self._check_view((item, NS, 'C'), comp) + # self._check_view((NS, date, 'C'), comp)# + + #def _check_view(self, indexer, comp): + # cp = self.panel.copy() + # obj = cp.ix[indexer] + # obj.values[:] = 0 + # self.assert_((obj.values == 0).all()) + # comp(cp.ix[indexer].reindex_like(obj), obj) + + def test_get_value(self): + for label in self.panel4d.labels: + for item in self.panel4d.items: + for mjr in self.panel4d.major_axis[::2]: + for mnr in self.panel4d.minor_axis: + result = self.panel4d.get_value(label, item, mjr, mnr) + expected = self.panel4d[label][item][mnr][mjr] + assert_almost_equal(result, expected) + + def test_set_value(self): + for label in self.panel4d.labels: + for item in self.panel4d.items: + for mjr in self.panel4d.major_axis[::2]: + for mnr in self.panel4d.minor_axis: + self.panel4d.set_value(label, item, mjr, mnr, 1.) + assert_almost_equal(self.panel4d[label][item][mnr][mjr], 1.) + + # resize + res = self.panel4d.set_value('l4', 'ItemE', 'foo', 'bar', 1.5) + self.assert_(isinstance(res, Panel4D)) + self.assert_(res is not self.panel4d) + self.assertEqual(res.get_value('l4', 'ItemE', 'foo', 'bar'), 1.5) + + res3 = self.panel4d.set_value('l4', 'ItemE', 'foobar', 'baz', 5) + self.assert_(com.is_float_dtype(res3['l4'].values)) + +class TestPanel4d(unittest.TestCase, CheckIndexing, SafeForSparse, SafeForLongAndSparse): + + @classmethod + def assert_panel4d_equal(cls,x, y): + assert_panel4d_equal(x, y) + + def setUp(self): + self.panel4d = tm.makePanel4D() + add_nans(self.panel4d) + + def test_constructor(self): + # with BlockManager + panel4d = Panel4D(self.panel4d._data) + self.assert_(panel4d._data is self.panel4d._data) + + panel4d = Panel4D(self.panel4d._data, copy=True) + self.assert_(panel4d._data is not self.panel4d._data) + assert_panel4d_equal(panel4d, self.panel4d) + + # strings handled prop + #panel4d = Panel4D([[['foo', 'foo', 'foo',], + # ['foo', 'foo', 'foo']]]) + #self.assert_(wp.values.dtype == np.object_) + + vals = self.panel4d.values + + # no copy + panel4d = Panel4D(vals) + self.assert_(panel4d.values is vals) + + # copy + panel4d = Panel4D(vals, copy=True) + self.assert_(panel4d.values is not vals) + + def test_constructor_cast(self): + zero_filled = self.panel4d.fillna(0) + + casted = Panel4D(zero_filled._data, dtype=int) + casted2 = Panel4D(zero_filled.values, dtype=int) + + exp_values = zero_filled.values.astype(int) + assert_almost_equal(casted.values, exp_values) + assert_almost_equal(casted2.values, exp_values) + + # can't cast + data = [[['foo', 'bar', 'baz']]] + self.assertRaises(ValueError, Panel, data, dtype=float) + + def test_constructor_empty_panel(self): + empty = Panel() + self.assert_(len(empty.items) == 0) + self.assert_(len(empty.major_axis) == 0) + self.assert_(len(empty.minor_axis) == 0) + + def test_constructor_observe_dtype(self): + # GH #411 + panel = Panel(items=range(3), major_axis=range(3), + minor_axis=range(3), dtype='O') + self.assert_(panel.values.dtype == np.object_) + + def test_consolidate(self): + self.assert_(self.panel4d._data.is_consolidated()) + + self.panel4d['foo'] = 1. + self.assert_(not self.panel4d._data.is_consolidated()) + + panel4d = self.panel4d.consolidate() + self.assert_(panel4d._data.is_consolidated()) + + def test_ctor_dict(self): + l1 = self.panel4d['l1'] + l2 = self.panel4d['l2'] + + d = {'A' : l1, 'B' : l2.ix[['ItemB'],:,:] } + #d2 = {'A' : itema._series, 'B' : itemb[5:]._series} + #d3 = {'A' : DataFrame(itema._series), + # 'B' : DataFrame(itemb[5:]._series)} + + panel4d = Panel4D(d) + #wp2 = Panel.from_dict(d2) # nested Dict + #wp3 = Panel.from_dict(d3) + #self.assert_(wp.major_axis.equals(self.panel.major_axis)) + assert_panel_equal(panel4d['A'], self.panel4d['l1']) + assert_frame_equal(panel4d.ix['B','ItemB',:,:], self.panel4d.ix['l2',['ItemB'],:,:]['ItemB']) + + # intersect + #wp = Panel.from_dict(d, intersect=True) + #self.assert_(wp.major_axis.equals(itemb.index[5:])) + + # use constructor + #assert_panel_equal(Panel(d), Panel.from_dict(d)) + #assert_panel_equal(Panel(d2), Panel.from_dict(d2)) + #assert_panel_equal(Panel(d3), Panel.from_dict(d3)) + + # cast + #dcasted = dict((k, v.reindex(wp.major_axis).fillna(0)) + # for k, v in d.iteritems()) + #result = Panel(dcasted, dtype=int) + #expected = Panel(dict((k, v.astype(int)) + # for k, v in dcasted.iteritems())) + #assert_panel_equal(result, expected) + + def test_constructor_dict_mixed(self): + data = dict((k, v.values) for k, v in self.panel4d.iterkv()) + result = Panel4D(data) + exp_major = Index(np.arange(len(self.panel4d.major_axis))) + self.assert_(result.major_axis.equals(exp_major)) + + result = Panel4D(data, + labels = self.panel4d.labels, + items = self.panel4d.items, + major_axis = self.panel4d.major_axis, + minor_axis = self.panel4d.minor_axis) + assert_panel4d_equal(result, self.panel4d) + + data['l2'] = self.panel4d['l2'] + result = Panel4D(data) + assert_panel4d_equal(result, self.panel4d) + + # corner, blow up + data['l2'] = data['l2']['ItemB'] + self.assertRaises(Exception, Panel4D, data) + + data['l2'] = self.panel4d['l2'].values[:, :, :-1] + self.assertRaises(Exception, Panel4D, data) + + def test_constructor_resize(self): + data = self.panel4d._data + labels= self.panel4d.labels[:-1] + items = self.panel4d.items[:-1] + major = self.panel4d.major_axis[:-1] + minor = self.panel4d.minor_axis[:-1] + + result = Panel4D(data, labels=labels, items=items, major_axis=major, minor_axis=minor) + expected = self.panel4d.reindex(labels=labels, items=items, major=major, minor=minor) + assert_panel4d_equal(result, expected) + + result = Panel4D(data, items=items, major_axis=major) + expected = self.panel4d.reindex(items=items, major=major) + assert_panel4d_equal(result, expected) + + result = Panel4D(data, items=items) + expected = self.panel4d.reindex(items=items) + assert_panel4d_equal(result, expected) + + result = Panel4D(data, minor_axis=minor) + expected = self.panel4d.reindex(minor=minor) + assert_panel4d_equal(result, expected) + + def test_from_dict_mixed_orient(self): + raise nose.SkipTest + # df = tm.makeDataFrame() + # df['foo'] = 'bar' + + # data = {'k1' : df, + # 'k2' : df} + + # panel = Panel.from_dict(data, orient='minor') + + # self.assert_(panel['foo'].values.dtype == np.object_) + # self.assert_(panel['A'].values.dtype == np.float64) + + def test_values(self): + self.assertRaises(Exception, Panel, np.random.randn(5, 5, 5), + range(5), range(5), range(4)) + + def test_conform(self): + p = self.panel4d['l1'].filter(items=['ItemA', 'ItemB']) + conformed = self.panel4d.conform(p) + + assert(conformed.items.equals(self.panel4d.labels)) + assert(conformed.major_axis.equals(self.panel4d.major_axis)) + assert(conformed.minor_axis.equals(self.panel4d.minor_axis)) + + def test_reindex(self): + ref = self.panel4d['l2'] + + # labels + result = self.panel4d.reindex(labels=['l1','l2']) + assert_panel_equal(result['l2'], ref) + + # items + result = self.panel4d.reindex(items=['ItemA', 'ItemB']) + assert_frame_equal(result['l2']['ItemB'], ref['ItemB']) + + # major + new_major = list(self.panel4d.major_axis[:10]) + result = self.panel4d.reindex(major=new_major) + assert_frame_equal(result['l2']['ItemB'], ref['ItemB'].reindex(index=new_major)) + + # raise exception put both major and major_axis + self.assertRaises(Exception, self.panel4d.reindex, + major_axis=new_major, major=new_major) + + # minor + new_minor = list(self.panel4d.minor_axis[:2]) + result = self.panel4d.reindex(minor=new_minor) + assert_frame_equal(result['l2']['ItemB'], ref['ItemB'].reindex(columns=new_minor)) + + result = self.panel4d.reindex(labels=self.panel4d.labels, + items =self.panel4d.items, + major =self.panel4d.major_axis, + minor =self.panel4d.minor_axis) + + assert(result.labels is self.panel4d.labels) + assert(result.items is self.panel4d.items) + assert(result.major_axis is self.panel4d.major_axis) + assert(result.minor_axis is self.panel4d.minor_axis) + + self.assertRaises(Exception, self.panel4d.reindex) + + # with filling + smaller_major = self.panel4d.major_axis[::5] + smaller = self.panel4d.reindex(major=smaller_major) + + larger = smaller.reindex(major=self.panel4d.major_axis, + method='pad') + + assert_panel_equal(larger.ix[:,:,self.panel4d.major_axis[1],:], + smaller.ix[:,:,smaller_major[0],:]) + + # don't necessarily copy + result = self.panel4d.reindex(major=self.panel4d.major_axis, copy=False) + self.assert_(result is self.panel4d) + + def test_reindex_like(self): + # reindex_like + smaller = self.panel4d.reindex(labels=self.panel4d.labels[:-1], + items =self.panel4d.items[:-1], + major =self.panel4d.major_axis[:-1], + minor =self.panel4d.minor_axis[:-1]) + smaller_like = self.panel4d.reindex_like(smaller) + assert_panel4d_equal(smaller, smaller_like) + + def test_take(self): + raise nose.SkipTest + # # axis == 0 + # result = self.panel.take([2, 0, 1], axis=0) + # expected = self.panel.reindex(items=['ItemC', 'ItemA', 'ItemB']) + # assert_panel_equal(result, expected)# + + # # axis >= 1 + # result = self.panel.take([3, 0, 1, 2], axis=2) + # expected = self.panel.reindex(minor=['D', 'A', 'B', 'C']) + # assert_panel_equal(result, expected) + + # self.assertRaises(Exception, self.panel.take, [3, -1, 1, 2], axis=2) + # self.assertRaises(Exception, self.panel.take, [4, 0, 1, 2], axis=2) + + def test_sort_index(self): + import random + + rlabels= list(self.panel4d.labels) + ritems = list(self.panel4d.items) + rmajor = list(self.panel4d.major_axis) + rminor = list(self.panel4d.minor_axis) + random.shuffle(rlabels) + random.shuffle(ritems) + random.shuffle(rmajor) + random.shuffle(rminor) + + random_order = self.panel4d.reindex(labels=rlabels) + sorted_panel4d = random_order.sort_index(axis=0) + assert_panel4d_equal(sorted_panel4d, self.panel4d) + + # descending + #random_order = self.panel.reindex(items=ritems) + #sorted_panel = random_order.sort_index(axis=0, ascending=False) + #assert_panel_equal(sorted_panel, + # self.panel.reindex(items=self.panel.items[::-1])) + + #random_order = self.panel.reindex(major=rmajor) + #sorted_panel = random_order.sort_index(axis=1) + #assert_panel_equal(sorted_panel, self.panel) + + #random_order = self.panel.reindex(minor=rminor) + #sorted_panel = random_order.sort_index(axis=2) + #assert_panel_equal(sorted_panel, self.panel) + + def test_fillna(self): + filled = self.panel4d.fillna(0) + self.assert_(np.isfinite(filled.values).all()) + + filled = self.panel4d.fillna(method='backfill') + assert_panel_equal(filled['l1'], + self.panel4d['l1'].fillna(method='backfill')) + + panel4d = self.panel4d.copy() + panel4d['str'] = 'foo' + + filled = panel4d.fillna(method='backfill') + assert_panel_equal(filled['l1'], + panel4d['l1'].fillna(method='backfill')) + + empty = self.panel4d.reindex(labels=[]) + filled = empty.fillna(0) + assert_panel4d_equal(filled, empty) + + def test_swapaxes(self): + result = self.panel4d.swapaxes('labels','items') + self.assert_(result.items is self.panel4d.labels) + + result = self.panel4d.swapaxes('labels','minor') + self.assert_(result.labels is self.panel4d.minor_axis) + + result = self.panel4d.swapaxes('items', 'minor') + self.assert_(result.items is self.panel4d.minor_axis) + + result = self.panel4d.swapaxes('items', 'major') + self.assert_(result.items is self.panel4d.major_axis) + + result = self.panel4d.swapaxes('major', 'minor') + self.assert_(result.major_axis is self.panel4d.minor_axis) + + # this should also work + result = self.panel4d.swapaxes(0, 1) + self.assert_(result.labels is self.panel4d.items) + + # this should also work + self.assertRaises(Exception, self.panel4d.swapaxes, 'items', 'items') + + def test_to_frame(self): + raise nose.SkipTest + # # filtered + # filtered = self.panel.to_frame() + # expected = self.panel.to_frame().dropna(how='any') + # assert_frame_equal(filtered, expected) + + # # unfiltered + # unfiltered = self.panel.to_frame(filter_observations=False) + # assert_panel_equal(unfiltered.to_panel(), self.panel) + + # # names + # self.assertEqual(unfiltered.index.names, ['major', 'minor']) + + def test_to_frame_mixed(self): + raise nose.SkipTest + # panel = self.panel.fillna(0) + # panel['str'] = 'foo' + # panel['bool'] = panel['ItemA'] > 0 + + # lp = panel.to_frame() + # wp = lp.to_panel() + # self.assertEqual(wp['bool'].values.dtype, np.bool_) + # assert_frame_equal(wp['bool'], panel['bool']) + + def test_filter(self): + pass + + def test_apply(self): + pass + + def test_compound(self): + raise nose.SkipTest + # compounded = self.panel.compound() + + # assert_series_equal(compounded['ItemA'], + # (1 + self.panel['ItemA']).product(0) - 1) + + def test_shift(self): + raise nose.SkipTest + # # major + # idx = self.panel.major_axis[0] + # idx_lag = self.panel.major_axis[1] + + # shifted = self.panel.shift(1) + + # assert_frame_equal(self.panel.major_xs(idx), + # shifted.major_xs(idx_lag)) + + # # minor + # idx = self.panel.minor_axis[0] + # idx_lag = self.panel.minor_axis[1] + + # shifted = self.panel.shift(1, axis='minor') + + # assert_frame_equal(self.panel.minor_xs(idx), + # shifted.minor_xs(idx_lag)) + + # self.assertRaises(Exception, self.panel.shift, 1, axis='items') + + def test_multiindex_get(self): + raise nose.SkipTest + # ind = MultiIndex.from_tuples([('a', 1), ('a', 2), ('b', 1), ('b',2)], + # names=['first', 'second']) + # wp = Panel(np.random.random((4,5,5)), + # items=ind, + # major_axis=np.arange(5), + # minor_axis=np.arange(5)) + # f1 = wp['a'] + # f2 = wp.ix['a'] + # assert_panel_equal(f1, f2) + + # self.assert_((f1.items == [1, 2]).all()) + # self.assert_((f2.items == [1, 2]).all()) + + # ind = MultiIndex.from_tuples([('a', 1), ('a', 2), ('b', 1)], + # names=['first', 'second']) + + def test_multiindex_blocks(self): + raise nose.SkipTest + # ind = MultiIndex.from_tuples([('a', 1), ('a', 2), ('b', 1)], + # names=['first', 'second']) + # wp = Panel(self.panel._data) + # wp.items = ind + # f1 = wp['a'] + # self.assert_((f1.items == [1, 2]).all()) + + # f1 = wp[('b',1)] + # self.assert_((f1.columns == ['A', 'B', 'C', 'D']).all()) + + def test_repr_empty(self): + empty = Panel4D() + repr(empty) + + def test_rename(self): + mapper = { + 'l1' : 'foo', + 'l2' : 'bar', + 'l3' : 'baz' + } + + renamed = self.panel4d.rename_axis(mapper, axis=0) + exp = Index(['foo', 'bar', 'baz']) + self.assert_(renamed.labels.equals(exp)) + + renamed = self.panel4d.rename_axis(str.lower, axis=3) + exp = Index(['a', 'b', 'c', 'd']) + self.assert_(renamed.minor_axis.equals(exp)) + + # don't copy + renamed_nocopy = self.panel4d.rename_axis(mapper, axis=0, copy=False) + renamed_nocopy['foo'] = 3. + self.assert_((self.panel4d['l1'].values == 3).all()) + + def test_get_attr(self): + assert_panel_equal(self.panel4d['l1'], self.panel4d.l1) + + def test_group_agg(self): + values = np.ones((10, 2)) * np.arange(10).reshape((10, 1)) + bounds = np.arange(5) * 2 + f = lambda x: x.mean(axis=0) + + agged = group_agg(values, bounds, f) + + assert(agged[1][0] == 2.5) + assert(agged[2][0] == 4.5) + + # test a function that doesn't aggregate + f2 = lambda x: np.zeros((2,2)) + self.assertRaises(Exception, group_agg, values, bounds, f2) + + def test_from_frame_level1_unsorted(self): + raise nose.SkipTest + # tuples = [('MSFT', 3), ('MSFT', 2), ('AAPL', 2), + # ('AAPL', 1), ('MSFT', 1)] + # midx = MultiIndex.from_tuples(tuples) + # df = DataFrame(np.random.rand(5,4), index=midx) + # p = df.to_panel() + # assert_frame_equal(p.minor_xs(2), df.ix[:,2].sort_index()) + + def test_to_excel(self): + raise nose.SkipTest + # try: + # import xlwt + # import xlrd + # import openpyxl + # except ImportError: + # raise nose.SkipTest + + # for ext in ['xls', 'xlsx']: + # path = '__tmp__.' + ext + # self.panel.to_excel(path) + # reader = ExcelFile(path) + # for item, df in self.panel.iteritems(): + # recdf = reader.parse(str(item),index_col=0) + # assert_frame_equal(df, recdf) + # os.remove(path) + + +if __name__ == '__main__': + import nose + nose.runmodule(argv=[__file__,'-vvs','-x','--pdb', '--pdb-failure'], + exit=False) diff --git a/pandas/tools/tests/test_merge.py b/pandas/tools/tests/test_merge.py old mode 100644 new mode 100755 diff --git a/pandas/util/testing.py b/pandas/util/testing.py index 3ee53a8c1b5da..aa692f4844c49 100644 --- a/pandas/util/testing.py +++ b/pandas/util/testing.py @@ -19,6 +19,7 @@ import pandas.core.series as series import pandas.core.frame as frame import pandas.core.panel as panel +import pandas.core.panel4d as panel4d from pandas import bdate_range from pandas.tseries.index import DatetimeIndex @@ -29,6 +30,7 @@ Series = series.Series DataFrame = frame.DataFrame Panel = panel.Panel +Panel4D = panel4d.Panel4D N = 30 K = 4 @@ -198,6 +200,18 @@ def assert_panel_equal(left, right, check_panel_type=False): for col in right: assert(col in left) +def assert_panel4d_equal(left, right): + assert(left.labels.equals(right.labels)) + assert(left.items.equals(right.items)) + assert(left.major_axis.equals(right.major_axis)) + assert(left.minor_axis.equals(right.minor_axis)) + + for col, series in left.iterkv(): + assert(col in right) + assert_panel_equal(series, right[col]) + + for col in right: + assert(col in left) def assert_contains_all(iterable, dic): for k in iterable: @@ -316,6 +330,8 @@ def makePanel(): data = dict((c, makeTimeDataFrame()) for c in cols) return Panel.fromDict(data) +def makePanel4D(): + return Panel4D(dict(l1 = makePanel(), l2 = makePanel(), l3 = makePanel())) def add_nans(panel): I, J, N = panel.shape @@ -324,6 +340,10 @@ def add_nans(panel): for j, col in enumerate(dm.columns): dm[col][:i + j] = np.NaN +def add_nans_panel4d(panel4d): + for l, label in enumerate(panel4d.labels): + panel = panel4d[label] + add_nans(panel) class TestSubDict(dict): def __init__(self, *args, **kwargs): From 77aa4f687fec2635fead61d97dc642c7a8a75d2f Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Tue, 13 Nov 2012 23:22:58 -0500 Subject: [PATCH 2/3] fixed file modes --- pandas/core/api.py | 1 - pandas/core/panel.py | 1 - pandas/core/panel4d.py | 1 + pandas/core/panelnd.py | 2 -- pandas/tests/test_panel.py | 1 - pandas/tests/test_panel4d.py | 1 + 6 files changed, 2 insertions(+), 5 deletions(-) diff --git a/pandas/core/api.py b/pandas/core/api.py index 6679afcff6ca8..6cbdae430ba0b 100755 --- a/pandas/core/api.py +++ b/pandas/core/api.py @@ -4,7 +4,6 @@ import numpy as np from pandas.core.algorithms import factorize, match, unique, value_counts - from pandas.core.common import isnull, notnull, save, load from pandas.core.categorical import Categorical, Factor from pandas.core.format import (set_printoptions, reset_printoptions, diff --git a/pandas/core/panel.py b/pandas/core/panel.py index 697566e8f2404..4f5203b103ce7 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -6,7 +6,6 @@ import operator import sys import numpy as np - from pandas.core.common import (PandasError, _mut_exclusive, _try_sort, _default_index, _infer_dtype) from pandas.core.categorical import Factor diff --git a/pandas/core/panel4d.py b/pandas/core/panel4d.py index 09858c0ff59b6..504111bef5414 100755 --- a/pandas/core/panel4d.py +++ b/pandas/core/panel4d.py @@ -3,6 +3,7 @@ from pandas.core.panel import Panel import pandas.lib as lib + class Panel4D(Panel): _AXIS_ORDERS = ['labels','items','major_axis','minor_axis'] _AXIS_NUMBERS = dict([ (a,i) for i, a in enumerate(_AXIS_ORDERS) ]) diff --git a/pandas/core/panelnd.py b/pandas/core/panelnd.py index 3173b2d1c14e5..e4638750aa1b2 100644 --- a/pandas/core/panelnd.py +++ b/pandas/core/panelnd.py @@ -1,7 +1,5 @@ """ Factory methods to create N-D panels """ -import sys -sys.path.insert(0,'/home/jreback/pandas') import pandas from pandas.core.panel import Panel import pandas.lib as lib diff --git a/pandas/tests/test_panel.py b/pandas/tests/test_panel.py index d36e35e0e20fd..82c6ea65d133a 100755 --- a/pandas/tests/test_panel.py +++ b/pandas/tests/test_panel.py @@ -981,7 +981,6 @@ def test_swapaxes(self): self.assertRaises(Exception, self.panel.swapaxes, 'items', 'items') def test_transpose(self): - result = self.panel.transpose('minor', 'major', 'items') expected = self.panel.swapaxes('items', 'minor') assert_panel_equal(result, expected) diff --git a/pandas/tests/test_panel4d.py b/pandas/tests/test_panel4d.py index 4058739ba772b..40bd84044dbdd 100755 --- a/pandas/tests/test_panel4d.py +++ b/pandas/tests/test_panel4d.py @@ -798,6 +798,7 @@ def test_reindex_like(self): def test_take(self): raise nose.SkipTest + # # axis == 0 # result = self.panel.take([2, 0, 1], axis=0) # expected = self.panel.reindex(items=['ItemC', 'ItemA', 'ItemB']) From da287b094f153cda7974f0cabe5daf07c426a02e Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Thu, 15 Nov 2012 10:56:30 -0500 Subject: [PATCH 3/3] added tests file for panelnd in pandas/tests/test_panelnd.py --- pandas/tests/test_panelnd.py | 75 ++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 pandas/tests/test_panelnd.py diff --git a/pandas/tests/test_panelnd.py b/pandas/tests/test_panelnd.py new file mode 100644 index 0000000000000..0d8a8c2023014 --- /dev/null +++ b/pandas/tests/test_panelnd.py @@ -0,0 +1,75 @@ +from datetime import datetime +import os +import operator +import unittest +import nose + +import numpy as np + +from pandas.core import panelnd +from pandas.core.panel import Panel +import pandas.core.common as com +from pandas.util import py3compat + +from pandas.util.testing import (assert_panel_equal, + assert_panel4d_equal, + assert_frame_equal, + assert_series_equal, + assert_almost_equal) +import pandas.util.testing as tm + +class TestPanelnd(unittest.TestCase): + + def setUp(self): + pass + + def test_4d_construction(self): + + # create a 4D + Panel4D = panelnd.create_nd_panel_factory( + klass_name = 'Panel4D', + axis_orders = ['labels','items','major_axis','minor_axis'], + axis_slices = { 'items' : 'items', 'major_axis' : 'major_axis', 'minor_axis' : 'minor_axis' }, + slicer = Panel, + axis_aliases = { 'major' : 'major_axis', 'minor' : 'minor_axis' }, + stat_axis = 2) + + p4d = Panel4D(dict(L1 = tm.makePanel(), L2 = tm.makePanel())) + + def test_5d_construction(self): + + # create a 4D + Panel4D = panelnd.create_nd_panel_factory( + klass_name = 'Panel4D', + axis_orders = ['labels1','items','major_axis','minor_axis'], + axis_slices = { 'items' : 'items', 'major_axis' : 'major_axis', 'minor_axis' : 'minor_axis' }, + slicer = Panel, + axis_aliases = { 'major' : 'major_axis', 'minor' : 'minor_axis' }, + stat_axis = 2) + + p4d = Panel4D(dict(L1 = tm.makePanel(), L2 = tm.makePanel())) + + # create a 5D + Panel5D = panelnd.create_nd_panel_factory( + klass_name = 'Panel5D', + axis_orders = [ 'cool1', 'labels1','items','major_axis','minor_axis'], + axis_slices = { 'labels1' : 'labels1', 'items' : 'items', 'major_axis' : 'major_axis', 'minor_axis' : 'minor_axis' }, + slicer = Panel4D, + axis_aliases = { 'major' : 'major_axis', 'minor' : 'minor_axis' }, + stat_axis = 2) + + p5d = Panel5D(dict(C1 = p4d)) + + # slice back to 4d + results = p5d.ix['C1',:,:,0:3,:] + expected = p4d.ix[:,:,0:3,:] + assert_panel_equal(results['L1'], expected['L1']) + + # test a transpose + #results = p5d.transpose(1,2,3,4,0) + #expected = + +if __name__ == '__main__': + import nose + nose.runmodule(argv=[__file__,'-vvs','-x','--pdb', '--pdb-failure'], + exit=False)