Skip to content
Closed
66 changes: 58 additions & 8 deletions doc/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -248,12 +248,30 @@ Binary operator functions
:toctree: generated/

Series.add
Series.div
Series.mul
Series.sub
Series.mul
Series.div
Series.truediv
Series.floordiv
Series.mod
Series.pow
Series.radd
Series.rsub
Series.rmul
Series.rdiv
Series.rtruediv
Series.rfloordiv
Series.rmod
Series.rpow
Series.combine
Series.combine_first
Series.round
Series.lt
Series.gt
Series.le
Series.ge
Series.ne
Series.eq

Function application, GroupBy
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -451,13 +469,27 @@ Binary operator functions
:toctree: generated/

DataFrame.add
DataFrame.div
DataFrame.mul
DataFrame.sub
DataFrame.mul
DataFrame.div
DataFrame.truediv
DataFrame.floordiv
DataFrame.mod
DataFrame.pow
DataFrame.radd
DataFrame.rdiv
DataFrame.rmul
DataFrame.rsub
DataFrame.rmul
DataFrame.rdiv
DataFrame.rtruediv
DataFrame.rfloordiv
DataFrame.rmod
DataFrame.rpow
DataFrame.lt
DataFrame.gt
DataFrame.le
DataFrame.ge
DataFrame.ne
DataFrame.eq
DataFrame.combine
DataFrame.combineAdd
DataFrame.combine_first
Expand Down Expand Up @@ -680,9 +712,27 @@ Binary operator functions
:toctree: generated/

Panel.add
Panel.div
Panel.mul
Panel.sub
Panel.mul
Panel.div
Panel.truediv
Panel.floordiv
Panel.mod
Panel.pow
Panel.radd
Panel.rsub
Panel.rmul
Panel.rdiv
Panel.rtruediv
Panel.rfloordiv
Panel.rmod
Panel.rpow
Panel.lt
Panel.gt
Panel.le
Panel.ge
Panel.ne
Panel.eq

Function application, GroupBy
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
3 changes: 3 additions & 0 deletions doc/source/release.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ pandas 0.12
- Access to historical Google Finance data in pandas.io.data (:issue:`3814`)
- DataFrame plotting methods can sample column colors from a Matplotlib
colormap via the ``colormap`` keyword. (:issue:`3860`)
- All non-Index NDFrames (``Series``, ``DataFrame``, ``Panel``, ``Panel4D``,
``SparsePanel``, etc.), now support the entire set of arithmetic operators
and arithmetic flex methods (add, sub, mul, pow, etc.). (:issue:`3765`)

**Improvements to existing features**

Expand Down
4 changes: 4 additions & 0 deletions doc/source/v0.12.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ API changes
p / p
p / 0

- All non-Index NDFrames (``Series``, ``DataFrame``, ``Panel``, ``Panel4D``,
``SparsePanel``, etc.), now support the entire set of arithmetic operators
and arithmetic flex methods (add, sub, mul, pow, etc.). (:issue:`3765`)

- Add ``squeeze`` keyword to ``groupby`` to allow reduction from
DataFrame -> Series if groups are unique. This is a Regression from 0.10.1.
We are reverting back to the prior behavior. This means groupby will return the
Expand Down
26 changes: 25 additions & 1 deletion pandas/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import itertools
import re
from datetime import datetime
import types

from numpy.lib.format import read_array, write_array
import numpy as np
Expand Down Expand Up @@ -38,6 +38,30 @@ class AmbiguousIndexError(PandasError, KeyError):
pass


def bind_method(cls, name, func):
"""Bind a method to class, python 2 and python 3 compatible.

Parameters
----------

cls : type
class to receive bound method
name : basestring
name of method on class instance
func : function
function to be bound as method


Returns
-------
None
"""
# only python 2 has bound/unbound method issue
if not py3compat.PY3:
setattr(cls, name, types.MethodType(func, None, cls))
else:
setattr(cls, name, func)

_POSSIBLY_CAST_DTYPES = set([ np.dtype(t) for t in ['M8[ns]','m8[ns]','O','int8','uint8','int16','uint16','int32','uint32','int64','uint64'] ])
_NS_DTYPE = np.dtype('M8[ns]')
_TD_DTYPE = np.dtype('m8[ns]')
Expand Down
70 changes: 45 additions & 25 deletions pandas/core/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
except ImportError: # pragma: no cover
_NUMEXPR_INSTALLED = False

_TEST_MODE = None
_TEST_RESULT = None
_USE_NUMEXPR = _NUMEXPR_INSTALLED
_evaluate = None
_where = None
Expand Down Expand Up @@ -53,12 +55,14 @@ def set_numexpr_threads(n = None):

def _evaluate_standard(op, op_str, a, b, raise_on_error=True, **eval_kwargs):
""" standard evaluation """
if _TEST_MODE:
_store_test_result(False)
return op(a,b)

def _can_use_numexpr(op, op_str, a, b, dtype_check):
""" return a boolean if we WILL be using numexpr """
if op_str is not None:

# required min elements (otherwise we are adding overhead)
if np.prod(a.shape) > _MIN_ELEMENTS:

Expand All @@ -81,31 +85,30 @@ def _can_use_numexpr(op, op_str, a, b, dtype_check):

def _evaluate_numexpr(op, op_str, a, b, raise_on_error = False, **eval_kwargs):
result = None

if _can_use_numexpr(op, op_str, a, b, 'evaluate'):
try:
a_value, b_value = a, b
if hasattr(a_value,'values'):
a_value = a_value.values
if hasattr(b_value,'values'):
b_value = b_value.values
result = ne.evaluate('a_value %s b_value' % op_str,
local_dict={ 'a_value' : a_value,
'b_value' : b_value },
a_value = getattr(a, "values", a)
b_value = getattr(b, "values", b)
result = ne.evaluate('a_value %s b_value' % op_str,
local_dict={ 'a_value' : a_value,
'b_value' : b_value },
casting='safe', **eval_kwargs)
except (ValueError), detail:
except ValueError as detail:
if 'unknown type object' in str(detail):
pass
except (Exception), detail:
except Exception as detail:
if raise_on_error:
raise TypeError(str(detail))

if _TEST_MODE:
_store_test_result(result is not None)

if result is None:
result = _evaluate_standard(op,op_str,a,b,raise_on_error)

return result

def _where_standard(cond, a, b, raise_on_error=True):
def _where_standard(cond, a, b, raise_on_error=True):
return np.where(cond, a, b)

def _where_numexpr(cond, a, b, raise_on_error = False):
Expand All @@ -114,22 +117,18 @@ def _where_numexpr(cond, a, b, raise_on_error = False):
if _can_use_numexpr(None, 'where', a, b, 'where'):

try:
cond_value, a_value, b_value = cond, a, b
if hasattr(cond_value,'values'):
cond_value = cond_value.values
if hasattr(a_value,'values'):
a_value = a_value.values
if hasattr(b_value,'values'):
b_value = b_value.values
cond_value = getattr(cond, 'values', cond)
a_value = getattr(a, 'values', a)
b_value = getattr(b, 'values', b)
result = ne.evaluate('where(cond_value,a_value,b_value)',
local_dict={ 'cond_value' : cond_value,
'a_value' : a_value,
'b_value' : b_value },
'a_value' : a_value,
'b_value' : b_value },
casting='safe')
except (ValueError), detail:
except ValueError as detail:
if 'unknown type object' in str(detail):
pass
except (Exception), detail:
except Exception as detail:
if raise_on_error:
raise TypeError(str(detail))

Expand All @@ -156,7 +155,6 @@ def evaluate(op, op_str, a, b, raise_on_error=False, use_numexpr=True, **eval_kw
otherwise evaluate the op with and return the results
use_numexpr : whether to try to use numexpr (default True)
"""

if use_numexpr:
return _evaluate(op, op_str, a, b, raise_on_error=raise_on_error, **eval_kwargs)
return _evaluate_standard(op, op_str, a, b, raise_on_error=raise_on_error)
Expand All @@ -178,3 +176,25 @@ def where(cond, a, b, raise_on_error=False, use_numexpr=True):
if use_numexpr:
return _where(cond, a, b, raise_on_error=raise_on_error)
return _where_standard(cond, a, b, raise_on_error=raise_on_error)

def set_test_mode(v = True):
"""
Keeps track of whether numexpr was used. Stores an additional ``True`` for
every successful use of evaluate with numexpr since the last
``get_test_result``
"""
global _TEST_MODE, _TEST_RESULT
_TEST_MODE = v
_TEST_RESULT = []

def _store_test_result(used_numexpr):
global _TEST_RESULT
if used_numexpr:
_TEST_RESULT.append(used_numexpr)

def get_test_result():
"""get test result and reset test_results"""
global _TEST_RESULT
res = _TEST_RESULT
_TEST_RESULT = []
return res
Loading