Skip to content

Commit 7c330c6

Browse files
committed
Add na_tuple kwarg
1 parent b341e49 commit 7c330c6

File tree

3 files changed

+52
-8
lines changed

3 files changed

+52
-8
lines changed

doc/source/whatsnew/v0.22.0.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ Other Enhancements
138138
- :func:`Series` / :func:`DataFrame` tab completion also returns identifiers in the first level of a :func:`MultiIndex`. (:issue:`16326`)
139139
- :func:`read_excel()` has gained the ``nrows`` parameter (:issue:`16645`)
140140
- :func:``DataFrame.to_json`` and ``Series.to_json`` now accept an ``index`` argument which allows the user to exclude the index from the JSON output (:issue:`17394`)
141+
- ``IntervalIndex.to_tuples()`` has gained the ``na_tuple`` parameter to control whether NA is returned as a tuple of NA, or NA itself (:issue:`18756`)
141142

142143
.. _whatsnew_0220.api_breaking:
143144

@@ -264,7 +265,6 @@ Conversion
264265
- Fixed a bug where ``FY5253`` date offsets could incorrectly raise an ``AssertionError`` in arithmetic operatons (:issue:`14774`)
265266
- Bug in :meth:`Index.astype` with a categorical dtype where the resultant index is not converted to a :class:`CategoricalIndex` for all types of index (:issue:`18630`)
266267
- Bug in :meth:`Series.astype` and ``Categorical.astype()`` where an existing categorical data does not get updated (:issue:`10696`, :issue:`18593`)
267-
- Bug in ``IntervalIndex.to_tuples()`` where NA values are returned as a tuple of NA values instead of the NA value itself (:issue:`18756`)
268268

269269

270270
Indexing

pandas/core/indexes/interval.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -544,10 +544,28 @@ def from_tuples(cls, data, closed='right', name=None, copy=False):
544544

545545
return cls.from_arrays(left, right, closed, name=name, copy=False)
546546

547-
def to_tuples(self):
548-
"""Return an Index of tuples of the form (left, right)"""
547+
def to_tuples(self, na_tuple=True):
548+
"""
549+
Return an Index of tuples of the form (left, right)
550+
551+
Parameters
552+
----------
553+
na_tuple : boolean, default True
554+
Returns NA as a tuple if True, ``(nan, nan)``, or just as the NA
555+
value itself if False, ``nan``.
556+
557+
..versionadded:: 0.22.0
558+
559+
Examples
560+
--------
561+
>>> idx = pd.IntervalIndex.from_arrays([0, np.nan, 2], [1, np.nan, 3])
562+
>>> idx.to_tuples()
563+
Index([(0.0, 1.0), (nan, nan), (2.0, 3.0)], dtype='object')
564+
>>> idx.to_tuples(na_tuple=False)
565+
Index([(0.0, 1.0), nan, (2.0, 3.0)], dtype='object')
566+
"""
549567
tuples = _asarray_tuplesafe(zip(self.left, self.right))
550-
if self.hasnans:
568+
if not na_tuple:
551569
# GH 18756
552570
tuples = np.where(~self._isnan, tuples, np.nan)
553571
return Index(tuples)

pandas/tests/indexes/test_interval.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,17 +1075,43 @@ def test_is_non_overlapping_monotonic(self, closed):
10751075

10761076
@pytest.mark.parametrize('tuples', [
10771077
lzip(range(10), range(1, 11)),
1078-
lzip(range(10), range(1, 11)) + [np.nan],
10791078
lzip(date_range('20170101', periods=10),
10801079
date_range('20170101', periods=10)),
1081-
[np.nan] + lzip(date_range('20170101', periods=10),
1082-
date_range('20170101', periods=10))])
1080+
lzip(timedelta_range('0 days', periods=10),
1081+
timedelta_range('1 day', periods=10))])
10831082
def test_to_tuples(self, tuples):
10841083
# GH 18756
1085-
result = IntervalIndex.from_tuples(tuples).to_tuples()
1084+
idx = IntervalIndex.from_tuples(tuples)
1085+
result = idx.to_tuples()
10861086
expected = Index(_asarray_tuplesafe(tuples))
10871087
tm.assert_index_equal(result, expected)
10881088

1089+
@pytest.mark.parametrize('tuples', [
1090+
lzip(range(10), range(1, 11)) + [np.nan],
1091+
lzip(date_range('20170101', periods=10),
1092+
date_range('20170101', periods=10)) + [np.nan],
1093+
lzip(timedelta_range('0 days', periods=10),
1094+
timedelta_range('1 day', periods=10)) + [np.nan]])
1095+
@pytest.mark.parametrize('na_tuple', [True, False])
1096+
def test_to_tuples_na(self, tuples, na_tuple):
1097+
# GH 18756
1098+
idx = IntervalIndex.from_tuples(tuples)
1099+
result = idx.to_tuples(na_tuple=na_tuple)
1100+
1101+
# check the non-NA portion
1102+
expected_notna = Index(_asarray_tuplesafe(tuples[:-1]))
1103+
result_notna = result[:-1]
1104+
tm.assert_index_equal(result_notna, expected_notna)
1105+
1106+
# check the NA portion
1107+
result_na = result[-1]
1108+
if na_tuple:
1109+
assert isinstance(result_na, tuple)
1110+
assert len(result_na) == 2
1111+
assert all(isna(x) for x in result_na)
1112+
else:
1113+
assert isna(result_na)
1114+
10891115

10901116
class TestIntervalRange(object):
10911117

0 commit comments

Comments
 (0)