diff --git a/pandas/core/nanops.py b/pandas/core/nanops.py index 43533b67b5441..7764d6e1e1fa9 100644 --- a/pandas/core/nanops.py +++ b/pandas/core/nanops.py @@ -1,6 +1,7 @@ import itertools import functools import numpy as np +import operator try: import bottleneck as bn @@ -10,13 +11,10 @@ import pandas.hashtable as _hash from pandas import compat, lib, algos, tslib -from pandas.compat import builtins from pandas.core.common import (isnull, notnull, _values_from_object, - _maybe_upcast_putmask, - ensure_float, _ensure_float64, - _ensure_int64, _ensure_object, - is_float, is_integer, is_complex, - is_float_dtype, + _maybe_upcast_putmask, _ensure_float64, + _ensure_int64, _ensure_object, is_float, + is_integer, is_complex, is_float_dtype, is_complex_dtype, is_integer_dtype, is_bool_dtype, is_object_dtype, is_datetime64_dtype, is_timedelta64_dtype, @@ -26,7 +24,6 @@ class disallow(object): - def __init__(self, *dtypes): super(disallow, self).__init__() self.dtypes = tuple(np.dtype(dtype).type for dtype in dtypes) @@ -41,8 +38,8 @@ def _f(*args, **kwargs): obj_iter = itertools.chain(args, compat.itervalues(kwargs)) if any(self.check(obj) for obj in obj_iter): raise TypeError('reduction operation {0!r} not allowed for ' - 'this dtype'.format(f.__name__.replace('nan', - ''))) + 'this dtype'.format( + f.__name__.replace('nan', ''))) try: return f(*args, **kwargs) except ValueError as e: @@ -53,11 +50,11 @@ def _f(*args, **kwargs): if is_object_dtype(args[0]): raise TypeError(e) raise + return _f class bottleneck_switch(object): - def __init__(self, zero_value=None, **kwargs): self.zero_value = zero_value self.kwargs = kwargs @@ -91,8 +88,8 @@ def f(values, axis=None, skipna=True, **kwds): result.fill(0) return result - if _USE_BOTTLENECK and skipna and _bn_ok_dtype(values.dtype, - bn_name): + if (_USE_BOTTLENECK and skipna and + _bn_ok_dtype(values.dtype, bn_name)): result = bn_func(values, axis=axis, **kwds) # prefer to treat inf/-inf as NA, but must compute the func @@ -121,8 +118,7 @@ def f(values, axis=None, skipna=True, **kwds): def _bn_ok_dtype(dt, name): # Bottleneck chokes on datetime64 - if (not is_object_dtype(dt) and - not is_datetime_or_timedelta_dtype(dt)): + if (not is_object_dtype(dt) and not is_datetime_or_timedelta_dtype(dt)): # bottleneck does not properly upcast during the sum # so can overflow @@ -142,7 +138,7 @@ def _has_infs(result): return lib.has_infs_f4(result.ravel()) try: return np.isinf(result).any() - except (TypeError, NotImplementedError) as e: + except (TypeError, NotImplementedError): # if it doesn't support infs, then it can't have infs return False @@ -173,8 +169,9 @@ def _get_fill_value(dtype, fill_value=None, fill_value_typ=None): def _get_values(values, skipna, fill_value=None, fill_value_typ=None, isfinite=False, copy=True): """ utility to get the values view, mask, dtype - if necessary copy and mask using the specified fill_value - copy = True will force the copy """ + if necessary copy and mask using the specified fill_value + copy = True will force the copy + """ values = _values_from_object(values) if isfinite: mask = _isfinite(values) @@ -331,7 +328,8 @@ def get_median(x): if values.ndim > 1: # there's a non-empty array to apply over otherwise numpy raises if notempty: - return _wrap_results(np.apply_along_axis(get_median, axis, values), dtype) + return _wrap_results( + np.apply_along_axis(get_median, axis, values), dtype) # must return the correct shape, but median is not defined for the # empty set so return nans of shape "everything but the passed axis" @@ -400,7 +398,7 @@ def nanvar(values, axis=None, skipna=True, ddof=1): avg = _ensure_numeric(values.sum(axis=axis, dtype=np.float64)) / count if axis is not None: avg = np.expand_dims(avg, axis) - sqr = _ensure_numeric((avg - values) ** 2) + sqr = _ensure_numeric((avg - values)**2) np.putmask(sqr, mask, 0) result = sqr.sum(axis=axis, dtype=np.float64) / d @@ -429,13 +427,10 @@ def _nanminmax(meth, fill_value_typ): @bottleneck_switch() def reduction(values, axis=None, skipna=True): values, mask, dtype, dtype_max = _get_values( - values, - skipna, - fill_value_typ=fill_value_typ, - ) + values, skipna, fill_value_typ=fill_value_typ, ) - if ((axis is not None and values.shape[axis] == 0) - or values.size == 0): + if ((axis is not None and values.shape[axis] == 0) or + values.size == 0): try: result = getattr(values, meth)(axis, dtype=dtype_max) result.fill(np.nan) @@ -477,7 +472,7 @@ def nanargmin(values, axis=None, skipna=True): return result -@disallow('M8','m8') +@disallow('M8', 'm8') def nanskew(values, axis=None, skipna=True): mask = isnull(values) @@ -493,15 +488,15 @@ def nanskew(values, axis=None, skipna=True): typ = values.dtype.type A = values.sum(axis) / count - B = (values ** 2).sum(axis) / count - A ** typ(2) - C = (values ** 3).sum(axis) / count - A ** typ(3) - typ(3) * A * B + B = (values**2).sum(axis) / count - A**typ(2) + C = (values**3).sum(axis) / count - A**typ(3) - typ(3) * A * B # floating point error B = _zero_out_fperr(B) C = _zero_out_fperr(C) result = ((np.sqrt(count * count - count) * C) / - ((count - typ(2)) * np.sqrt(B) ** typ(3))) + ((count - typ(2)) * np.sqrt(B)**typ(3))) if isinstance(result, np.ndarray): result = np.where(B == 0, 0, result) @@ -514,7 +509,7 @@ def nanskew(values, axis=None, skipna=True): return result -@disallow('M8','m8') +@disallow('M8', 'm8') def nankurt(values, axis=None, skipna=True): mask = isnull(values) @@ -530,22 +525,25 @@ def nankurt(values, axis=None, skipna=True): typ = values.dtype.type A = values.sum(axis) / count - B = (values ** 2).sum(axis) / count - A ** typ(2) - C = (values ** 3).sum(axis) / count - A ** typ(3) - typ(3) * A * B - D = (values ** 4).sum(axis) / count - A ** typ(4) - typ(6) * B * A * A - typ(4) * C * A + B = (values**2).sum(axis) / count - A**typ(2) + C = (values**3).sum(axis) / count - A**typ(3) - typ(3) * A * B + D = ((values**4).sum(axis) / count - A**typ(4) - + typ(6) * B * A * A - typ(4) * C * A) B = _zero_out_fperr(B) D = _zero_out_fperr(D) if not isinstance(B, np.ndarray): - # if B is a scalar, check these corner cases first before doing division + # if B is a scalar, check these corner cases first before doing + # division if count < 4: return np.nan if B == 0: return 0 - result = (((count * count - typ(1)) * D / (B * B) - typ(3) * ((count - typ(1)) ** typ(2))) / - ((count - typ(2)) * (count - typ(3)))) + result = (((count * count - typ(1)) * D / (B * B) - typ(3) * + ((count - typ(1))**typ(2))) / ((count - typ(2)) * + (count - typ(3)))) if isinstance(result, np.ndarray): result = np.where(B == 0, 0, result) @@ -554,7 +552,7 @@ def nankurt(values, axis=None, skipna=True): return result -@disallow('M8','m8') +@disallow('M8', 'm8') def nanprod(values, axis=None, skipna=True): mask = isnull(values) if skipna and not is_any_int_dtype(values): @@ -621,7 +619,7 @@ def _zero_out_fperr(arg): return arg.dtype.type(0) if np.abs(arg) < 1e-14 else arg -@disallow('M8','m8') +@disallow('M8', 'm8') def nancorr(a, b, method='pearson', min_periods=None): """ a, b: ndarrays @@ -668,7 +666,7 @@ def _spearman(a, b): return _cor_methods[method] -@disallow('M8','m8') +@disallow('M8', 'm8') def nancov(a, b, min_periods=None): if len(a) != len(b): raise AssertionError('Operands to nancov must have same size') @@ -711,8 +709,6 @@ def _ensure_numeric(x): # NA-friendly array comparisons -import operator - def make_nancomp(op): def f(x, y): @@ -728,8 +724,10 @@ def f(x, y): np.putmask(result, mask, np.nan) return result + return f + nangt = make_nancomp(operator.gt) nange = make_nancomp(operator.ge) nanlt = make_nancomp(operator.lt) diff --git a/pandas/core/ops.py b/pandas/core/ops.py index 4d003456f8102..ef699be301d4b 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -18,12 +18,13 @@ from pandas.lib import isscalar from pandas.tslib import iNaT from pandas.compat import bind_method -from pandas.core.common import(is_list_like, notnull, isnull, - _values_from_object, _maybe_match_name, - needs_i8_conversion, is_datetimelike_v_numeric, - is_integer_dtype, is_categorical_dtype, is_object_dtype, - is_timedelta64_dtype, is_datetime64_dtype, is_datetime64tz_dtype, - is_bool_dtype) +from pandas.core.common import (is_list_like, notnull, isnull, + _values_from_object, _maybe_match_name, + needs_i8_conversion, is_datetimelike_v_numeric, + is_integer_dtype, is_categorical_dtype, + is_object_dtype, is_timedelta64_dtype, + is_datetime64_dtype, is_datetime64tz_dtype, + is_bool_dtype) from pandas.io.common import PerformanceWarning # ----------------------------------------------------------------------------- @@ -44,6 +45,7 @@ def _create_methods(arith_method, radd_func, comp_method, bool_method, else: op = lambda x: None if special: + def names(x): if x[-1] == "_": return "__%s_" % x @@ -54,6 +56,7 @@ def names(x): radd_func = radd_func or operator.add # Inframe, all special methods have default_axis=None, flex methods have # default_axis set to the default (columns) + # yapf: disable new_methods = dict( add=arith_method(operator.add, names('add'), op('+'), default_axis=default_axis), @@ -68,8 +71,8 @@ def names(x): default_axis=default_axis), floordiv=arith_method(operator.floordiv, names('floordiv'), op('//'), default_axis=default_axis, fill_zeros=np.inf), - # Causes a floating point exception in the tests when numexpr - # enabled, so for now no speedup + # Causes a floating point exception in the tests when numexpr enabled, + # so for now no speedup mod=arith_method(operator.mod, names('mod'), None, default_axis=default_axis, fill_zeros=np.nan), pow=arith_method(operator.pow, names('pow'), op('**'), @@ -88,12 +91,12 @@ def names(x): names('rfloordiv'), op('//'), default_axis=default_axis, fill_zeros=np.inf, reversed=True), - rpow=arith_method(lambda x, y: y ** x, names('rpow'), op('**'), + rpow=arith_method(lambda x, y: y**x, names('rpow'), op('**'), default_axis=default_axis, reversed=True), rmod=arith_method(lambda x, y: y % x, names('rmod'), op('%'), default_axis=default_axis, fill_zeros=np.nan, - reversed=True), - ) + reversed=True),) + # yapf: enable new_methods['div'] = new_methods['truediv'] new_methods['rdiv'] = new_methods['rtruediv'] @@ -105,19 +108,19 @@ def names(x): lt=comp_method(operator.lt, names('lt'), op('<')), gt=comp_method(operator.gt, names('gt'), op('>')), le=comp_method(operator.le, names('le'), op('<=')), - ge=comp_method(operator.ge, names('ge'), op('>=')), - )) + ge=comp_method(operator.ge, names('ge'), op('>=')), )) if bool_method: - new_methods.update(dict( - and_=bool_method(operator.and_, names('and_'), op('&')), - or_=bool_method(operator.or_, names('or_'), op('|')), - # For some reason ``^`` wasn't used in original. - xor=bool_method(operator.xor, names('xor'), op('^')), - rand_=bool_method(lambda x, y: operator.and_(y, x), - names('rand_'), op('&')), - ror_=bool_method(lambda x, y: operator.or_(y, x), names('ror_'), op('|')), - rxor=bool_method(lambda x, y: operator.xor(y, x), names('rxor'), op('^')) - )) + new_methods.update( + dict(and_=bool_method(operator.and_, names('and_'), op('&')), + or_=bool_method(operator.or_, names('or_'), op('|')), + # For some reason ``^`` wasn't used in original. + xor=bool_method(operator.xor, names('xor'), op('^')), + rand_=bool_method(lambda x, y: operator.and_(y, x), + names('rand_'), op('&')), + ror_=bool_method(lambda x, y: operator.or_(y, x), + names('ror_'), op('|')), + rxor=bool_method(lambda x, y: operator.xor(y, x), + names('rxor'), op('^')))) new_methods = dict((names(k), v) for k, v in new_methods.items()) return new_methods @@ -142,7 +145,7 @@ def add_methods(cls, new_methods, force, select, exclude): bind_method(cls, name, method) -#---------------------------------------------------------------------- +# ---------------------------------------------------------------------- # Arithmetic def add_special_arithmetic_methods(cls, arith_method=None, radd_func=None, comp_method=None, bool_method=None, @@ -193,19 +196,19 @@ def f(self, other): # this makes sure that we are aligned like the input # we are updating inplace so we want to ignore is_copy - self._update_inplace(result.reindex_like(self,copy=False)._data, + self._update_inplace(result.reindex_like(self, copy=False)._data, verify_is_copy=False) return self + return f - new_methods.update(dict( - __iadd__=_wrap_inplace_method(new_methods["__add__"]), - __isub__=_wrap_inplace_method(new_methods["__sub__"]), - __imul__=_wrap_inplace_method(new_methods["__mul__"]), - __itruediv__=_wrap_inplace_method(new_methods["__truediv__"]), - __ipow__=_wrap_inplace_method(new_methods["__pow__"]), - )) + new_methods.update( + dict(__iadd__=_wrap_inplace_method(new_methods["__add__"]), + __isub__=_wrap_inplace_method(new_methods["__sub__"]), + __imul__=_wrap_inplace_method(new_methods["__mul__"]), + __itruediv__=_wrap_inplace_method(new_methods["__truediv__"]), + __ipow__=_wrap_inplace_method(new_methods["__pow__"]), )) if not compat.PY3: new_methods["__idiv__"] = new_methods["__div__"] @@ -243,14 +246,13 @@ def add_flex_arithmetic_methods(cls, flex_arith_method, radd_func=None, """ radd_func = radd_func or (lambda x, y: operator.add(y, x)) # in frame, default axis is 'columns', doesn't matter for series and panel - new_methods = _create_methods( - flex_arith_method, radd_func, flex_comp_method, flex_bool_method, - use_numexpr, default_axis='columns', special=False) - new_methods.update(dict( - multiply=new_methods['mul'], - subtract=new_methods['sub'], - divide=new_methods['div'] - )) + new_methods = _create_methods(flex_arith_method, radd_func, + flex_comp_method, flex_bool_method, + use_numexpr, default_axis='columns', + special=False) + new_methods.update(dict(multiply=new_methods['mul'], + subtract=new_methods['sub'], + divide=new_methods['div'])) # opt out of bool flex methods for now for k in ('ror_', 'rxor', 'rand_'): if k in new_methods: @@ -261,7 +263,6 @@ def add_flex_arithmetic_methods(cls, flex_arith_method, radd_func=None, class _TimeOp(object): - """ Wrapper around Series datetime/time/timedelta arithmetic operations. Generally, you should use classmethod ``maybe_convert_for_time_op`` as an @@ -275,7 +276,7 @@ def __init__(self, left, right, name, na_op): # need to make sure that we are aligning the data if isinstance(left, pd.Series) and isinstance(right, pd.Series): - left, right = left.align(right,copy=False) + left, right = left.align(right, copy=False) lvalues = self._convert_to_array(left, name=name) rvalues = self._convert_to_array(right, name=name, other=lvalues) @@ -289,7 +290,8 @@ def __init__(self, left, right, name, na_op): self.is_timedelta_lhs = is_timedelta64_dtype(lvalues) self.is_datetime64_lhs = is_datetime64_dtype(lvalues) self.is_datetime64tz_lhs = is_datetime64tz_dtype(lvalues) - self.is_datetime_lhs = self.is_datetime64_lhs or self.is_datetime64tz_lhs + self.is_datetime_lhs = (self.is_datetime64_lhs or + self.is_datetime64tz_lhs) self.is_integer_lhs = left.dtype.kind in ['i', 'u'] self.is_floating_lhs = left.dtype.kind == 'f' @@ -298,21 +300,23 @@ def __init__(self, left, right, name, na_op): self.is_offset_rhs = self._is_offset(right) self.is_datetime64_rhs = is_datetime64_dtype(rvalues) self.is_datetime64tz_rhs = is_datetime64tz_dtype(rvalues) - self.is_datetime_rhs = self.is_datetime64_rhs or self.is_datetime64tz_rhs + self.is_datetime_rhs = (self.is_datetime64_rhs or + self.is_datetime64tz_rhs) self.is_timedelta_rhs = is_timedelta64_dtype(rvalues) self.is_integer_rhs = rvalues.dtype.kind in ('i', 'u') self.is_floating_rhs = rvalues.dtype.kind == 'f' self._validate(lvalues, rvalues, name) - self.lvalues, self.rvalues = self._convert_for_datetime(lvalues, rvalues) + self.lvalues, self.rvalues = self._convert_for_datetime(lvalues, + rvalues) def _validate(self, lvalues, rvalues, name): # timedelta and integer mul/div - if (self.is_timedelta_lhs and - (self.is_integer_rhs or self.is_floating_rhs)) or ( - self.is_timedelta_rhs and - (self.is_integer_lhs or self.is_floating_lhs)): + if ((self.is_timedelta_lhs and + (self.is_integer_rhs or self.is_floating_rhs)) or + (self.is_timedelta_rhs and + (self.is_integer_lhs or self.is_floating_lhs))): if name not in ('__div__', '__truediv__', '__mul__', '__rmul__'): raise TypeError("can only operate on a timedelta and an " @@ -320,59 +324,56 @@ def _validate(self, lvalues, rvalues, name): "multiplication, but the operator [%s] was" "passed" % name) - # 2 timedeltas elif ((self.is_timedelta_lhs and (self.is_timedelta_rhs or self.is_offset_rhs)) or (self.is_timedelta_rhs and (self.is_timedelta_lhs or self.is_offset_lhs))): - if name not in ('__div__', '__rdiv__', '__truediv__', '__rtruediv__', - '__add__', '__radd__', '__sub__', '__rsub__'): + if name not in ('__div__', '__rdiv__', '__truediv__', + '__rtruediv__', '__add__', '__radd__', '__sub__', + '__rsub__'): raise TypeError("can only operate on a timedeltas for " "addition, subtraction, and division, but the" " operator [%s] was passed" % name) - # datetime and timedelta/DateOffset elif (self.is_datetime_lhs and (self.is_timedelta_rhs or self.is_offset_rhs)): if name not in ('__add__', '__radd__', '__sub__'): - raise TypeError("can only operate on a datetime with a rhs of" - " a timedelta/DateOffset for addition and subtraction," - " but the operator [%s] was passed" % - name) + raise TypeError("can only operate on a datetime with a rhs of " + "a timedelta/DateOffset for addition and " + "subtraction, but the operator [%s] was " + "passed" % name) elif (self.is_datetime_rhs and (self.is_timedelta_lhs or self.is_offset_lhs)): if name not in ('__add__', '__radd__', '__rsub__'): - raise TypeError("can only operate on a timedelta/DateOffset with a rhs of" - " a datetime for addition," - " but the operator [%s] was passed" % - name) - + raise TypeError("can only operate on a timedelta/DateOffset " + "with a rhs of a datetime for addition, " + "but the operator [%s] was passed" % name) # 2 datetimes elif self.is_datetime_lhs and self.is_datetime_rhs: - if name not in ('__sub__','__rsub__'): + if name not in ('__sub__', '__rsub__'): raise TypeError("can only operate on a datetimes for" " subtraction, but the operator [%s] was" " passed" % name) # if tz's must be equal (same or None) - if getattr(lvalues,'tz',None) != getattr(rvalues,'tz',None): - raise ValueError("Incompatbile tz's on datetime subtraction ops") + if getattr(lvalues, 'tz', None) != getattr(rvalues, 'tz', None): + raise ValueError("Incompatbile tz's on datetime subtraction " + "ops") - - elif ((self.is_timedelta_lhs or self.is_offset_lhs) - and self.is_datetime_rhs): + elif ((self.is_timedelta_lhs or self.is_offset_lhs) and + self.is_datetime_rhs): if name not in ('__add__', '__radd__'): - raise TypeError("can only operate on a timedelta/DateOffset and" - " a datetime for addition, but the operator" - " [%s] was passed" % name) + raise TypeError("can only operate on a timedelta/DateOffset " + "and a datetime for addition, but the " + "operator [%s] was passed" % name) else: raise TypeError('cannot operate on a series without a rhs ' 'of a series/ndarray of type datetime64[ns] ' @@ -389,18 +390,17 @@ def _convert_to_array(self, values, name=None, other=None): # if this is a Series that contains relevant dtype info, then use this # instead of the inferred type; this avoids coercing Series([NaT], # dtype='datetime64[ns]') to Series([NaT], dtype='timedelta64[ns]') - elif isinstance(values, pd.Series) and ( - is_timedelta64_dtype(values) or is_datetime64_dtype(values)): + elif (isinstance(values, pd.Series) and + (is_timedelta64_dtype(values) or is_datetime64_dtype(values))): supplied_dtype = values.dtype inferred_type = supplied_dtype or lib.infer_dtype(values) - if (inferred_type in ('datetime64', 'datetime', 'date', 'time') - or com.is_datetimetz(inferred_type)): + if (inferred_type in ('datetime64', 'datetime', 'date', 'time') or + com.is_datetimetz(inferred_type)): # if we have a other of timedelta, but use pd.NaT here we # we are in the wrong path - if (supplied_dtype is None - and other is not None - and (other.dtype in ('timedelta64[ns]', 'datetime64[ns]')) - and isnull(values).all()): + if (supplied_dtype is None and other is not None and + (other.dtype in ('timedelta64[ns]', 'datetime64[ns]')) and + isnull(values).all()): values = np.empty(values.shape, dtype='timedelta64[ns]') values[:] = iNaT @@ -408,7 +408,8 @@ def _convert_to_array(self, values, name=None, other=None): elif isinstance(values, pd.DatetimeIndex): values = values.to_series() # datetime with tz - elif isinstance(ovalues, datetime.datetime) and hasattr(ovalues,'tz'): + elif (isinstance(ovalues, datetime.datetime) and + hasattr(ovalues, 'tz')): values = pd.DatetimeIndex(values) # datetime array with tz elif com.is_datetimetz(values): @@ -430,8 +431,8 @@ def _convert_to_array(self, values, name=None, other=None): raise TypeError("incompatible type for a datetime/timedelta " "operation [{0}]".format(name)) elif inferred_type == 'floating': - if isnull(values).all() and name in ('__add__', '__radd__', - '__sub__', '__rsub__'): + if (isnull(values).all() and + name in ('__add__', '__radd__', '__sub__', '__rsub__')): values = np.empty(values.shape, dtype=other.dtype) values[:] = iNaT return values @@ -471,15 +472,14 @@ def _offset(lvalues, rvalues): rvalues = pd.DatetimeIndex(rvalues) lvalues = lvalues[0] else: - warnings.warn("Adding/subtracting array of DateOffsets to Series not vectorized", - PerformanceWarning) + warnings.warn("Adding/subtracting array of DateOffsets to " + "Series not vectorized", PerformanceWarning) rvalues = rvalues.astype('O') # pass thru on the na_op - self.na_op = lambda x, y: getattr(x,self.name)(y) + self.na_op = lambda x, y: getattr(x, self.name)(y) return lvalues, rvalues - if self.is_offset_lhs: lvalues, rvalues = _offset(lvalues, rvalues) elif self.is_offset_rhs: @@ -512,10 +512,9 @@ def _offset(lvalues, rvalues): # time delta division -> unit less # integer gets converted to timedelta in np < 1.6 - if (self.is_timedelta_lhs and self.is_timedelta_rhs) and\ - not self.is_integer_rhs and\ - not self.is_integer_lhs and\ - self.name in ('__div__', '__truediv__'): + if ((self.is_timedelta_lhs and self.is_timedelta_rhs) and + not self.is_integer_rhs and not self.is_integer_lhs and + self.name in ('__div__', '__truediv__')): self.dtype = 'float64' self.fill_value = np.nan lvalues = lvalues.astype(np.float64) @@ -523,6 +522,7 @@ def _offset(lvalues, rvalues): # if we need to mask the results if mask.any(): + def f(x): # datetime64[ns]/timedelta64[ns] masking @@ -533,11 +533,11 @@ def f(x): np.putmask(x, mask, self.fill_value) return x + self.wrap_results = f return lvalues, rvalues - def _is_offset(self, arr_or_obj): """ check if obj or all elements of list-like is DateOffset """ if isinstance(arr_or_obj, pd.DateOffset): @@ -559,7 +559,8 @@ def maybe_convert_for_time_op(cls, left, right, name, na_op): """ # decide if we can do it is_timedelta_lhs = is_timedelta64_dtype(left) - is_datetime_lhs = is_datetime64_dtype(left) or is_datetime64tz_dtype(left) + is_datetime_lhs = (is_datetime64_dtype(left) or + is_datetime64tz_dtype(left)) if not (is_datetime_lhs or is_timedelta_lhs): return None @@ -567,12 +568,13 @@ def maybe_convert_for_time_op(cls, left, right, name, na_op): return cls(left, right, name, na_op) -def _arith_method_SERIES(op, name, str_rep, fill_zeros=None, - default_axis=None, **eval_kwargs): +def _arith_method_SERIES(op, name, str_rep, fill_zeros=None, default_axis=None, + **eval_kwargs): """ Wrapper function for Series arithmetic operations, to avoid code duplication. """ + def na_op(x, y): try: result = expressions.evaluate(op, str_rep, x, y, @@ -588,7 +590,9 @@ def na_op(x, y): mask = notnull(x) result[mask] = op(x[mask], y) else: - raise TypeError("{typ} cannot perform the operation {op}".format(typ=type(x).__name__,op=str_rep)) + raise TypeError("{typ} cannot perform the operation " + "{op}".format(typ=type(x).__name__, + op=str_rep)) result, changed = com._maybe_upcast_putmask(result, ~mask, np.nan) @@ -600,7 +604,8 @@ def wrapper(left, right, name=name, na_op=na_op): if isinstance(right, pd.DataFrame): return NotImplemented - time_converted = _TimeOp.maybe_convert_for_time_op(left, right, name, na_op) + time_converted = _TimeOp.maybe_convert_for_time_op(left, right, name, + na_op) if time_converted is None: lvalues, rvalues = left, right @@ -616,7 +621,7 @@ def wrapper(left, right, name=name, na_op=na_op): na_op = time_converted.na_op if isinstance(rvalues, pd.Series): - rindex = getattr(rvalues,'index',rvalues) + rindex = getattr(rvalues, 'index', rvalues) name = _maybe_match_name(left, rvalues) lvalues = getattr(lvalues, 'values', lvalues) rvalues = getattr(rvalues, 'values', rvalues) @@ -624,7 +629,7 @@ def wrapper(left, right, name=name, na_op=na_op): index = left.index else: index, lidx, ridx = left.index.join(rindex, how='outer', - return_indexers=True) + return_indexers=True) if lidx is not None: lvalues = com.take_1d(lvalues, lidx) @@ -638,12 +643,14 @@ def wrapper(left, right, name=name, na_op=na_op): name=name, dtype=dtype) else: # scalars - if hasattr(lvalues, 'values') and not isinstance(lvalues, pd.DatetimeIndex): + if (hasattr(lvalues, 'values') and + not isinstance(lvalues, pd.DatetimeIndex)): lvalues = lvalues.values return left._constructor(wrap_results(na_op(lvalues, rvalues)), index=left.index, name=left.name, dtype=dtype) + return wrapper @@ -652,14 +659,15 @@ def _comp_method_SERIES(op, name, str_rep, masker=False): Wrapper function for Series arithmetic operations, to avoid code duplication. """ + def na_op(x, y): # dispatch to the categorical if we have a categorical # in either operand if is_categorical_dtype(x): - return op(x,y) + return op(x, y) elif is_categorical_dtype(y) and not isscalar(y): - return op(y,x) + return op(y, x) if is_object_dtype(x.dtype): if isinstance(y, list): @@ -691,10 +699,11 @@ def na_op(x, y): # we have a datetime/timedelta and may need to convert mask = None - if needs_i8_conversion(x) or (not isscalar(y) and needs_i8_conversion(y)): + if (needs_i8_conversion(x) or + (not isscalar(y) and needs_i8_conversion(y))): if isscalar(y): - y = _index.convert_scalar(x,_values_from_object(y)) + y = _index.convert_scalar(x, _values_from_object(y)) else: y = y.view('i8') @@ -734,15 +743,16 @@ def wrapper(self, other, axis=None): index=self.index).__finalize__(self) elif isinstance(other, pd.Categorical): if not is_categorical_dtype(self): - msg = "Cannot compare a Categorical for op {op} with Series of dtype {typ}.\n"\ - "If you want to compare values, use 'series np.asarray(other)'." - raise TypeError(msg.format(op=op,typ=self.dtype)) - + msg = ("Cannot compare a Categorical for op {op} with Series " + "of dtype {typ}.\nIf you want to compare values, use " + "'series np.asarray(other)'.") + raise TypeError(msg.format(op=op, typ=self.dtype)) if is_categorical_dtype(self): - # cats are a special case as get_values() would return an ndarray, which would then - # not take categories ordering into account - # we can go directly to op, as the na_op would just test again and dispatch to it. + # cats are a special case as get_values() would return an ndarray, + # which would then not take categories ordering into account + # we can go directly to op, as the na_op would just test again and + # dispatch to it. res = op(self.values, other) else: values = self.get_values() @@ -751,15 +761,15 @@ def wrapper(self, other, axis=None): res = na_op(values, other) if isscalar(res): - raise TypeError('Could not compare %s type with Series' - % type(other)) + raise TypeError('Could not compare %s type with Series' % + type(other)) # always return a full value series here res = _values_from_object(res) - res = pd.Series(res, index=self.index, name=self.name, - dtype='bool') + res = pd.Series(res, index=self.index, name=self.name, dtype='bool') return res + return wrapper @@ -768,6 +778,7 @@ def _bool_method_SERIES(op, name, str_rep): Wrapper function for Series arithmetic operations, to avoid code duplication. """ + def na_op(x, y): try: result = op(x, y) @@ -808,19 +819,21 @@ def wrapper(self, other): is_other_int_dtype = is_integer_dtype(other.dtype) other = fill_int(other) if is_other_int_dtype else fill_bool(other) - filler = fill_int if is_self_int_dtype and is_other_int_dtype else fill_bool + filler = (fill_int if is_self_int_dtype and is_other_int_dtype + else fill_bool) return filler(self._constructor(na_op(self.values, other.values), - index=self.index, - name=name)) + index=self.index, name=name)) elif isinstance(other, pd.DataFrame): return NotImplemented else: # scalars, list, tuple, np.array - filler = fill_int if is_self_int_dtype and is_integer_dtype(np.asarray(other)) else fill_bool - return filler(self._constructor(na_op(self.values, other), - index=self.index)).__finalize__(self) + filler = (fill_int if is_self_int_dtype and + is_integer_dtype(np.asarray(other)) else fill_bool) + return filler(self._constructor( + na_op(self.values, other), + index=self.index)).__finalize__(self) return wrapper @@ -835,13 +848,35 @@ def _radd_compat(left, right): return output -_op_descriptions = {'add': {'op': '+', 'desc': 'Addition', 'reversed': False, 'reverse': 'radd'}, - 'sub': {'op': '-', 'desc': 'Subtraction', 'reversed': False, 'reverse': 'rsub'}, - 'mul': {'op': '*', 'desc': 'Multiplication', 'reversed': False, 'reverse': 'rmul'}, - 'mod': {'op': '%', 'desc': 'Modulo', 'reversed': False, 'reverse': 'rmod'}, - 'pow': {'op': '**', 'desc': 'Exponential power', 'reversed': False, 'reverse': 'rpow'}, - 'truediv': {'op': '/', 'desc': 'Floating division', 'reversed': False, 'reverse': 'rtruediv'}, - 'floordiv': {'op': '//', 'desc': 'Integer division', 'reversed': False, 'reverse': 'rfloordiv'}} + +_op_descriptions = {'add': {'op': '+', + 'desc': 'Addition', + 'reversed': False, + 'reverse': 'radd'}, + 'sub': {'op': '-', + 'desc': 'Subtraction', + 'reversed': False, + 'reverse': 'rsub'}, + 'mul': {'op': '*', + 'desc': 'Multiplication', + 'reversed': False, + 'reverse': 'rmul'}, + 'mod': {'op': '%', + 'desc': 'Modulo', + 'reversed': False, + 'reverse': 'rmod'}, + 'pow': {'op': '**', + 'desc': 'Exponential power', + 'reversed': False, + 'reverse': 'rpow'}, + 'truediv': {'op': '/', + 'desc': 'Floating division', + 'reversed': False, + 'reverse': 'rtruediv'}, + 'floordiv': {'op': '//', + 'desc': 'Integer division', + 'reversed': False, + 'reverse': 'rfloordiv'}} _op_names = list(_op_descriptions.keys()) for k in _op_names: @@ -850,8 +885,9 @@ def _radd_compat(left, right): _op_descriptions[reverse_op]['reversed'] = True _op_descriptions[reverse_op]['reverse'] = k -def _flex_method_SERIES(op, name, str_rep, default_axis=None, - fill_zeros=None, **eval_kwargs): + +def _flex_method_SERIES(op, name, str_rep, default_axis=None, fill_zeros=None, + **eval_kwargs): op_name = name.replace('__', '') op_desc = _op_descriptions[op_name] if op_desc['reversed']: @@ -902,6 +938,7 @@ def flex_wrapper(self, other, level=None, fill_value=None, axis=0): flex_wrapper.__name__ = name return flex_wrapper + series_flex_funcs = dict(flex_arith_method=_flex_method_SERIES, radd_func=_radd_compat, flex_comp_method=_comp_method_SERIES) @@ -911,7 +948,6 @@ def flex_wrapper(self, other, level=None, fill_value=None, axis=0): comp_method=_comp_method_SERIES, bool_method=_bool_method_SERIES) - _arith_doc_FRAME = """ Binary operator %s with support to substitute a fill_value for missing data in one of the inputs @@ -942,8 +978,8 @@ def _arith_method_FRAME(op, name, str_rep=None, default_axis='columns', fill_zeros=None, **eval_kwargs): def na_op(x, y): try: - result = expressions.evaluate( - op, str_rep, x, y, raise_on_error=True, **eval_kwargs) + result = expressions.evaluate(op, str_rep, x, y, + raise_on_error=True, **eval_kwargs) except TypeError: xrav = x.ravel() if isinstance(y, (np.ndarray, pd.Series)): @@ -955,15 +991,16 @@ def na_op(x, y): yrav = yrav[mask] if np.prod(xrav.shape) and np.prod(yrav.shape): result[mask] = op(xrav, yrav) - elif hasattr(x,'size'): + elif hasattr(x, 'size'): result = np.empty(x.size, dtype=x.dtype) mask = notnull(xrav) xrav = xrav[mask] if np.prod(xrav.shape): result[mask] = op(xrav, y) else: - raise TypeError("cannot perform operation {op} between objects " - "of type {x} and {y}".format(op=name,x=type(x),y=type(y))) + raise TypeError("cannot perform operation {op} between " + "objects of type {x} and {y}".format( + op=name, x=type(x), y=type(y))) result, changed = com._maybe_upcast_putmask(result, ~mask, np.nan) result = result.reshape(x.shape) @@ -992,8 +1029,8 @@ def na_op(x, y): axis : {0, 1, 'index', 'columns'} For Series input, axis to match Series index on fill_value : None or float value, default None - Fill missing (NaN) values with this value. If both DataFrame locations are - missing, the result will be missing + Fill missing (NaN) values with this value. If both DataFrame + locations are missing, the result will be missing level : int or name Broadcast across a level, matching Index values on the passed MultiIndex level @@ -1015,7 +1052,7 @@ def na_op(x, y): @Appender(doc) def f(self, other, axis=default_axis, level=None, fill_value=None): - if isinstance(other, pd.DataFrame): # Another DataFrame + if isinstance(other, pd.DataFrame): # Another DataFrame return self._combine_frame(other, na_op, fill_value, level) elif isinstance(other, pd.Series): return self._combine_series(other, na_op, fill_value, axis, level) @@ -1038,8 +1075,8 @@ def f(self, other, axis=default_axis, level=None, fill_value=None): # casted = self._constructor_sliced(other, # index=self.columns) casted = pd.Series(other, index=self.columns) - return self._combine_series(casted, na_op, fill_value, - axis, level) + return self._combine_series(casted, na_op, fill_value, axis, + level) elif other.ndim == 2: # casted = self._constructor(other, index=self.index, # columns=self.columns) @@ -1060,7 +1097,6 @@ def f(self, other, axis=default_axis, level=None, fill_value=None): # Masker unused for now def _flex_comp_method_FRAME(op, name, str_rep=None, default_axis='columns', masker=False): - def na_op(x, y): try: result = op(x, y) @@ -1086,7 +1122,7 @@ def na_op(x, y): @Appender('Wrapper for flexible comparison methods %s' % name) def f(self, other, axis=default_axis, level=None): - if isinstance(other, pd.DataFrame): # Another DataFrame + if isinstance(other, pd.DataFrame): # Another DataFrame return self._flex_compare_frame(other, na_op, str_rep, level) elif isinstance(other, pd.Series): @@ -1130,7 +1166,7 @@ def f(self, other, axis=default_axis, level=None): def _comp_method_FRAME(func, name, str_rep, masker=False): @Appender('Wrapper for comparison method %s' % name) def f(self, other): - if isinstance(other, pd.DataFrame): # Another DataFrame + if isinstance(other, pd.DataFrame): # Another DataFrame return self._compare_frame(other, func, str_rep) elif isinstance(other, pd.Series): return self._combine_series_infer(other, func) @@ -1150,7 +1186,6 @@ def f(self, other): radd_func=_radd_compat, flex_comp_method=_flex_comp_method_FRAME) - frame_special_funcs = dict(arith_method=_arith_method_FRAME, radd_func=_radd_compat, comp_method=_comp_method_FRAME, @@ -1184,12 +1219,12 @@ def f(self, other): self._constructor.__name__) return self._combine(other, op) + f.__name__ = name return f def _comp_method_PANEL(op, name, str_rep=None, masker=False): - def na_op(x, y): try: result = expressions.evaluate(op, str_rep, x, y, diff --git a/pandas/core/panel.py b/pandas/core/panel.py index e0d9405a66b75..e0989574d79e1 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -13,8 +13,7 @@ import pandas.core.ops as ops from pandas import compat from pandas import lib -from pandas.compat import (map, zip, range, u, OrderedDict, - OrderedDefaultdict) +from pandas.compat import (map, zip, range, u, OrderedDict, OrderedDefaultdict) from pandas.core.categorical import Categorical from pandas.core.common import (PandasError, _try_sort, _default_index, _infer_dtype_from_scalar, is_list_like) @@ -36,7 +35,7 @@ klass="Panel", axes_single_arg="{0, 1, 2, 'items', 'major_axis', 'minor_axis'}") _shared_doc_kwargs['args_transpose'] = ("three positional arguments: each one" - "of\n %s" % + "of\n%s" % _shared_doc_kwargs['axes_single_arg']) @@ -171,7 +170,8 @@ def _init_data(self, data, copy, dtype, **kwargs): dtype, data = _infer_dtype_from_scalar(data) values = np.empty([len(x) for x in passed_axes], dtype=dtype) values.fill(data) - mgr = self._init_matrix(values, passed_axes, dtype=dtype, copy=False) + mgr = self._init_matrix(values, passed_axes, dtype=dtype, + copy=False) copy = False else: # pragma: no cover raise PandasError('Panel constructor not properly called!') @@ -184,8 +184,9 @@ def _init_dict(self, data, axes, dtype=None): # prefilter if haxis passed if haxis is not None: haxis = _ensure_index(haxis) - data = OrderedDict((k, v) for k, v - in compat.iteritems(data) if k in haxis) + data = OrderedDict((k, v) + for k, v in compat.iteritems(data) + if k in haxis) else: ks = list(data.keys()) if not isinstance(data, OrderedDict): @@ -197,8 +198,8 @@ def _init_dict(self, data, axes, dtype=None): data[k] = self._constructor_sliced(v) # 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 = [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) # shallow copy @@ -277,8 +278,7 @@ def _getitem_multilevel(self, key): if isinstance(loc, (slice, np.ndarray)): new_index = info[loc] result_index = maybe_droplevels(new_index, key) - slices = [loc] + [slice(None) for x in range( - self._AXIS_LEN - 1)] + 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:]) @@ -379,7 +379,8 @@ def _get_plane_axes(self, axis): (as compared with higher level planes), as we are returning a DataFrame axes """ - return [self._get_axis(axi) for axi in self._get_plane_axes_index(axis)] + return [self._get_axis(axi) + for axi in self._get_plane_axes_index(axis)] fromDict = from_dict @@ -400,8 +401,7 @@ def to_sparse(self, fill_value=None, kind='block'): frames = dict(compat.iteritems(self)) return SparsePanel(frames, items=self.items, major_axis=self.major_axis, - minor_axis=self.minor_axis, - default_kind=kind, + minor_axis=self.minor_axis, default_kind=kind, default_fill_value=fill_value) def to_excel(self, path, na_rep='', engine=None, **kwargs): @@ -544,8 +544,7 @@ def set_value(self, *args, **kwargs): result = self.reindex(**d) args = list(args) likely_dtype, args[-1] = _infer_dtype_from_scalar(args[-1]) - made_bigger = not np.array_equal( - axes[0], self._info_axis) + made_bigger = not np.array_equal(axes[0], self._info_axis) # how to make this logic simpler? if made_bigger: com._possibly_cast_item(result, args[0], likely_dtype) @@ -573,9 +572,9 @@ def __setitem__(self, key, value): mat = value.values elif isinstance(value, np.ndarray): if value.shape != shape[1:]: - raise ValueError( - 'shape of value must be {0}, shape of given object was ' - '{1}'.format(shape[1:], tuple(map(int, value.shape)))) + raise ValueError('shape of value must be {0}, shape of given ' + 'object was {1}'.format( + shape[1:], tuple(map(int, value.shape)))) mat = np.asarray(value) elif np.isscalar(value): dtype, value = _infer_dtype_from_scalar(value) @@ -624,24 +623,24 @@ def head(self, n=5): def tail(self, n=5): raise NotImplementedError - + def round(self, decimals=0): """ Round each value in Panel to a specified number of decimal places. - + .. versionadded:: 0.18.0 - + Parameters ---------- decimals : int - Number of decimal places to round to (default: 0). - If decimals is negative, it specifies the number of + Number of decimal places to round to (default: 0). + If decimals is negative, it specifies the number of positions to the left of the decimal point. - + Returns ------- Panel object - + See Also -------- numpy.around @@ -650,7 +649,6 @@ def round(self, decimals=0): result = np.apply_along_axis(np.round, 0, self.values) return self._wrap_result(result, axis=0) raise TypeError("decimals must be an integer") - def _needs_reindex_multi(self, axes, method, level): """ don't allow a multi reindex on Panel or above ndim """ @@ -708,9 +706,9 @@ def _combine(self, other, func, axis=0): elif np.isscalar(other): return self._combine_const(other, func) else: - raise NotImplementedError(str(type(other)) + - ' is not supported in combine operation with ' + - str(type(self))) + raise NotImplementedError("%s is not supported in combine " + "operation with %s" % + (str(type(other)), str(type(self)))) def _combine_const(self, other, func): new_values = func(self.values, other) @@ -768,8 +766,9 @@ def major_xs(self, key, copy=None): ----- major_xs is only for getting, not setting values. - MultiIndex Slicers is a generic way to get/set values on any level or levels - it is a superset of major_xs functionality, see :ref:`MultiIndex Slicers ` + MultiIndex Slicers is a generic way to get/set values on any level or + levels and is a superset of major_xs functionality, see + :ref:`MultiIndex Slicers ` """ if copy is not None: @@ -798,8 +797,9 @@ def minor_xs(self, key, copy=None): ----- minor_xs is only for getting, not setting values. - MultiIndex Slicers is a generic way to get/set values on any level or levels - it is a superset of minor_xs functionality, see :ref:`MultiIndex Slicers ` + MultiIndex Slicers is a generic way to get/set values on any level or + levels and is a superset of minor_xs functionality, see + :ref:`MultiIndex Slicers ` """ if copy is not None: @@ -828,8 +828,9 @@ def xs(self, key, axis=1, copy=None): ----- xs is only for getting, not setting values. - MultiIndex Slicers is a generic way to get/set values on any level or levels - it is a superset of xs functionality, see :ref:`MultiIndex Slicers ` + MultiIndex Slicers is a generic way to get/set values on any level or + levels and is a superset of xs functionality, see + :ref:`MultiIndex Slicers ` """ if copy is not None: @@ -860,7 +861,8 @@ def _ixs(self, i, axis=0): key = ax[i] # xs cannot handle a non-scalar key, so just reindex here - # if we have a multi-index and a single tuple, then its a reduction (GH 7516) + # if we have a multi-index and a single tuple, then its a reduction + # (GH 7516) if not (isinstance(ax, MultiIndex) and isinstance(key, tuple)): if is_list_like(key): indexer = {self._get_axis_name(axis): key} @@ -962,8 +964,8 @@ def construct_index_parts(idx, major=True): labels = major_labels + minor_labels names = major_names + minor_names - index = MultiIndex(levels=levels, labels=labels, - names=names, verify_integrity=False) + index = MultiIndex(levels=levels, labels=labels, names=names, + verify_integrity=False) return DataFrame(data, index=index, columns=self.items) @@ -979,9 +981,10 @@ def apply(self, func, axis='major', **kwargs): func : function Function to apply to each combination of 'other' axes e.g. if axis = 'items', the combination of major_axis/minor_axis - will each be passed as a Series; if axis = ('items', 'major'), DataFrames - of items & major axis will be passed - axis : {'items', 'minor', 'major'}, or {0, 1, 2}, or a tuple with two axes + will each be passed as a Series; if axis = ('items', 'major'), + DataFrames of items & major axis will be passed + axis : {'items', 'minor', 'major'}, or {0, 1, 2}, or a tuple with two + axes Additional keyword arguments will be passed as keywords to the function Examples @@ -1000,7 +1003,8 @@ def apply(self, func, axis='major', **kwargs): >>> p.apply(lambda x: x.sum(), axis='minor') - Return the shapes of each DataFrame over axis 2 (i.e the shapes of items x major), as a Series + Return the shapes of each DataFrame over axis 2 (i.e the shapes of + items x major), as a Series >>> p.apply(lambda x: x.shape, axis=(0,1)) @@ -1034,7 +1038,6 @@ def apply(self, func, axis='major', **kwargs): def _apply_1d(self, func, axis): axis_name = self._get_axis_name(axis) - ax = self._get_axis(axis) ndim = self.ndim values = self.values @@ -1119,8 +1122,8 @@ def _apply_2d(self, func, axis): def _reduce(self, op, name, axis=0, skipna=True, numeric_only=None, filter_type=None, **kwds): if numeric_only: - raise NotImplementedError( - 'Panel.{0} does not implement numeric_only.'.format(name)) + raise NotImplementedError('Panel.{0} does not implement ' + 'numeric_only.'.format(name)) axis_name = self._get_axis_name(axis) axis_number = self._get_axis_number(axis_name) @@ -1153,7 +1156,7 @@ def _construct_return_type(self, result, axes=None): # same as self elif self.ndim == ndim: - """ return the construction dictionary for these axes """ + # return the construction dictionary for these axes if axes is None: return self._constructor(result) return self._constructor(result, **self._construct_axes_dict()) @@ -1178,19 +1181,19 @@ def _wrap_result(self, result, axis): @Appender(_shared_docs['reindex'] % _shared_doc_kwargs) def reindex(self, items=None, major_axis=None, minor_axis=None, **kwargs): - major_axis = (major_axis if major_axis is not None - else kwargs.pop('major', None)) - minor_axis = (minor_axis if minor_axis is not None - else kwargs.pop('minor', None)) + major_axis = (major_axis if major_axis is not None else + kwargs.pop('major', None)) + minor_axis = (minor_axis if minor_axis is not None else + kwargs.pop('minor', None)) return super(Panel, self).reindex(items=items, major_axis=major_axis, minor_axis=minor_axis, **kwargs) @Appender(_shared_docs['rename'] % _shared_doc_kwargs) def rename(self, items=None, major_axis=None, minor_axis=None, **kwargs): - major_axis = (major_axis if major_axis is not None - else kwargs.pop('major', None)) - minor_axis = (minor_axis if minor_axis is not None - else kwargs.pop('minor', None)) + major_axis = (major_axis if major_axis is not None else + kwargs.pop('major', None)) + minor_axis = (minor_axis if minor_axis is not None else + kwargs.pop('minor', None)) return super(Panel, self).rename(items=items, major_axis=major_axis, minor_axis=minor_axis, **kwargs) @@ -1209,10 +1212,9 @@ def transpose(self, *args, **kwargs): @Appender(_shared_docs['fillna'] % _shared_doc_kwargs) def fillna(self, value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs): - return super(Panel, self).fillna(value=value, method=method, - axis=axis, inplace=inplace, - limit=limit, downcast=downcast, - **kwargs) + return super(Panel, self).fillna(value=value, method=method, axis=axis, + inplace=inplace, limit=limit, + downcast=downcast, **kwargs) def count(self, axis='major'): """ @@ -1359,15 +1361,16 @@ def _get_join_index(self, other, how): @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)] + 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)]) + for i, a in zip( + self._AXIS_ORDERS[self._AXIS_LEN - len(axes):], + axes)]) @staticmethod def _prep_ndarray(self, values, copy=True): @@ -1519,7 +1522,8 @@ def na_op(x, y): Parameters ---------- - other : %s or %s""" % (cls._constructor_sliced.__name__, cls.__name__) + """ + other : %s or %s""" % (cls._constructor_sliced.__name__, + cls.__name__) + """ axis : {""" + ', '.join(cls._AXIS_ORDERS) + "}" + """ Axis to broadcast over @@ -1530,7 +1534,8 @@ def na_op(x, y): See also -------- """ + cls.__name__ + ".%s\n" - doc = _op_doc % (op_desc['desc'], op_name, equiv, op_desc['reverse']) + doc = _op_doc % (op_desc['desc'], op_name, equiv, + op_desc['reverse']) else: doc = _agg_doc % name @@ -1547,11 +1552,9 @@ def f(self, other, axis=0): flex_comp_method=ops._comp_method_PANEL) -Panel._setup_axes(axes=['items', 'major_axis', 'minor_axis'], - info_axis=0, - stat_axis=1, - aliases={'major': 'major_axis', - 'minor': 'minor_axis'}, +Panel._setup_axes(axes=['items', 'major_axis', 'minor_axis'], info_axis=0, + stat_axis=1, aliases={'major': 'major_axis', + 'minor': 'minor_axis'}, slicers={'major_axis': 'index', 'minor_axis': 'columns'}) @@ -1559,6 +1562,7 @@ def f(self, other, axis=0): Panel._add_aggregate_operations() Panel._add_numeric_operations() + # legacy class WidePanel(Panel): def __init__(self, *args, **kwargs): diff --git a/pandas/core/panel4d.py b/pandas/core/panel4d.py index 7fafbd0eaa2b5..33bd79195cc77 100644 --- a/pandas/core/panel4d.py +++ b/pandas/core/panel4d.py @@ -3,17 +3,19 @@ from pandas.core.panelnd import create_nd_panel_factory from pandas.core.panel import Panel -Panel4D = create_nd_panel_factory( - klass_name='Panel4D', - orders=['labels', 'items', 'major_axis', 'minor_axis'], - slices={'labels': 'labels', 'items': 'items', 'major_axis': 'major_axis', - 'minor_axis': 'minor_axis'}, - slicer=Panel, - aliases={'major': 'major_axis', 'minor': 'minor_axis'}, - stat_axis=2, - ns=dict(__doc__=""" - Panel4D is a 4-Dimensional named container very much like a Panel, but - having 4 named dimensions. It is intended as a test bed for more +Panel4D = create_nd_panel_factory(klass_name='Panel4D', + orders=['labels', 'items', 'major_axis', + 'minor_axis'], + slices={'labels': 'labels', + 'items': 'items', + 'major_axis': 'major_axis', + 'minor_axis': 'minor_axis'}, + slicer=Panel, + aliases={'major': 'major_axis', + 'minor': 'minor_axis'}, stat_axis=2, + ns=dict(__doc__=""" + Panel4D is a 4-Dimensional named container very much like a Panel, but + having 4 named dimensions. It is intended as a test bed for more N-Dimensional named containers. Parameters @@ -29,15 +31,15 @@ Data type to force, otherwise infer copy : boolean, default False Copy data from inputs. Only affects DataFrame / 2d ndarray input - """) -) + """)) def panel4d_init(self, data=None, labels=None, items=None, major_axis=None, minor_axis=None, copy=False, dtype=None): self._init_data(data=data, labels=labels, items=items, - major_axis=major_axis, minor_axis=minor_axis, - copy=copy, dtype=dtype) + major_axis=major_axis, minor_axis=minor_axis, copy=copy, + dtype=dtype) + Panel4D.__init__ = panel4d_init diff --git a/pandas/core/panelnd.py b/pandas/core/panelnd.py index 35e6412efc760..04fbaab30b42e 100644 --- a/pandas/core/panelnd.py +++ b/pandas/core/panelnd.py @@ -8,21 +8,20 @@ def create_nd_panel_factory(klass_name, orders, slices, slicer, aliases=None, stat_axis=2, info_axis=0, ns=None): """ manufacture a n-d class: - Parameters - ---------- - klass_name : the klass name - orders : the names of the axes in order (highest to lowest) - slices : a dictionary that defines how the axes map to the slice axis - slicer : the class representing a slice of this panel - aliases : a dictionary defining aliases for various axes - default = { major : major_axis, minor : minor_axis } - stat_axis : the default statistic axis default = 2 - info_axis : the info axis - - Returns - ------- - a class object representing this panel - + Parameters + ---------- + klass_name : the klass name + orders : the names of the axes in order (highest to lowest) + slices : a dictionary that defines how the axes map to the slice axis + slicer : the class representing a slice of this panel + aliases : a dictionary defining aliases for various axes + default = { major : major_axis, minor : minor_axis } + stat_axis : the default statistic axis default = 2 + info_axis : the info axis + + Returns + ------- + a class object representing this panel """ # if slicer is a name, get the object @@ -35,7 +34,7 @@ def create_nd_panel_factory(klass_name, orders, slices, slicer, aliases=None, # build the klass ns = {} if not ns else ns - klass = type(klass_name, (slicer,), ns) + klass = type(klass_name, (slicer, ), ns) # setup the axes klass._setup_axes(axes=orders, info_axis=info_axis, stat_axis=stat_axis, @@ -46,19 +45,21 @@ def create_nd_panel_factory(klass_name, orders, slices, slicer, aliases=None, # define the methods #### 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) + 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__ def _get_plane_axes_index(self, axis): """ return the sliced index for this object """ - axis_name = self._get_axis_name(axis) + # TODO: axis_name is not used, remove? + axis_name = self._get_axis_name(axis) # noqa index = self._AXIS_ORDERS.index(axis) planes = [] @@ -68,12 +69,14 @@ def _get_plane_axes_index(self, axis): planes.extend(self._AXIS_ORDERS[index + 1:]) return planes + klass._get_plane_axes_index = _get_plane_axes_index def _combine(self, other, func, axis=0): if isinstance(other, klass): return self._combine_with_constructor(other, func) return super(klass, self)._combine(other, func, axis=axis) + klass._combine = _combine def _combine_with_constructor(self, other, func): @@ -92,13 +95,16 @@ def _combine_with_constructor(self, other, func): result_values = func(this.values, other.values) return self._constructor(result_values, **d) + klass._combine_with_constructor = _combine_with_constructor # set as NonImplemented operations which we don't support for f in ['to_frame', 'to_excel', 'to_sparse', 'groupby', 'join', 'filter', 'dropna', 'shift']: + def func(self, *args, **kwargs): raise NotImplementedError("this operation is not supported") + setattr(klass, f, func) # add the aggregate operations