Skip to content

Commit

Permalink
Merge pull request #10 from venaturum/GH4_isdisjoint
Browse files Browse the repository at this point in the history
added piso.isdisjoint and corresponding accessor method (#GH4)
  • Loading branch information
venaturum committed Oct 12, 2021
2 parents 27ecb68 + f73b02a commit 458e622
Show file tree
Hide file tree
Showing 10 changed files with 312 additions and 19 deletions.
3 changes: 2 additions & 1 deletion docs/reference/accessors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ Accessors
ArrayAccessor.union
ArrayAccessor.intersection
ArrayAccessor.difference
ArrayAccessor.symmetric_difference
ArrayAccessor.symmetric_difference
ArrayAccessor.isdisjoint
3 changes: 2 additions & 1 deletion docs/reference/package.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ Top level functions
union
intersection
difference
symmetric_difference
symmetric_difference
isdisjoint
7 changes: 6 additions & 1 deletion docs/release_notes/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
Release notes
========================

- added :meth:`piso.isdisjoint` method, and corresponding accessor method

ADD UNRELEASED CHANGED ABOVE THIS LINE

**v0.1.0 2021-10-10**

The following methods are included in the initial release of `piso`
The following methods (and corresponding accessor methods) are included in the initial release of `piso`

- :meth:`piso.register_accessors`
- :meth:`piso.union`
Expand All @@ -17,3 +21,4 @@ The following methods are included in the initial release of `piso`
- :meth:`piso.interval.intersection`
- :meth:`piso.interval.difference`
- :meth:`piso.interval.symmetric_difference`

8 changes: 7 additions & 1 deletion piso/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
from piso.intervalarray import difference, intersection, symmetric_difference, union
from piso.intervalarray import (
difference,
intersection,
isdisjoint,
symmetric_difference,
union,
)


def register_accessors():
Expand Down
7 changes: 7 additions & 0 deletions piso/accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ def symmetric_difference(
return_type=return_type,
)

@Appender(docstrings.isdisjoint_docstring, join="\n", indents=1)
def isdisjoint(self, *interval_arrays):
return intervalarray.isdisjoint(
self._interval_array,
*interval_arrays,
)


def _register_accessors():
_register_accessor("piso", pd.IntervalIndex)(ArrayAccessor)
Expand Down
82 changes: 73 additions & 9 deletions piso/docstrings/accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,38 @@
"""


isdisjoint_examples = """
Examples
-----------
>>> import pandas as pd
>>> import piso
>>> piso.register_accessors()
>>> arr1 = pd.arrays.IntervalArray.from_tuples(
... [(0, 3), (2, 4)],
... )
>>> arr2 = pd.arrays.IntervalArray.from_tuples(
... [(4, 7), (8, 11)],
... )
>>> arr3 = pd.arrays.IntervalArray.from_tuples(
... [(2, 4), (7, 8)],
... )
>>> arr1.piso.isdisjoint()
False
>>> arr2.piso.isdisjoint()
True
>>> arr1.piso.isdisjoint(arr2)
True
>>> arr1.piso.isdisjoint(arr3)
False
"""


def join_params(list_of_param_strings):
return "".join(list_of_param_strings).replace("\n\n", "\n")

Expand Down Expand Up @@ -291,17 +323,15 @@ def join_params(list_of_param_strings):
If supplied, must be done so as a keyword argument.
"""


template_doc = """
Performs a set {operation} operation.
What is considered a set is determined by the number of positional arguments used, that is, determined by the
size of *interval_arrays*.
If *interval_arrays* is empty then the sets are considered to be the intervals contained in *interval_array*.
If *interval_arrays* is empty then the sets are considered to be the intervals contained in the array object the
accessor belongs to (an instance of :class:`pandas.IntervalIndex`, :class:`pandas.arrays.IntervalArray`).
If *interval_arrays* is not empty then the sets are considered to be the elements in *interval_arrays*, in addition to the
interval array object the accessor belongs to (an instance of :class:`pandas.IntervalIndex`, :class:`pandas.arrays.IntervalArray`).
intervals in the array object the accessor belongs to.
Each of these arrays is assumed to contain disjoint intervals (and satisfy the definition of a set). Any array containing
overlaps between intervals will be mapped to one with disjoint intervals via a union operation.
Expand All @@ -312,11 +342,18 @@ def join_params(list_of_param_strings):
Returns
----------
:class:`pandas.IntervalIndex` or :class:`pandas.arrays.IntervalArray`
{return_type}
{examples}
"""

operation_template_doc = (
"""
Performs a set {operation} operation.
"""
+ template_doc
)

doc_difference_template = """
Performs a set difference operation.
Expand Down Expand Up @@ -346,6 +383,9 @@ def join_params(list_of_param_strings):
{examples}
"""

array_return_type = (
":class:`pandas.IntervalIndex` or :class:`pandas.arrays.IntervalArray`"
)

union_params = join_params(
[
Expand All @@ -354,10 +394,11 @@ def join_params(list_of_param_strings):
param_return_type,
]
)
union_docstring = template_doc.format(
union_docstring = operation_template_doc.format(
operation="union",
extra_desc="",
params=union_params,
return_type=array_return_type,
examples=union_examples,
)

Expand All @@ -369,10 +410,11 @@ def join_params(list_of_param_strings):
param_return_type,
]
)
intersection_docstring = template_doc.format(
intersection_docstring = operation_template_doc.format(
operation="intersection",
extra_desc="",
params=intersection_params,
return_type=array_return_type,
examples=intersection_examples,
)

Expand All @@ -387,6 +429,7 @@ def join_params(list_of_param_strings):
operation="difference",
extra_desc="",
params=difference_params,
return_type=array_return_type,
examples=difference_examples,
)

Expand All @@ -404,9 +447,30 @@ def join_params(list_of_param_strings):
The parameter *min_overlaps* in :meth:`piso.intersection`, which defines the minimum number of intervals
in an overlap required to constitute an intersection, follows through to symmetric difference under this definition.
"""
symmetric_difference_docstring = template_doc.format(
symmetric_difference_docstring = operation_template_doc.format(
operation="symmetric difference",
extra_desc=symmetric_difference_extra_desc,
params=symmetric_difference_params,
return_type=array_return_type,
examples=symmetric_difference_examples,
)


isdisjoint_doc = (
"""
Indicates whether one, or more, sets are disjoint or not.
"""
+ template_doc
)

isdisjoint_params = join_params(
[
param_optional_args,
]
)
isdisjoint_docstring = isdisjoint_doc.format(
extra_desc="",
params=isdisjoint_params,
return_type="boolean",
examples=isdisjoint_examples,
)
78 changes: 72 additions & 6 deletions piso/docstrings/intervalarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,37 @@
"""


isdisjoint_examples = """
Examples
-----------
>>> import pandas as pd
>>> import piso
>>> arr1 = pd.arrays.IntervalArray.from_tuples(
... [(0, 3), (2, 4)],
... )
>>> arr2 = pd.arrays.IntervalArray.from_tuples(
... [(4, 7), (8, 11)],
... )
>>> arr3 = pd.arrays.IntervalArray.from_tuples(
... [(2, 4), (7, 8)],
... )
>>> piso.isdisjoint(arr1)
False
>>> piso.isdisjoint(arr2)
True
>>> piso.isdisjoint(arr1, arr2)
True
>>> piso.isdisjoint(arr1, arr3)
False
"""


def join_params(list_of_param_strings):
return "".join(list_of_param_strings).replace("\n\n", "\n")

Expand Down Expand Up @@ -282,8 +313,6 @@ def join_params(list_of_param_strings):


template_doc = """
Performs a set {operation} operation.
What is considered a set is determined by the number of positional arguments used, that is, determined by the
size of *interval_arrays*.
Expand All @@ -300,11 +329,20 @@ def join_params(list_of_param_strings):
Returns
----------
:class:`pandas.IntervalIndex` or :class:`pandas.arrays.IntervalArray`
{return_type}
{examples}
"""


operation_template_doc = (
"""
Performs a set {operation} operation.
"""
+ template_doc
)


doc_difference_template = """
Performs a set difference operation.
Expand Down Expand Up @@ -332,6 +370,9 @@ def join_params(list_of_param_strings):
{examples}
"""

array_return_type = (
":class:`pandas.IntervalIndex` or :class:`pandas.arrays.IntervalArray`"
)

union_params = join_params(
[
Expand All @@ -341,10 +382,11 @@ def join_params(list_of_param_strings):
param_return_type,
]
)
union_docstring = template_doc.format(
union_docstring = operation_template_doc.format(
operation="union",
extra_desc="",
params=union_params,
return_type=array_return_type,
examples=union_examples,
)

Expand All @@ -357,10 +399,11 @@ def join_params(list_of_param_strings):
param_return_type,
]
)
intersection_docstring = template_doc.format(
intersection_docstring = operation_template_doc.format(
operation="intersection",
extra_desc="",
params=intersection_params,
return_type=array_return_type,
examples=intersection_examples,
)

Expand All @@ -376,6 +419,7 @@ def join_params(list_of_param_strings):
operation="difference",
extra_desc="",
params=difference_params,
return_type=array_return_type,
examples=difference_examples,
)

Expand All @@ -394,9 +438,31 @@ def join_params(list_of_param_strings):
The parameter *min_overlaps* in :meth:`piso.intersection`, which defines the minimum number of intervals
in an overlap required to constitute an intersection, follows through to symmetric difference under this definition.
"""
symmetric_difference_docstring = template_doc.format(
symmetric_difference_docstring = operation_template_doc.format(
operation="symmetric difference",
extra_desc=symmetric_difference_extra_desc,
params=symmetric_difference_params,
return_type=array_return_type,
examples=symmetric_difference_examples,
)


isdisjoint_doc = (
"""
Indicates whether one, or more, sets are disjoint or not.
"""
+ template_doc
)

isdisjoint_params = join_params(
[
param_interval_array.format(operation="isdisjoint"),
param_optional_args,
]
)
isdisjoint_docstring = isdisjoint_doc.format(
extra_desc="",
params=isdisjoint_params,
return_type="boolean",
examples=isdisjoint_examples,
)
16 changes: 16 additions & 0 deletions piso/intervalarray.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import numpy as np
import pandas as pd
import staircase as sc

Expand Down Expand Up @@ -103,3 +104,18 @@ def symmetric_difference(
if squeeze and len(result) == 1:
result = result[0]
return result


@Appender(docstrings.isdisjoint_docstring, join="\n", indents=1)
def isdisjoint(interval_array, *interval_arrays):
_validate_array_of_intervals_arrays(interval_array, *interval_arrays)
if interval_arrays:
stairs = _make_stairs(interval_array, *interval_arrays)
result = stairs.max() <= 1
elif len(interval_array) == 0:
result = True
else:
arr = np.stack([interval_array.left.values, interval_array.right.values])
arr = arr[arr[:, 0].argsort()]
result = np.all(arr[0, 1:] >= arr[1, :-1])
return result
Loading

0 comments on commit 458e622

Please sign in to comment.