Skip to content

Commit 7d79370

Browse files
committed
BUG: Avoid sentinel-infinity comparison problems (pandas-dev#13445)
1 parent 8b50d8c commit 7d79370

File tree

4 files changed

+51
-16
lines changed

4 files changed

+51
-16
lines changed

doc/source/whatsnew/v0.19.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,7 @@ Bug Fixes
893893
- Bug in ``.rolling()`` that allowed a negative integer window in contruction of the ``Rolling()`` object, but would later fail on aggregation (:issue:`13383`)
894894

895895
- Bug in printing ``pd.DataFrame`` where unusual elements with the ``object`` dtype were causing segfaults (:issue:`13717`)
896+
- Bug in ranking ``Series`` which could result in segfaults (:issue:`13445`)
896897
- Bug in various index types, which did not propagate the name of passed index (:issue:`12309`)
897898
- Bug in ``DatetimeIndex``, which did not honour the ``copy=True`` (:issue:`13205`)
898899
- Bug in ``DatetimeIndex.is_normalized`` returns incorrectly for normalized date_range in case of local timezones (:issue:`13459`)

pandas/algos.pyx

+13-16
Original file line numberDiff line numberDiff line change
@@ -567,28 +567,25 @@ cdef inline are_diff(object left, object right):
567567
except TypeError:
568568
return left != right
569569

570-
_return_false = lambda self, other: False
571-
_return_true = lambda self, other: True
572570

573571
class Infinity(object):
574572

575-
__lt__ = _return_false
576-
__le__ = _return_false
577-
__eq__ = _return_false
578-
__ne__ = _return_true
579-
__gt__ = _return_true
580-
__ge__ = _return_true
581-
__cmp__ = _return_false
573+
__lt__ = lambda self, other: False
574+
__le__ = lambda self, other: self is other
575+
__eq__ = lambda self, other: self is other
576+
__ne__ = lambda self, other: self is not other
577+
__gt__ = lambda self, other: self is not other
578+
__ge__ = lambda self, other: True
582579

583580
class NegInfinity(object):
584581

585-
__lt__ = _return_true
586-
__le__ = _return_true
587-
__eq__ = _return_false
588-
__ne__ = _return_true
589-
__gt__ = _return_false
590-
__ge__ = _return_false
591-
__cmp__ = _return_true
582+
__lt__ = lambda self, other: self is not other
583+
__le__ = lambda self, other: True
584+
__eq__ = lambda self, other: self is other
585+
__ne__ = lambda self, other: self is not other
586+
__gt__ = lambda self, other: False
587+
__ge__ = lambda self, other: self is other
588+
592589

593590
def rank_2d_generic(object in_arr, axis=0, ties_method='average',
594591
ascending=True, na_option='keep', pct=False):

pandas/tests/test_algos.py

+30
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from numpy.random import RandomState
66
from numpy import nan
77
from datetime import datetime
8+
from itertools import permutations
89
from pandas import Series, Categorical, CategoricalIndex, Index
910
import pandas as pd
1011

@@ -1270,6 +1271,35 @@ def test_groupsort_indexer():
12701271
assert (np.array_equal(result, expected))
12711272

12721273

1274+
def test_infinity_sort():
1275+
# GH 13445
1276+
# numpy's argsort can be unhappy if something is less than
1277+
# itself. Instead, let's give our infinities a self-consistent
1278+
# ordering, but outside the float extended real line.
1279+
1280+
Inf = _algos.Infinity()
1281+
NegInf = _algos.NegInfinity()
1282+
1283+
ref_nums = [NegInf, float("-inf"), -1e100, 0, 1e100, float("inf"), Inf]
1284+
1285+
assert all(Inf >= x for x in ref_nums)
1286+
assert all(Inf > x or x is Inf for x in ref_nums)
1287+
assert Inf >= Inf and Inf == Inf
1288+
assert not Inf < Inf and not Inf > Inf
1289+
1290+
assert all(NegInf <= x for x in ref_nums)
1291+
assert all(NegInf < x or x is NegInf for x in ref_nums)
1292+
assert NegInf <= NegInf and NegInf == NegInf
1293+
assert not NegInf < NegInf and not NegInf > NegInf
1294+
1295+
for perm in permutations(ref_nums):
1296+
assert sorted(perm) == ref_nums
1297+
1298+
# smoke tests
1299+
np.array([_algos.Infinity()] * 32).argsort()
1300+
np.array([_algos.NegInfinity()] * 32).argsort()
1301+
1302+
12731303
def test_ensure_platform_int():
12741304
arr = np.arange(100)
12751305

pandas/tests/test_stats.py

+7
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,13 @@ def test_rank_int(self):
179179
expected.index = result.index
180180
assert_series_equal(result, expected)
181181

182+
def test_rank_object_bug(self):
183+
# GH 13445
184+
185+
# smoke tests
186+
Series([np.nan] * 32).astype(object).rank(ascending=True)
187+
Series([np.nan] * 32).astype(object).rank(ascending=False)
188+
182189

183190
if __name__ == '__main__':
184191
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],

0 commit comments

Comments
 (0)