Skip to content

Commit 05cb960

Browse files
committed
Merge pull request #5439 from jtratner/make-div-do-truediv
ENH: Always do true division on Python 2.X
2 parents 2d2e8b5 + 98857c1 commit 05cb960

File tree

12 files changed

+80
-50
lines changed

12 files changed

+80
-50
lines changed

doc/source/release.rst

+19
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,25 @@ API Changes
376376
dates are given (:issue:`5242`)
377377
- ``Timestamp`` now supports ``now/today/utcnow`` class methods
378378
(:issue:`5339`)
379+
- **All** division with ``NDFrame`` - likes is now truedivision, regardless
380+
of the future import. You can use ``//`` and ``floordiv`` to do integer
381+
division.
382+
383+
.. code-block:: python
384+
In [3]: arr = np.array([1, 2, 3, 4])
385+
386+
In [4]: arr2 = np.array([5, 3, 2, 1])
387+
388+
In [5]: arr / arr2
389+
Out[5]: array([0, 0, 1, 4])
390+
391+
In [6]: pd.Series(arr) / pd.Series(arr2) # no future import required
392+
Out[6]:
393+
0 0.200000
394+
1 0.666667
395+
2 1.500000
396+
3 4.000000
397+
dtype: float64
379398
380399
Internal Refactoring
381400
~~~~~~~~~~~~~~~~~~~~

doc/source/v0.13.0.txt

+20
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,26 @@ API changes
5555
# and all methods take an inplace kwarg - but returns None
5656
index.set_names(["bob", "cranberry"], inplace=True)
5757

58+
- **All** division with ``NDFrame`` - likes is now truedivision, regardless
59+
of the future import. You can use ``//`` and ``floordiv`` to do integer
60+
division.
61+
62+
.. code-block:: python
63+
In [3]: arr = np.array([1, 2, 3, 4])
64+
65+
In [4]: arr2 = np.array([5, 3, 2, 1])
66+
67+
In [5]: arr / arr2
68+
Out[5]: array([0, 0, 1, 4])
69+
70+
In [6]: pd.Series(arr) / pd.Series(arr2) # no future import required
71+
Out[6]:
72+
0 0.200000
73+
1 0.666667
74+
2 1.500000
75+
3 4.000000
76+
dtype: float64
77+
5878
- Infer and downcast dtype if ``downcast='infer'`` is passed to ``fillna/ffill/bfill`` (:issue:`4604`)
5979
- ``__nonzero__`` for all NDFrame objects, will now raise a ``ValueError``, this reverts back to (:issue:`1073`, :issue:`4633`)
6080
behavior. See :ref:`gotchas<gotchas.truth>` for a more detailed discussion.

pandas/computation/expressions.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def _evaluate_standard(op, op_str, a, b, raise_on_error=True, **eval_kwargs):
6161
_store_test_result(False)
6262
return op(a, b)
6363

64+
6465
def _can_use_numexpr(op, op_str, a, b, dtype_check):
6566
""" return a boolean if we WILL be using numexpr """
6667
if op_str is not None:
@@ -86,7 +87,8 @@ def _can_use_numexpr(op, op_str, a, b, dtype_check):
8687
return False
8788

8889

89-
def _evaluate_numexpr(op, op_str, a, b, raise_on_error=False, **eval_kwargs):
90+
def _evaluate_numexpr(op, op_str, a, b, raise_on_error=False, truediv=True,
91+
**eval_kwargs):
9092
result = None
9193

9294
if _can_use_numexpr(op, op_str, a, b, 'evaluate'):
@@ -96,7 +98,8 @@ def _evaluate_numexpr(op, op_str, a, b, raise_on_error=False, **eval_kwargs):
9698
result = ne.evaluate('a_value %s b_value' % op_str,
9799
local_dict={'a_value': a_value,
98100
'b_value': b_value},
99-
casting='safe', **eval_kwargs)
101+
casting='safe', truediv=truediv,
102+
**eval_kwargs)
100103
except (ValueError) as detail:
101104
if 'unknown type object' in str(detail):
102105
pass
@@ -112,10 +115,12 @@ def _evaluate_numexpr(op, op_str, a, b, raise_on_error=False, **eval_kwargs):
112115

113116
return result
114117

118+
115119
def _where_standard(cond, a, b, raise_on_error=True):
116120
return np.where(_values_from_object(cond), _values_from_object(a),
117121
_values_from_object(b))
118122

123+
119124
def _where_numexpr(cond, a, b, raise_on_error=False):
120125
result = None
121126

@@ -190,10 +195,10 @@ def where(cond, a, b, raise_on_error=False, use_numexpr=True):
190195
return _where_standard(cond, a, b, raise_on_error=raise_on_error)
191196

192197

193-
def set_test_mode(v = True):
198+
def set_test_mode(v=True):
194199
"""
195-
Keeps track of whether numexpr was used. Stores an additional ``True`` for
196-
every successful use of evaluate with numexpr since the last
200+
Keeps track of whether numexpr was used. Stores an additional ``True``
201+
for every successful use of evaluate with numexpr since the last
197202
``get_test_result``
198203
"""
199204
global _TEST_MODE, _TEST_RESULT

pandas/core/algorithms.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Generic data algorithms. This module is experimental at the moment and not
33
intended for public consumption
44
"""
5-
5+
from __future__ import division
66
from warnings import warn
77
import numpy as np
88

pandas/core/frame.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
alignment and a host of useful data manipulation methods having to do with the
99
labeling information
1010
"""
11-
11+
from __future__ import division
1212
# pylint: disable=E1101,E1103
1313
# pylint: disable=W0212,W0231,W0703,W0622
1414

pandas/core/ops.py

+6-18
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
44
This is not a public API.
55
"""
6+
# necessary to enforce truediv in Python 2.X
7+
from __future__ import division
68
import operator
79
import numpy as np
810
import pandas as pd
@@ -80,24 +82,10 @@ def names(x):
8082
rmod=arith_method(lambda x, y: y % x, names('rmod'),
8183
default_axis=default_axis),
8284
)
83-
if not compat.PY3:
84-
new_methods["div"] = arith_method(operator.div, names('div'), op('/'),
85-
truediv=False, fill_zeros=np.inf,
86-
default_axis=default_axis)
87-
new_methods["rdiv"] = arith_method(lambda x, y: operator.div(y, x),
88-
names('rdiv'), truediv=False,
89-
fill_zeros=np.inf,
90-
default_axis=default_axis)
91-
else:
92-
new_methods["div"] = arith_method(operator.truediv, names('div'),
93-
op('/'), truediv=True,
94-
fill_zeros=np.inf,
95-
default_axis=default_axis)
96-
new_methods["rdiv"] = arith_method(lambda x, y: operator.truediv(y, x),
97-
names('rdiv'), truediv=False,
98-
fill_zeros=np.inf,
99-
default_axis=default_axis)
100-
# Comp methods never had a default axis set
85+
new_methods['div'] = new_methods['truediv']
86+
new_methods['rdiv'] = new_methods['rtruediv']
87+
88+
# Comp methods never had a default axis set
10189
if comp_method:
10290
new_methods.update(dict(
10391
eq=comp_method(operator.eq, names('eq'), op('==')),

pandas/core/panel.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Contains data structures designed for manipulating panel (3-dimensional) data
33
"""
44
# pylint: disable=E1103,W0231,W0212,W0621
5-
5+
from __future__ import division
66
from pandas.compat import map, zip, range, lrange, lmap, u, OrderedDict, OrderedDefaultdict
77
from pandas import compat
88
import sys

pandas/core/series.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""
22
Data structure for 1-dimensional cross-sectional and time series data
33
"""
4+
from __future__ import division
45

56
# pylint: disable=E1101,E1103
67
# pylint: disable=W0703,W0622,W0613,W0201

pandas/sparse/array.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
SparseArray data structure
33
"""
4-
4+
from __future__ import division
55
# pylint: disable=E1101,E1103,W0231
66

77
from numpy import nan, ndarray

pandas/sparse/frame.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Data structures for sparse float data. Life is made simpler by dealing only
33
with float64 data
44
"""
5-
5+
from __future__ import division
66
# pylint: disable=E1101,E1103,W0231,E0202
77

88
from numpy import nan

pandas/tests/test_expressions.py

+10-15
Original file line numberDiff line numberDiff line change
@@ -72,23 +72,21 @@ def run_arithmetic_test(self, df, other, assert_func, check_dtype=False,
7272
if not compat.PY3:
7373
operations.append('div')
7474
for arith in operations:
75-
if test_flex:
76-
op = getattr(df, arith)
77-
else:
78-
op = getattr(operator, arith)
75+
operator_name = arith
76+
if arith == 'div':
77+
operator_name = 'truediv'
78+
7979
if test_flex:
8080
op = lambda x, y: getattr(df, arith)(y)
8181
op.__name__ = arith
8282
else:
83-
op = getattr(operator, arith)
83+
op = getattr(operator, operator_name)
8484
expr.set_use_numexpr(False)
8585
expected = op(df, other)
8686
expr.set_use_numexpr(True)
8787
result = op(df, other)
8888
try:
8989
if check_dtype:
90-
if arith == 'div':
91-
assert expected.dtype.kind == df.dtype.kind
9290
if arith == 'truediv':
9391
assert expected.dtype.kind == 'f'
9492
assert_func(expected, result)
@@ -103,7 +101,7 @@ def test_integer_arithmetic(self):
103101
assert_series_equal, check_dtype=True)
104102

105103
@nose.tools.nottest
106-
def run_binary_test(self, df, other, assert_func, check_dtype=False,
104+
def run_binary_test(self, df, other, assert_func,
107105
test_flex=False, numexpr_ops=set(['gt', 'lt', 'ge',
108106
'le', 'eq', 'ne'])):
109107
"""
@@ -127,11 +125,6 @@ def run_binary_test(self, df, other, assert_func, check_dtype=False,
127125
result = op(df, other)
128126
used_numexpr = expr.get_test_result()
129127
try:
130-
if check_dtype:
131-
if arith == 'div':
132-
assert expected.dtype.kind == result.dtype.kind
133-
if arith == 'truediv':
134-
assert result.dtype.kind == 'f'
135128
if arith in numexpr_ops:
136129
assert used_numexpr, "Did not use numexpr as expected."
137130
else:
@@ -267,8 +260,10 @@ def testit():
267260
for f, f2 in [ (self.frame, self.frame2), (self.mixed, self.mixed2) ]:
268261

269262
for op, op_str in [('add','+'),('sub','-'),('mul','*'),('div','/'),('pow','**')]:
270-
271-
op = getattr(operator,op,None)
263+
if op == 'div':
264+
op = getattr(operator, 'truediv', None)
265+
else:
266+
op = getattr(operator, op, None)
272267
if op is not None:
273268
result = expr._can_use_numexpr(op, op_str, f, f, 'evaluate')
274269
self.assert_(result == (not f._is_mixed_type))

pandas/tests/test_series.py

+9-7
Original file line numberDiff line numberDiff line change
@@ -2032,7 +2032,12 @@ def check(series, other, check_reverse=False):
20322032

20332033
for opname in simple_ops:
20342034
op = getattr(Series, opname)
2035-
alt = getattr(operator, opname)
2035+
2036+
if op == 'div':
2037+
alt = operator.truediv
2038+
else:
2039+
alt = getattr(operator, opname)
2040+
20362041
result = op(series, other)
20372042
expected = alt(series, other)
20382043
tm.assert_almost_equal(result, expected)
@@ -2079,11 +2084,11 @@ def test_modulo(self):
20792084

20802085
def test_div(self):
20812086

2082-
# integer div, but deal with the 0's
2087+
# no longer do integer div for any ops, but deal with the 0's
20832088
p = DataFrame({'first': [3, 4, 5, 8], 'second': [0, 0, 0, 3]})
20842089
result = p['first'] / p['second']
20852090
expected = Series(
2086-
p['first'].values / p['second'].values, dtype='float64')
2091+
p['first'].values.astype(float) / p['second'].values, dtype='float64')
20872092
expected.iloc[0:3] = np.inf
20882093
assert_series_equal(result, expected)
20892094

@@ -2098,10 +2103,7 @@ def test_div(self):
20982103

20992104
p = DataFrame({'first': [3, 4, 5, 8], 'second': [1, 1, 1, 1]})
21002105
result = p['first'] / p['second']
2101-
if compat.PY3:
2102-
assert_series_equal(result, p['first'].astype('float64'))
2103-
else:
2104-
assert_series_equal(result, p['first'])
2106+
assert_series_equal(result, p['first'].astype('float64'))
21052107
self.assertFalse(np.array_equal(result, p['second'] / p['first']))
21062108

21072109
def test_operators(self):

0 commit comments

Comments
 (0)