Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENH] Use default EA repr for IntervalArray #26316

Merged
merged 23 commits into from
Sep 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 19 additions & 1 deletion doc/source/whatsnew/v1.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,25 @@ Backwards incompatible API changes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

- :class:`pandas.core.groupby.GroupBy.transform` now raises on invalid operation names (:issue:`27489`).
-
- :class:`pandas.core.arrays.IntervalArray` adopts a new ``__repr__`` in accordance with other array classes (:issue:`25022`)

*pandas 0.25.x*

.. code-block:: ipython

In [1]: pd.arrays.IntervalArray.from_tuples([(0, 1), (2, 3)])
Out[2]:
IntervalArray([(0, 1], (2, 3]],
closed='right',
dtype='interval[int64]')


*pandas 1.0.0*

.. ipython:: python

pd.arrays.IntervalArray.from_tuples([(0, 1), (2, 3)])


.. _whatsnew_1000.api.other:

Expand Down
210 changes: 137 additions & 73 deletions pandas/core/arrays/interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@
``Interval`` objects:

>>> pd.arrays.IntervalArray([pd.Interval(0, 1), pd.Interval(1, 5)])
IntervalArray([(0, 1], (1, 5]],
closed='right',
dtype='interval[int64]')
<IntervalArray>
[(0, 1], (1, 5]]
Length: 2, closed: right, dtype: interval[int64]

It may also be constructed using one of the constructor
methods: :meth:`IntervalArray.from_arrays`,
Expand Down Expand Up @@ -248,9 +248,8 @@ def _from_factorized(cls, values, original):
values = values.astype(original.dtype.subtype)
return cls(values, closed=original.closed)

_interval_shared_docs[
"from_breaks"
] = """
_interval_shared_docs["from_breaks"] = textwrap.dedent(
"""
Construct an %(klass)s from an array of splits.

Parameters
Expand All @@ -277,24 +276,34 @@ def _from_factorized(cls, values, original):
%(klass)s.from_arrays : Construct from a left and right array.
%(klass)s.from_tuples : Construct from a sequence of tuples.

Examples
--------
>>> pd.%(qualname)s.from_breaks([0, 1, 2, 3])
%(klass)s([(0, 1], (1, 2], (2, 3]],
closed='right',
dtype='interval[int64]')
%(examples)s\
"""
)

@classmethod
@Appender(_interval_shared_docs["from_breaks"] % _shared_docs_kwargs)
@Appender(
_interval_shared_docs["from_breaks"]
% dict(
klass="IntervalArray",
examples=textwrap.dedent(
"""\
Examples
--------
>>> pd.arrays.IntervalArray.from_breaks([0, 1, 2, 3])
<IntervalArray>
[(0, 1], (1, 2], (2, 3]]
Length: 3, closed: right, dtype: interval[int64]
"""
),
)
)
def from_breaks(cls, breaks, closed="right", copy=False, dtype=None):
breaks = maybe_convert_platform_interval(breaks)

return cls.from_arrays(breaks[:-1], breaks[1:], closed, copy=copy, dtype=dtype)

_interval_shared_docs[
"from_arrays"
] = """
_interval_shared_docs["from_arrays"] = textwrap.dedent(
"""
Construct from two arrays defining the left and right bounds.

Parameters
Expand Down Expand Up @@ -340,16 +349,25 @@ def from_breaks(cls, breaks, closed="right", copy=False, dtype=None):
using an unsupported type for `left` or `right`. At the moment,
'category', 'object', and 'string' subtypes are not supported.

Examples
--------
>>> %(klass)s.from_arrays([0, 1, 2], [1, 2, 3])
%(klass)s([(0, 1], (1, 2], (2, 3]],
closed='right',
dtype='interval[int64]')
%(examples)s\
"""
)

@classmethod
@Appender(_interval_shared_docs["from_arrays"] % _shared_docs_kwargs)
@Appender(
_interval_shared_docs["from_arrays"]
% dict(
klass="IntervalArray",
examples=textwrap.dedent(
"""\
>>> pd.arrays.IntervalArray.from_arrays([0, 1, 2], [1, 2, 3])
<IntervalArray>
[(0, 1], (1, 2], (2, 3]]
Length: 3, closed: right, dtype: interval[int64]
"""
),
)
)
def from_arrays(cls, left, right, closed="right", copy=False, dtype=None):
left = maybe_convert_platform_interval(left)
right = maybe_convert_platform_interval(right)
Expand All @@ -358,9 +376,8 @@ def from_arrays(cls, left, right, closed="right", copy=False, dtype=None):
left, right, closed, copy=copy, dtype=dtype, verify_integrity=True
)

_interval_shared_docs[
"from_tuples"
] = """
_interval_shared_docs["from_tuples"] = textwrap.dedent(
"""
Construct an %(klass)s from an array-like of tuples.

Parameters
Expand Down Expand Up @@ -389,15 +406,27 @@ def from_arrays(cls, left, right, closed="right", copy=False, dtype=None):
%(klass)s.from_breaks : Construct an %(klass)s from an array of
splits.

Examples
--------
>>> pd.%(qualname)s.from_tuples([(0, 1), (1, 2)])
%(klass)s([(0, 1], (1, 2]],
closed='right', dtype='interval[int64]')
%(examples)s\
"""
)

@classmethod
@Appender(_interval_shared_docs["from_tuples"] % _shared_docs_kwargs)
@Appender(
_interval_shared_docs["from_tuples"]
% dict(
klass="IntervalArray",
examples=textwrap.dedent(
"""\
Examples
--------
>>> pd.arrays.IntervalArray.from_tuples([(0, 1), (1, 2)])
<IntervalArray>
[(0, 1], (1, 2]]
Length: 2, closed: right, dtype: interval[int64]
"""
),
)
)
def from_tuples(cls, data, closed="right", copy=False, dtype=None):
if len(data):
left, right = [], []
Expand Down Expand Up @@ -832,16 +861,20 @@ def _format_data(self):
return summary

def __repr__(self):
tpl = textwrap.dedent(
"""\
{cls}({data},
{lead}closed='{closed}',
{lead}dtype='{dtype}')"""
template = (
"{class_name}"
"{data}\n"
"Length: {length}, closed: {closed}, dtype: {dtype}"
)
return tpl.format(
cls=self.__class__.__name__,
data=self._format_data(),
lead=" " * len(self.__class__.__name__) + " ",
# the short repr has no trailing newline, while the truncated
# repr does. So we include a newline in our template, and strip
# any trailing newlines from format_object_summary
data = self._format_data()
class_name = "<{}>\n".format(self.__class__.__name__)
return template.format(
class_name=class_name,
data=data,
length=len(self),
closed=self.closed,
dtype=self.dtype,
)
Expand Down Expand Up @@ -874,9 +907,8 @@ def closed(self):
"""
return self._closed

_interval_shared_docs[
"set_closed"
] = """
_interval_shared_docs["set_closed"] = textwrap.dedent(
"""
Return an %(klass)s identical to the current one, but closed on the
specified side.

Expand All @@ -892,20 +924,31 @@ def closed(self):
-------
new_index : %(klass)s

%(examples)s\
"""
)

@Appender(
_interval_shared_docs["set_closed"]
% dict(
klass="IntervalArray",
examples=textwrap.dedent(
"""\
Examples
--------
>>> index = pd.interval_range(0, 3)
>>> index = pd.arrays.IntervalArray.from_breaks(range(4))
>>> index
IntervalIndex([(0, 1], (1, 2], (2, 3]],
closed='right',
dtype='interval[int64]')
<IntervalArray>
[(0, 1], (1, 2], (2, 3]]
Length: 3, closed: right, dtype: interval[int64]
>>> index.set_closed('both')
IntervalIndex([[0, 1], [1, 2], [2, 3]],
closed='both',
dtype='interval[int64]')
<IntervalArray>
[[0, 1], [1, 2], [2, 3]]
Length: 3, closed: both, dtype: interval[int64]
"""

@Appender(_interval_shared_docs["set_closed"] % _shared_docs_kwargs)
),
)
)
def set_closed(self, closed):
if closed not in _VALID_CLOSED:
msg = "invalid option for 'closed': {closed}"
Expand Down Expand Up @@ -1028,9 +1071,8 @@ def repeat(self, repeats, axis=None):
right_repeat = self.right.repeat(repeats)
return self._shallow_copy(left=left_repeat, right=right_repeat)

_interval_shared_docs[
"contains"
] = """
_interval_shared_docs["contains"] = textwrap.dedent(
"""
Check elementwise if the Intervals contain the value.

Return a boolean mask whether the value is contained in the Intervals
Expand All @@ -1055,16 +1097,27 @@ def repeat(self, repeats, axis=None):

Examples
--------
>>> intervals = pd.%(qualname)s.from_tuples([(0, 1), (1, 3), (2, 4)])
>>> intervals
%(klass)s([(0, 1], (1, 3], (2, 4]],
closed='right',
dtype='interval[int64]')
%(examples)s
>>> intervals.contains(0.5)
array([ True, False, False])
"""
)

@Appender(_interval_shared_docs["contains"] % _shared_docs_kwargs)
@Appender(
_interval_shared_docs["contains"]
% dict(
klass="IntervalArray",
examples=textwrap.dedent(
"""\
>>> intervals = pd.arrays.IntervalArray.from_tuples([(0, 1), (1, 3), (2, 4)])
>>> intervals
<IntervalArray>
[(0, 1], (1, 3], (2, 4]]
Length: 3, closed: right, dtype: interval[int64]
"""
),
)
)
def contains(self, other):
if isinstance(other, Interval):
raise NotImplementedError("contains not implemented for two intervals")
Expand All @@ -1073,9 +1126,8 @@ def contains(self, other):
other < self.right if self.open_right else other <= self.right
)

_interval_shared_docs[
"overlaps"
] = """
_interval_shared_docs["overlaps"] = textwrap.dedent(
"""
Check elementwise if an Interval overlaps the values in the %(klass)s.

Two intervals overlap if they share a common point, including closed
Expand All @@ -1086,7 +1138,7 @@ def contains(self, other):

Parameters
----------
other : Interval
other : %(klass)s
Interval to check against for an overlap.

Returns
Expand All @@ -1100,11 +1152,7 @@ def contains(self, other):

Examples
--------
>>> intervals = pd.%(qualname)s.from_tuples([(0, 1), (1, 3), (2, 4)])
>>> intervals
%(klass)s([(0, 1], (1, 3], (2, 4]],
closed='right',
dtype='interval[int64]')
%(examples)s
>>> intervals.overlaps(pd.Interval(0.5, 1.5))
array([ True, True, False])

Expand All @@ -1117,9 +1165,25 @@ def contains(self, other):

>>> intervals.overlaps(pd.Interval(1, 2, closed='right'))
array([False, True, False])
"""
"""
)

@Appender(_interval_shared_docs["overlaps"] % _shared_docs_kwargs)
@Appender(
_interval_shared_docs["overlaps"]
% dict(
klass="IntervalArray",
examples=textwrap.dedent(
"""\
>>> data = [(0, 1), (1, 3), (2, 4)]
>>> intervals = pd.arrays.IntervalArray.from_tuples(data)
>>> intervals
<IntervalArray>
[(0, 1], (1, 3], (2, 4]]
Length: 3, closed: right, dtype: interval[int64]
"""
),
)
)
def overlaps(self, other):
if isinstance(other, (IntervalArray, ABCIntervalIndex)):
raise NotImplementedError
Expand Down