Skip to content

Commit 047e64c

Browse files
committed
reduce redirection in ops
1 parent 324379c commit 047e64c

File tree

4 files changed

+155
-79
lines changed

4 files changed

+155
-79
lines changed

pandas/core/ops.py

+152-71
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,78 @@ def _get_frame_op_default_axis(name):
188188
return 'columns'
189189

190190

191+
def _get_opstr(op, cls):
192+
"""
193+
Find the operation string, if any, to pass to numexpr for this
194+
operation.
195+
196+
Parameters
197+
----------
198+
op : binary operator
199+
cls : class
200+
201+
Returns
202+
-------
203+
op_str : string or None
204+
"""
205+
# numexpr is available for non-sparse classes
206+
subtyp = getattr(cls, '_subtyp', '')
207+
use_numexpr = 'sparse' not in subtyp
208+
209+
if not use_numexpr:
210+
# if we're not using numexpr, then don't pass a str_rep
211+
return None
212+
213+
return {operator.add: '+',
214+
radd: '+',
215+
operator.mul: '*',
216+
rmul: '*',
217+
operator.sub: '-',
218+
rsub: '-',
219+
operator.truediv: '/',
220+
rtruediv: '/',
221+
operator.floordiv: '//',
222+
rfloordiv: '//',
223+
operator.mod: None, # TODO: Why None for mod but '%' for rmod?
224+
rmod: '%',
225+
operator.pow: '**',
226+
rpow: '**',
227+
operator.eq: '==',
228+
operator.ne: '!=',
229+
operator.le: '<=',
230+
operator.lt: '<',
231+
operator.ge: '>=',
232+
operator.gt: '>',
233+
operator.and_: '&',
234+
rand_: '&',
235+
operator.or_: '|',
236+
ror_: '|',
237+
operator.xor: '^',
238+
rxor: '^',
239+
divmod: None,
240+
rdivmod: None}[op]
241+
242+
243+
def _get_op_name(op, special):
244+
"""
245+
Find the name to attach to this method according to conventions
246+
for special and non-special methods.
247+
248+
Parameters
249+
----------
250+
op : binary operator
251+
special : bool
252+
253+
Returns
254+
-------
255+
op_name : str
256+
"""
257+
opname = op.__name__.strip('_')
258+
if special:
259+
opname = '__{opname}__'.format(opname=opname)
260+
return opname
261+
262+
191263
# -----------------------------------------------------------------------------
192264
# Docstring Generation and Templates
193265

@@ -408,75 +480,56 @@ def _create_methods(cls, arith_method, comp_method, bool_method,
408480
# creates actual methods based upon arithmetic, comp and bool method
409481
# constructors.
410482

411-
# numexpr is available for non-sparse classes
412-
subtyp = getattr(cls, '_subtyp', '')
413-
use_numexpr = 'sparse' not in subtyp
414-
415483
have_divmod = issubclass(cls, ABCSeries)
416484
# divmod is available for Series and SparseSeries
417485

418-
# if we're not using numexpr, then don't pass a str_rep
419-
if use_numexpr:
420-
op = lambda x: x
421-
else:
422-
op = lambda x: None
423-
if special:
424-
425-
def names(x):
426-
if x[-1] == "_":
427-
return "__{name}_".format(name=x)
428-
else:
429-
return "__{name}__".format(name=x)
430-
else:
431-
names = lambda x: x
432-
433486
# yapf: disable
434487
new_methods = dict(
435-
add=arith_method(operator.add, names('add'), op('+')),
436-
radd=arith_method(radd, names('radd'), op('+')),
437-
sub=arith_method(operator.sub, names('sub'), op('-')),
438-
mul=arith_method(operator.mul, names('mul'), op('*')),
439-
truediv=arith_method(operator.truediv, names('truediv'), op('/')),
440-
floordiv=arith_method(operator.floordiv, names('floordiv'), op('//')),
488+
add=arith_method(cls, operator.add, special),
489+
radd=arith_method(cls, radd, special),
490+
sub=arith_method(cls, operator.sub, special),
491+
mul=arith_method(cls, operator.mul, special),
492+
truediv=arith_method(cls, operator.truediv, special),
493+
floordiv=arith_method(cls, operator.floordiv, special),
441494
# Causes a floating point exception in the tests when numexpr enabled,
442495
# so for now no speedup
443-
mod=arith_method(operator.mod, names('mod'), None),
444-
pow=arith_method(operator.pow, names('pow'), op('**')),
496+
mod=arith_method(cls, operator.mod, special),
497+
pow=arith_method(cls, operator.pow, special),
445498
# not entirely sure why this is necessary, but previously was included
446499
# so it's here to maintain compatibility
447-
rmul=arith_method(operator.mul, names('rmul'), op('*')),
448-
rsub=arith_method(rsub, names('rsub'), op('-')),
449-
rtruediv=arith_method(rtruediv, names('rtruediv'), op('/')),
450-
rfloordiv=arith_method(rfloordiv, names('rfloordiv'), op('//')),
451-
rpow=arith_method(rpow, names('rpow'), op('**')),
452-
rmod=arith_method(rmod, names('rmod'), op('%')))
500+
rmul=arith_method(cls, rmul, special),
501+
rsub=arith_method(cls, rsub, special),
502+
rtruediv=arith_method(cls, rtruediv, special),
503+
rfloordiv=arith_method(cls, rfloordiv, special),
504+
rpow=arith_method(cls, rpow, special),
505+
rmod=arith_method(cls, rmod, special))
453506
# yapf: enable
454507
new_methods['div'] = new_methods['truediv']
455508
new_methods['rdiv'] = new_methods['rtruediv']
456509

457510
# Comp methods never had a default axis set
458511
if comp_method:
459512
new_methods.update(dict(
460-
eq=comp_method(operator.eq, names('eq'), op('==')),
461-
ne=comp_method(operator.ne, names('ne'), op('!=')),
462-
lt=comp_method(operator.lt, names('lt'), op('<')),
463-
gt=comp_method(operator.gt, names('gt'), op('>')),
464-
le=comp_method(operator.le, names('le'), op('<=')),
465-
ge=comp_method(operator.ge, names('ge'), op('>='))))
513+
eq=comp_method(cls, operator.eq, special),
514+
ne=comp_method(cls, operator.ne, special),
515+
lt=comp_method(cls, operator.lt, special),
516+
gt=comp_method(cls, operator.gt, special),
517+
le=comp_method(cls, operator.le, special),
518+
ge=comp_method(cls, operator.ge, special)))
466519
if bool_method:
467520
new_methods.update(
468-
dict(and_=bool_method(operator.and_, names('and_'), op('&')),
469-
or_=bool_method(operator.or_, names('or_'), op('|')),
521+
dict(and_=bool_method(cls, operator.and_, special),
522+
or_=bool_method(cls, operator.or_, special),
470523
# For some reason ``^`` wasn't used in original.
471-
xor=bool_method(operator.xor, names('xor'), op('^')),
472-
rand_=bool_method(rand_, names('rand_'), op('&')),
473-
ror_=bool_method(ror_, names('ror_'), op('|')),
474-
rxor=bool_method(rxor, names('rxor'), op('^'))))
524+
xor=bool_method(cls, operator.xor, special),
525+
rand_=bool_method(cls, rand_, special),
526+
ror_=bool_method(cls, ror_, special),
527+
rxor=bool_method(cls, rxor, special)))
475528
if have_divmod:
476529
# divmod doesn't have an op that is supported by numexpr
477-
new_methods['divmod'] = arith_method(divmod, names('divmod'), None)
530+
new_methods['divmod'] = arith_method(cls, divmod, special)
478531

479-
new_methods = {names(k): v for k, v in new_methods.items()}
532+
new_methods = {v.__name__: v for k, v in new_methods.items()}
480533
return new_methods
481534

482535

@@ -503,12 +556,12 @@ def add_special_arithmetic_methods(cls, arith_method=None,
503556
Parameters
504557
----------
505558
arith_method : function (optional)
506-
factory for special arithmetic methods, with op string:
507-
f(op, name, str_rep)
559+
factory for special arithmetic methods:
560+
f(cls, op, special)
508561
comp_method : function (optional)
509-
factory for rich comparison - signature: f(op, name, str_rep)
562+
factory for rich comparison - signature: f(cls, op, special)
510563
bool_method : function (optional)
511-
factory for boolean methods - signature: f(op, name, str_rep)
564+
factory for boolean methods - signature: f(cls, op, special)
512565
"""
513566
new_methods = _create_methods(cls, arith_method, comp_method, bool_method,
514567
special=True)
@@ -552,22 +605,21 @@ def f(self, other):
552605
add_methods(cls, new_methods=new_methods)
553606

554607

555-
def add_flex_arithmetic_methods(cls, flex_arith_method,
556-
flex_comp_method=None, flex_bool_method=None):
608+
def add_flex_arithmetic_methods(cls, flex_arith_method, flex_comp_method=None):
557609
"""
558610
Adds the full suite of flex arithmetic methods (``pow``, ``mul``, ``add``)
559611
to the class.
560612
561613
Parameters
562614
----------
563615
flex_arith_method : function
564-
factory for flex arithmetic methods, with op string:
565-
f(op, name, str_rep)
616+
factory for flex arithmetic methods:
617+
f(cls, op, special)
566618
flex_comp_method : function, optional,
567-
factory for rich comparison - signature: f(op, name, str_rep)
619+
factory for rich comparison - signature: f(cls, op, special)
568620
"""
569621
new_methods = _create_methods(cls, flex_arith_method,
570-
flex_comp_method, flex_bool_method,
622+
flex_comp_method, bool_method=None,
571623
special=False)
572624
new_methods.update(dict(multiply=new_methods['mul'],
573625
subtract=new_methods['sub'],
@@ -626,11 +678,13 @@ def _construct_divmod_result(left, result, index, name, dtype):
626678
)
627679

628680

629-
def _arith_method_SERIES(op, name, str_rep):
681+
def _arith_method_SERIES(cls, op, special):
630682
"""
631683
Wrapper function for Series arithmetic operations, to avoid
632684
code duplication.
633685
"""
686+
str_rep = _get_opstr(op, cls)
687+
name = _get_op_name(op, special)
634688
eval_kwargs = _gen_eval_kwargs(name)
635689
fill_zeros = _gen_fill_zeros(name)
636690
construct_result = (_construct_divmod_result
@@ -763,11 +817,12 @@ def _comp_method_OBJECT_ARRAY(op, x, y):
763817
return result
764818

765819

766-
def _comp_method_SERIES(op, name, str_rep):
820+
def _comp_method_SERIES(cls, op, special):
767821
"""
768822
Wrapper function for Series arithmetic operations, to avoid
769823
code duplication.
770824
"""
825+
name = _get_op_name(op, special)
771826
masker = _gen_eval_kwargs(name).get('masker', False)
772827

773828
def na_op(x, y):
@@ -890,7 +945,7 @@ def wrapper(self, other, axis=None):
890945
return wrapper
891946

892947

893-
def _bool_method_SERIES(op, name, str_rep):
948+
def _bool_method_SERIES(cls, op, special):
894949
"""
895950
Wrapper function for Series arithmetic operations, to avoid
896951
code duplication.
@@ -961,7 +1016,8 @@ def wrapper(self, other):
9611016
return wrapper
9621017

9631018

964-
def _flex_method_SERIES(op, name, str_rep):
1019+
def _flex_method_SERIES(cls, op, special):
1020+
name = _get_op_name(op, special)
9651021
doc = _make_flex_doc(name, 'series')
9661022

9671023
@Appender(doc)
@@ -1042,7 +1098,9 @@ def to_series(right):
10421098
return right
10431099

10441100

1045-
def _arith_method_FRAME(op, name, str_rep=None):
1101+
def _arith_method_FRAME(cls, op, special):
1102+
str_rep = _get_opstr(op, cls)
1103+
name = _get_op_name(op, special)
10461104
eval_kwargs = _gen_eval_kwargs(name)
10471105
fill_zeros = _gen_fill_zeros(name)
10481106
default_axis = _get_frame_op_default_axis(name)
@@ -1119,7 +1177,9 @@ def f(self, other, axis=default_axis, level=None, fill_value=None):
11191177
return f
11201178

11211179

1122-
def _flex_comp_method_FRAME(op, name, str_rep=None):
1180+
def _flex_comp_method_FRAME(cls, op, special):
1181+
str_rep = _get_opstr(op, cls)
1182+
name = _get_op_name(op, special)
11231183
default_axis = _get_frame_op_default_axis(name)
11241184

11251185
def na_op(x, y):
@@ -1167,7 +1227,10 @@ def f(self, other, axis=default_axis, level=None):
11671227
return f
11681228

11691229

1170-
def _comp_method_FRAME(func, name, str_rep):
1230+
def _comp_method_FRAME(cls, func, special):
1231+
str_rep = _get_opstr(func, cls)
1232+
name = _get_op_name(func, special)
1233+
11711234
@Appender('Wrapper for comparison method {name}'.format(name=name))
11721235
def f(self, other):
11731236
if isinstance(other, ABCDataFrame): # Another DataFrame
@@ -1200,8 +1263,10 @@ def f(self, other):
12001263
# -----------------------------------------------------------------------------
12011264
# Panel
12021265

1203-
def _arith_method_PANEL(op, name, str_rep=None):
1266+
def _arith_method_PANEL(cls, op, special):
12041267
# work only for scalars
1268+
name = _get_op_name(op, special)
1269+
12051270
def f(self, other):
12061271
if not is_scalar(other):
12071272
raise ValueError('Simple arithmetic with {name} can only be '
@@ -1214,7 +1279,10 @@ def f(self, other):
12141279
return f
12151280

12161281

1217-
def _comp_method_PANEL(op, name, str_rep=None):
1282+
def _comp_method_PANEL(cls, op, special):
1283+
str_rep = _get_opstr(op, cls)
1284+
name = _get_op_name(op, special)
1285+
12181286
def na_op(x, y):
12191287
import pandas.core.computation.expressions as expressions
12201288

@@ -1260,7 +1328,9 @@ def f(self, other, axis=None):
12601328
return f
12611329

12621330

1263-
def _flex_method_PANEL(op, name, str_rep=None):
1331+
def _flex_method_PANEL(cls, op, special):
1332+
str_rep = _get_opstr(op, cls)
1333+
name = _get_op_name(op, special)
12641334
eval_kwargs = _gen_eval_kwargs(name)
12651335
fill_zeros = _gen_fill_zeros(name)
12661336

@@ -1298,18 +1368,19 @@ def f(self, other, axis=0):
12981368
comp_method=_comp_method_PANEL,
12991369
bool_method=_arith_method_PANEL)
13001370

1371+
panel_flex_funcs = dict(arith_method=_flex_method_PANEL,
1372+
flex_comp_method=_comp_method_PANEL)
13011373

13021374
# -----------------------------------------------------------------------------
13031375
# Sparse
13041376

13051377

1306-
def _arith_method_SPARSE_SERIES(op, name, str_rep=None):
1378+
def _arith_method_SPARSE_SERIES(cls, op, special):
13071379
"""
13081380
Wrapper function for Series arithmetic operations, to avoid
13091381
code duplication.
1310-
1311-
str_rep is not used, but is present for compatibility.
13121382
"""
1383+
name = _get_op_name(op, special)
13131384

13141385
def wrapper(self, other):
13151386
if isinstance(other, ABCDataFrame):
@@ -1347,11 +1418,12 @@ def _sparse_series_op(left, right, op, name):
13471418
return left._constructor(result, index=new_index, name=new_name)
13481419

13491420

1350-
def _arith_method_SPARSE_ARRAY(op, name, str_rep=None):
1421+
def _arith_method_SPARSE_ARRAY(cls, op, special):
13511422
"""
13521423
Wrapper function for Series arithmetic operations, to avoid
13531424
code duplication.
13541425
"""
1426+
name = _get_op_name(op, special)
13551427

13561428
def wrapper(self, other):
13571429
from pandas.core.sparse.array import (
@@ -1379,3 +1451,12 @@ def wrapper(self, other):
13791451
name = name[2:-2]
13801452
wrapper.__name__ = name
13811453
return wrapper
1454+
1455+
1456+
sparse_array_special_funcs = dict(arith_method=_arith_method_SPARSE_ARRAY,
1457+
comp_method=_arith_method_SPARSE_ARRAY,
1458+
bool_method=_arith_method_SPARSE_ARRAY)
1459+
1460+
sparse_series_special_funcs = dict(arith_method=_arith_method_SPARSE_SERIES,
1461+
comp_method=_arith_method_SPARSE_SERIES,
1462+
bool_method=None)

0 commit comments

Comments
 (0)