pruned venvs
This commit is contained in:
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
-209
@@ -1,209 +0,0 @@
|
||||
from __future__ import division
|
||||
|
||||
import pytest
|
||||
import numpy as np
|
||||
from pandas import (
|
||||
Index,
|
||||
IntervalIndex,
|
||||
interval_range,
|
||||
CategoricalIndex,
|
||||
Timestamp,
|
||||
Timedelta,
|
||||
NaT)
|
||||
from pandas.core.dtypes.dtypes import CategoricalDtype, IntervalDtype
|
||||
import pandas.util.testing as tm
|
||||
|
||||
|
||||
class Base(object):
|
||||
"""Tests common to IntervalIndex with any subtype"""
|
||||
|
||||
def test_astype_idempotent(self, index):
|
||||
result = index.astype('interval')
|
||||
tm.assert_index_equal(result, index)
|
||||
|
||||
result = index.astype(index.dtype)
|
||||
tm.assert_index_equal(result, index)
|
||||
|
||||
def test_astype_object(self, index):
|
||||
result = index.astype(object)
|
||||
expected = Index(index.values, dtype='object')
|
||||
tm.assert_index_equal(result, expected)
|
||||
assert not result.equals(index)
|
||||
|
||||
def test_astype_category(self, index):
|
||||
result = index.astype('category')
|
||||
expected = CategoricalIndex(index.values)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
result = index.astype(CategoricalDtype())
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# non-default params
|
||||
categories = index.dropna().unique().values[:-1]
|
||||
dtype = CategoricalDtype(categories=categories, ordered=True)
|
||||
result = index.astype(dtype)
|
||||
expected = CategoricalIndex(
|
||||
index.values, categories=categories, ordered=True)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
@pytest.mark.parametrize('dtype', [
|
||||
'int64', 'uint64', 'float64', 'complex128', 'period[M]',
|
||||
'timedelta64', 'timedelta64[ns]', 'datetime64', 'datetime64[ns]',
|
||||
'datetime64[ns, US/Eastern]'])
|
||||
def test_astype_cannot_cast(self, index, dtype):
|
||||
msg = 'Cannot cast IntervalIndex to dtype'
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
index.astype(dtype)
|
||||
|
||||
def test_astype_invalid_dtype(self, index):
|
||||
msg = 'data type "fake_dtype" not understood'
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
index.astype('fake_dtype')
|
||||
|
||||
|
||||
class TestIntSubtype(Base):
|
||||
"""Tests specific to IntervalIndex with integer-like subtype"""
|
||||
|
||||
indexes = [
|
||||
IntervalIndex.from_breaks(np.arange(-10, 11, dtype='int64')),
|
||||
IntervalIndex.from_breaks(
|
||||
np.arange(100, dtype='uint64'), closed='left'),
|
||||
]
|
||||
|
||||
@pytest.fixture(params=indexes)
|
||||
def index(self, request):
|
||||
return request.param
|
||||
|
||||
@pytest.mark.parametrize('subtype', [
|
||||
'float64', 'datetime64[ns]', 'timedelta64[ns]'])
|
||||
def test_subtype_conversion(self, index, subtype):
|
||||
dtype = IntervalDtype(subtype)
|
||||
result = index.astype(dtype)
|
||||
expected = IntervalIndex.from_arrays(index.left.astype(subtype),
|
||||
index.right.astype(subtype),
|
||||
closed=index.closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
@pytest.mark.parametrize('subtype_start, subtype_end', [
|
||||
('int64', 'uint64'), ('uint64', 'int64')])
|
||||
def test_subtype_integer(self, subtype_start, subtype_end):
|
||||
index = IntervalIndex.from_breaks(np.arange(100, dtype=subtype_start))
|
||||
dtype = IntervalDtype(subtype_end)
|
||||
result = index.astype(dtype)
|
||||
expected = IntervalIndex.from_arrays(index.left.astype(subtype_end),
|
||||
index.right.astype(subtype_end),
|
||||
closed=index.closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
@pytest.mark.xfail(reason='GH 15832')
|
||||
def test_subtype_integer_errors(self):
|
||||
# int64 -> uint64 fails with negative values
|
||||
index = interval_range(-10, 10)
|
||||
dtype = IntervalDtype('uint64')
|
||||
with pytest.raises(ValueError):
|
||||
index.astype(dtype)
|
||||
|
||||
|
||||
class TestFloatSubtype(Base):
|
||||
"""Tests specific to IntervalIndex with float subtype"""
|
||||
|
||||
indexes = [
|
||||
interval_range(-10.0, 10.0, closed='neither'),
|
||||
IntervalIndex.from_arrays([-1.5, np.nan, 0., 0., 1.5],
|
||||
[-0.5, np.nan, 1., 1., 3.],
|
||||
closed='both'),
|
||||
]
|
||||
|
||||
@pytest.fixture(params=indexes)
|
||||
def index(self, request):
|
||||
return request.param
|
||||
|
||||
@pytest.mark.parametrize('subtype', ['int64', 'uint64'])
|
||||
def test_subtype_integer(self, subtype):
|
||||
index = interval_range(0.0, 10.0)
|
||||
dtype = IntervalDtype(subtype)
|
||||
result = index.astype(dtype)
|
||||
expected = IntervalIndex.from_arrays(index.left.astype(subtype),
|
||||
index.right.astype(subtype),
|
||||
closed=index.closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# raises with NA
|
||||
msg = 'Cannot convert NA to integer'
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
index.insert(0, np.nan).astype(dtype)
|
||||
|
||||
@pytest.mark.xfail(reason='GH 15832')
|
||||
def test_subtype_integer_errors(self):
|
||||
# float64 -> uint64 fails with negative values
|
||||
index = interval_range(-10.0, 10.0)
|
||||
dtype = IntervalDtype('uint64')
|
||||
with pytest.raises(ValueError):
|
||||
index.astype(dtype)
|
||||
|
||||
# float64 -> integer-like fails with non-integer valued floats
|
||||
index = interval_range(0.0, 10.0, freq=0.25)
|
||||
dtype = IntervalDtype('int64')
|
||||
with pytest.raises(ValueError):
|
||||
index.astype(dtype)
|
||||
|
||||
dtype = IntervalDtype('uint64')
|
||||
with pytest.raises(ValueError):
|
||||
index.astype(dtype)
|
||||
|
||||
@pytest.mark.parametrize('subtype', ['datetime64[ns]', 'timedelta64[ns]'])
|
||||
def test_subtype_datetimelike(self, index, subtype):
|
||||
dtype = IntervalDtype(subtype)
|
||||
msg = 'Cannot convert .* to .*; subtypes are incompatible'
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
index.astype(dtype)
|
||||
|
||||
|
||||
class TestDatetimelikeSubtype(Base):
|
||||
"""Tests specific to IntervalIndex with datetime-like subtype"""
|
||||
|
||||
indexes = [
|
||||
interval_range(Timestamp('2018-01-01'), periods=10, closed='neither'),
|
||||
interval_range(Timestamp('2018-01-01'), periods=10).insert(2, NaT),
|
||||
interval_range(Timestamp('2018-01-01', tz='US/Eastern'), periods=10),
|
||||
interval_range(Timedelta('0 days'), periods=10, closed='both'),
|
||||
interval_range(Timedelta('0 days'), periods=10).insert(2, NaT),
|
||||
]
|
||||
|
||||
@pytest.fixture(params=indexes)
|
||||
def index(self, request):
|
||||
return request.param
|
||||
|
||||
@pytest.mark.parametrize('subtype', ['int64', 'uint64'])
|
||||
def test_subtype_integer(self, index, subtype):
|
||||
dtype = IntervalDtype(subtype)
|
||||
result = index.astype(dtype)
|
||||
expected = IntervalIndex.from_arrays(index.left.astype(subtype),
|
||||
index.right.astype(subtype),
|
||||
closed=index.closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
def test_subtype_float(self, index):
|
||||
dtype = IntervalDtype('float64')
|
||||
msg = 'Cannot convert .* to .*; subtypes are incompatible'
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
index.astype(dtype)
|
||||
|
||||
def test_subtype_datetimelike(self):
|
||||
# datetime -> timedelta raises
|
||||
dtype = IntervalDtype('timedelta64[ns]')
|
||||
msg = 'Cannot convert .* to .*; subtypes are incompatible'
|
||||
|
||||
index = interval_range(Timestamp('2018-01-01'), periods=10)
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
index.astype(dtype)
|
||||
|
||||
index = interval_range(Timestamp('2018-01-01', tz='CET'), periods=10)
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
index.astype(dtype)
|
||||
|
||||
# timedelta -> datetime raises
|
||||
dtype = IntervalDtype('datetime64[ns]')
|
||||
index = interval_range(Timedelta('0 days'), periods=10)
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
index.astype(dtype)
|
||||
-363
@@ -1,363 +0,0 @@
|
||||
from __future__ import division
|
||||
|
||||
import pytest
|
||||
import numpy as np
|
||||
from functools import partial
|
||||
|
||||
from pandas import (
|
||||
Interval, IntervalIndex, Index, Int64Index, Float64Index, Categorical,
|
||||
CategoricalIndex, date_range, timedelta_range, period_range, notna)
|
||||
from pandas.compat import lzip
|
||||
from pandas.core.dtypes.common import is_categorical_dtype
|
||||
from pandas.core.dtypes.dtypes import IntervalDtype
|
||||
import pandas.core.common as com
|
||||
import pandas.util.testing as tm
|
||||
|
||||
|
||||
@pytest.fixture(params=['left', 'right', 'both', 'neither'])
|
||||
def closed(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(params=[None, 'foo'])
|
||||
def name(request):
|
||||
return request.param
|
||||
|
||||
|
||||
class Base(object):
|
||||
"""
|
||||
Common tests for all variations of IntervalIndex construction. Input data
|
||||
to be supplied in breaks format, then converted by the subclass method
|
||||
get_kwargs_from_breaks to the expected format.
|
||||
"""
|
||||
|
||||
@pytest.mark.parametrize('breaks', [
|
||||
[3, 14, 15, 92, 653],
|
||||
np.arange(10, dtype='int64'),
|
||||
Int64Index(range(-10, 11)),
|
||||
Float64Index(np.arange(20, 30, 0.5)),
|
||||
date_range('20180101', periods=10),
|
||||
date_range('20180101', periods=10, tz='US/Eastern'),
|
||||
timedelta_range('1 day', periods=10)])
|
||||
def test_constructor(self, constructor, breaks, closed, name):
|
||||
result_kwargs = self.get_kwargs_from_breaks(breaks, closed)
|
||||
result = constructor(closed=closed, name=name, **result_kwargs)
|
||||
|
||||
assert result.closed == closed
|
||||
assert result.name == name
|
||||
assert result.dtype.subtype == getattr(breaks, 'dtype', 'int64')
|
||||
tm.assert_index_equal(result.left, Index(breaks[:-1]))
|
||||
tm.assert_index_equal(result.right, Index(breaks[1:]))
|
||||
|
||||
@pytest.mark.parametrize('breaks, subtype', [
|
||||
(Int64Index([0, 1, 2, 3, 4]), 'float64'),
|
||||
(Int64Index([0, 1, 2, 3, 4]), 'datetime64[ns]'),
|
||||
(Int64Index([0, 1, 2, 3, 4]), 'timedelta64[ns]'),
|
||||
(Float64Index([0, 1, 2, 3, 4]), 'int64'),
|
||||
(date_range('2017-01-01', periods=5), 'int64'),
|
||||
(timedelta_range('1 day', periods=5), 'int64')])
|
||||
def test_constructor_dtype(self, constructor, breaks, subtype):
|
||||
# GH 19262: conversion via dtype parameter
|
||||
expected_kwargs = self.get_kwargs_from_breaks(breaks.astype(subtype))
|
||||
expected = constructor(**expected_kwargs)
|
||||
|
||||
result_kwargs = self.get_kwargs_from_breaks(breaks)
|
||||
iv_dtype = IntervalDtype(subtype)
|
||||
for dtype in (iv_dtype, str(iv_dtype)):
|
||||
result = constructor(dtype=dtype, **result_kwargs)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
@pytest.mark.parametrize('breaks', [
|
||||
[np.nan] * 2, [np.nan] * 4, [np.nan] * 50])
|
||||
def test_constructor_nan(self, constructor, breaks, closed):
|
||||
# GH 18421
|
||||
result_kwargs = self.get_kwargs_from_breaks(breaks)
|
||||
result = constructor(closed=closed, **result_kwargs)
|
||||
|
||||
expected_subtype = np.float64
|
||||
expected_values = np.array(breaks[:-1], dtype=object)
|
||||
|
||||
assert result.closed == closed
|
||||
assert result.dtype.subtype == expected_subtype
|
||||
tm.assert_numpy_array_equal(result.values, expected_values)
|
||||
|
||||
@pytest.mark.parametrize('breaks', [
|
||||
[],
|
||||
np.array([], dtype='int64'),
|
||||
np.array([], dtype='float64'),
|
||||
np.array([], dtype='datetime64[ns]'),
|
||||
np.array([], dtype='timedelta64[ns]')])
|
||||
def test_constructor_empty(self, constructor, breaks, closed):
|
||||
# GH 18421
|
||||
result_kwargs = self.get_kwargs_from_breaks(breaks)
|
||||
result = constructor(closed=closed, **result_kwargs)
|
||||
|
||||
expected_values = np.array([], dtype=object)
|
||||
expected_subtype = getattr(breaks, 'dtype', np.int64)
|
||||
|
||||
assert result.empty
|
||||
assert result.closed == closed
|
||||
assert result.dtype.subtype == expected_subtype
|
||||
tm.assert_numpy_array_equal(result.values, expected_values)
|
||||
|
||||
@pytest.mark.parametrize('breaks', [
|
||||
tuple('0123456789'),
|
||||
list('abcdefghij'),
|
||||
np.array(list('abcdefghij'), dtype=object),
|
||||
np.array(list('abcdefghij'), dtype='<U1')])
|
||||
def test_constructor_string(self, constructor, breaks):
|
||||
# GH 19016
|
||||
msg = ('category, object, and string subtypes are not supported '
|
||||
'for IntervalIndex')
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
constructor(**self.get_kwargs_from_breaks(breaks))
|
||||
|
||||
@pytest.mark.parametrize('cat_constructor', [
|
||||
Categorical, CategoricalIndex])
|
||||
def test_constructor_categorical_valid(self, constructor, cat_constructor):
|
||||
# GH 21243/21253
|
||||
if isinstance(constructor, partial) and constructor.func is Index:
|
||||
# Index is defined to create CategoricalIndex from categorical data
|
||||
pytest.skip()
|
||||
|
||||
breaks = np.arange(10, dtype='int64')
|
||||
expected = IntervalIndex.from_breaks(breaks)
|
||||
|
||||
cat_breaks = cat_constructor(breaks)
|
||||
result_kwargs = self.get_kwargs_from_breaks(cat_breaks)
|
||||
result = constructor(**result_kwargs)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
def test_generic_errors(self, constructor):
|
||||
# filler input data to be used when supplying invalid kwargs
|
||||
filler = self.get_kwargs_from_breaks(range(10))
|
||||
|
||||
# invalid closed
|
||||
msg = "invalid option for 'closed': invalid"
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
constructor(closed='invalid', **filler)
|
||||
|
||||
# unsupported dtype
|
||||
msg = 'dtype must be an IntervalDtype, got int64'
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
constructor(dtype='int64', **filler)
|
||||
|
||||
# invalid dtype
|
||||
msg = 'data type "invalid" not understood'
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
constructor(dtype='invalid', **filler)
|
||||
|
||||
# no point in nesting periods in an IntervalIndex
|
||||
periods = period_range('2000-01-01', periods=10)
|
||||
periods_kwargs = self.get_kwargs_from_breaks(periods)
|
||||
msg = 'Period dtypes are not supported, use a PeriodIndex instead'
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
constructor(**periods_kwargs)
|
||||
|
||||
# decreasing values
|
||||
decreasing_kwargs = self.get_kwargs_from_breaks(range(10, -1, -1))
|
||||
msg = 'left side of interval must be <= right side'
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
constructor(**decreasing_kwargs)
|
||||
|
||||
|
||||
class TestFromArrays(Base):
|
||||
"""Tests specific to IntervalIndex.from_arrays"""
|
||||
|
||||
@pytest.fixture
|
||||
def constructor(self):
|
||||
return IntervalIndex.from_arrays
|
||||
|
||||
def get_kwargs_from_breaks(self, breaks, closed='right'):
|
||||
"""
|
||||
converts intervals in breaks format to a dictionary of kwargs to
|
||||
specific to the format expected by IntervalIndex.from_arrays
|
||||
"""
|
||||
return {'left': breaks[:-1], 'right': breaks[1:]}
|
||||
|
||||
def test_constructor_errors(self):
|
||||
# GH 19016: categorical data
|
||||
data = Categorical(list('01234abcde'), ordered=True)
|
||||
msg = ('category, object, and string subtypes are not supported '
|
||||
'for IntervalIndex')
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
IntervalIndex.from_arrays(data[:-1], data[1:])
|
||||
|
||||
# unequal length
|
||||
left = [0, 1, 2]
|
||||
right = [2, 3]
|
||||
msg = 'left and right must have the same length'
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
IntervalIndex.from_arrays(left, right)
|
||||
|
||||
@pytest.mark.parametrize('left_subtype, right_subtype', [
|
||||
(np.int64, np.float64), (np.float64, np.int64)])
|
||||
def test_mixed_float_int(self, left_subtype, right_subtype):
|
||||
"""mixed int/float left/right results in float for both sides"""
|
||||
left = np.arange(9, dtype=left_subtype)
|
||||
right = np.arange(1, 10, dtype=right_subtype)
|
||||
result = IntervalIndex.from_arrays(left, right)
|
||||
|
||||
expected_left = Float64Index(left)
|
||||
expected_right = Float64Index(right)
|
||||
expected_subtype = np.float64
|
||||
|
||||
tm.assert_index_equal(result.left, expected_left)
|
||||
tm.assert_index_equal(result.right, expected_right)
|
||||
assert result.dtype.subtype == expected_subtype
|
||||
|
||||
|
||||
class TestFromBreaks(Base):
|
||||
"""Tests specific to IntervalIndex.from_breaks"""
|
||||
|
||||
@pytest.fixture
|
||||
def constructor(self):
|
||||
return IntervalIndex.from_breaks
|
||||
|
||||
def get_kwargs_from_breaks(self, breaks, closed='right'):
|
||||
"""
|
||||
converts intervals in breaks format to a dictionary of kwargs to
|
||||
specific to the format expected by IntervalIndex.from_breaks
|
||||
"""
|
||||
return {'breaks': breaks}
|
||||
|
||||
def test_constructor_errors(self):
|
||||
# GH 19016: categorical data
|
||||
data = Categorical(list('01234abcde'), ordered=True)
|
||||
msg = ('category, object, and string subtypes are not supported '
|
||||
'for IntervalIndex')
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
IntervalIndex.from_breaks(data)
|
||||
|
||||
def test_length_one(self):
|
||||
"""breaks of length one produce an empty IntervalIndex"""
|
||||
breaks = [0]
|
||||
result = IntervalIndex.from_breaks(breaks)
|
||||
expected = IntervalIndex.from_breaks([])
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
|
||||
class TestFromTuples(Base):
|
||||
"""Tests specific to IntervalIndex.from_tuples"""
|
||||
|
||||
@pytest.fixture
|
||||
def constructor(self):
|
||||
return IntervalIndex.from_tuples
|
||||
|
||||
def get_kwargs_from_breaks(self, breaks, closed='right'):
|
||||
"""
|
||||
converts intervals in breaks format to a dictionary of kwargs to
|
||||
specific to the format expected by IntervalIndex.from_tuples
|
||||
"""
|
||||
if len(breaks) == 0:
|
||||
return {'data': breaks}
|
||||
|
||||
tuples = lzip(breaks[:-1], breaks[1:])
|
||||
if isinstance(breaks, (list, tuple)):
|
||||
return {'data': tuples}
|
||||
elif is_categorical_dtype(breaks):
|
||||
return {'data': breaks._constructor(tuples)}
|
||||
return {'data': com._asarray_tuplesafe(tuples)}
|
||||
|
||||
def test_constructor_errors(self):
|
||||
# non-tuple
|
||||
tuples = [(0, 1), 2, (3, 4)]
|
||||
msg = 'IntervalIndex.from_tuples received an invalid item, 2'
|
||||
with tm.assert_raises_regex(TypeError, msg.format(t=tuples)):
|
||||
IntervalIndex.from_tuples(tuples)
|
||||
|
||||
# too few/many items
|
||||
tuples = [(0, 1), (2,), (3, 4)]
|
||||
msg = 'IntervalIndex.from_tuples requires tuples of length 2, got {t}'
|
||||
with tm.assert_raises_regex(ValueError, msg.format(t=tuples)):
|
||||
IntervalIndex.from_tuples(tuples)
|
||||
|
||||
tuples = [(0, 1), (2, 3, 4), (5, 6)]
|
||||
with tm.assert_raises_regex(ValueError, msg.format(t=tuples)):
|
||||
IntervalIndex.from_tuples(tuples)
|
||||
|
||||
def test_na_tuples(self):
|
||||
# tuple (NA, NA) evaluates the same as NA as an elemenent
|
||||
na_tuple = [(0, 1), (np.nan, np.nan), (2, 3)]
|
||||
idx_na_tuple = IntervalIndex.from_tuples(na_tuple)
|
||||
idx_na_element = IntervalIndex.from_tuples([(0, 1), np.nan, (2, 3)])
|
||||
tm.assert_index_equal(idx_na_tuple, idx_na_element)
|
||||
|
||||
|
||||
class TestClassConstructors(Base):
|
||||
"""Tests specific to the IntervalIndex/Index constructors"""
|
||||
|
||||
@pytest.fixture(params=[IntervalIndex, partial(Index, dtype='interval')],
|
||||
ids=['IntervalIndex', 'Index'])
|
||||
def constructor(self, request):
|
||||
return request.param
|
||||
|
||||
def get_kwargs_from_breaks(self, breaks, closed='right'):
|
||||
"""
|
||||
converts intervals in breaks format to a dictionary of kwargs to
|
||||
specific to the format expected by the IntervalIndex/Index constructors
|
||||
"""
|
||||
if len(breaks) == 0:
|
||||
return {'data': breaks}
|
||||
|
||||
ivs = [Interval(l, r, closed) if notna(l) else l
|
||||
for l, r in zip(breaks[:-1], breaks[1:])]
|
||||
|
||||
if isinstance(breaks, list):
|
||||
return {'data': ivs}
|
||||
elif is_categorical_dtype(breaks):
|
||||
return {'data': breaks._constructor(ivs)}
|
||||
return {'data': np.array(ivs, dtype=object)}
|
||||
|
||||
def test_generic_errors(self, constructor):
|
||||
"""
|
||||
override the base class implementation since errors are handled
|
||||
differently; checks unnecessary since caught at the Interval level
|
||||
"""
|
||||
pass
|
||||
|
||||
def test_constructor_errors(self, constructor):
|
||||
# mismatched closed inferred from intervals vs constructor.
|
||||
ivs = [Interval(0, 1, closed='both'), Interval(1, 2, closed='both')]
|
||||
msg = 'conflicting values for closed'
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
constructor(ivs, closed='neither')
|
||||
|
||||
# mismatched closed within intervals
|
||||
ivs = [Interval(0, 1, closed='right'), Interval(2, 3, closed='left')]
|
||||
msg = 'intervals must all be closed on the same side'
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
constructor(ivs)
|
||||
|
||||
# scalar
|
||||
msg = (r'IntervalIndex\(...\) must be called with a collection of '
|
||||
'some kind, 5 was passed')
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
constructor(5)
|
||||
|
||||
# not an interval
|
||||
msg = ("type <(class|type) 'numpy.int64'> with value 0 "
|
||||
"is not an interval")
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
constructor([0, 1])
|
||||
|
||||
|
||||
class TestFromIntervals(TestClassConstructors):
|
||||
"""
|
||||
Tests for IntervalIndex.from_intervals, which is deprecated in favor of the
|
||||
IntervalIndex constructor. Same tests as the IntervalIndex constructor,
|
||||
plus deprecation test. Should only need to delete this class when removed.
|
||||
"""
|
||||
|
||||
@pytest.fixture
|
||||
def constructor(self):
|
||||
def from_intervals_ignore_warnings(*args, **kwargs):
|
||||
with tm.assert_produces_warning(FutureWarning,
|
||||
check_stacklevel=False):
|
||||
return IntervalIndex.from_intervals(*args, **kwargs)
|
||||
return from_intervals_ignore_warnings
|
||||
|
||||
def test_deprecated(self):
|
||||
ivs = [Interval(0, 1), Interval(1, 2)]
|
||||
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
|
||||
IntervalIndex.from_intervals(ivs)
|
||||
-984
@@ -1,984 +0,0 @@
|
||||
from __future__ import division
|
||||
|
||||
import pytest
|
||||
import numpy as np
|
||||
from pandas import (
|
||||
Interval, IntervalIndex, Index, isna, notna, interval_range, Timestamp,
|
||||
Timedelta, date_range, timedelta_range)
|
||||
from pandas.compat import lzip
|
||||
import pandas.core.common as com
|
||||
from pandas.tests.indexes.common import Base
|
||||
import pandas.util.testing as tm
|
||||
import pandas as pd
|
||||
|
||||
|
||||
@pytest.fixture(scope='class', params=['left', 'right', 'both', 'neither'])
|
||||
def closed(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(scope='class', params=[None, 'foo'])
|
||||
def name(request):
|
||||
return request.param
|
||||
|
||||
|
||||
class TestIntervalIndex(Base):
|
||||
_holder = IntervalIndex
|
||||
|
||||
def setup_method(self, method):
|
||||
self.index = IntervalIndex.from_arrays([0, 1], [1, 2])
|
||||
self.index_with_nan = IntervalIndex.from_tuples(
|
||||
[(0, 1), np.nan, (1, 2)])
|
||||
self.indices = dict(intervalIndex=tm.makeIntervalIndex(10))
|
||||
|
||||
def create_index(self, closed='right'):
|
||||
return IntervalIndex.from_breaks(range(11), closed=closed)
|
||||
|
||||
def create_index_with_nan(self, closed='right'):
|
||||
mask = [True, False] + [True] * 8
|
||||
return IntervalIndex.from_arrays(
|
||||
np.where(mask, np.arange(10), np.nan),
|
||||
np.where(mask, np.arange(1, 11), np.nan), closed=closed)
|
||||
|
||||
def test_properties(self, closed):
|
||||
index = self.create_index(closed=closed)
|
||||
assert len(index) == 10
|
||||
assert index.size == 10
|
||||
assert index.shape == (10, )
|
||||
|
||||
tm.assert_index_equal(index.left, Index(np.arange(10)))
|
||||
tm.assert_index_equal(index.right, Index(np.arange(1, 11)))
|
||||
tm.assert_index_equal(index.mid, Index(np.arange(0.5, 10.5)))
|
||||
|
||||
assert index.closed == closed
|
||||
|
||||
ivs = [Interval(l, r, closed) for l, r in zip(range(10), range(1, 11))]
|
||||
expected = np.array(ivs, dtype=object)
|
||||
tm.assert_numpy_array_equal(np.asarray(index), expected)
|
||||
tm.assert_numpy_array_equal(index.values, expected)
|
||||
|
||||
# with nans
|
||||
index = self.create_index_with_nan(closed=closed)
|
||||
assert len(index) == 10
|
||||
assert index.size == 10
|
||||
assert index.shape == (10, )
|
||||
|
||||
expected_left = Index([0, np.nan, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||
expected_right = expected_left + 1
|
||||
expected_mid = expected_left + 0.5
|
||||
tm.assert_index_equal(index.left, expected_left)
|
||||
tm.assert_index_equal(index.right, expected_right)
|
||||
tm.assert_index_equal(index.mid, expected_mid)
|
||||
|
||||
assert index.closed == closed
|
||||
|
||||
ivs = [Interval(l, r, closed) if notna(l) else np.nan
|
||||
for l, r in zip(expected_left, expected_right)]
|
||||
expected = np.array(ivs, dtype=object)
|
||||
tm.assert_numpy_array_equal(np.asarray(index), expected)
|
||||
tm.assert_numpy_array_equal(index.values, expected)
|
||||
|
||||
@pytest.mark.parametrize('breaks', [
|
||||
[1, 1, 2, 5, 15, 53, 217, 1014, 5335, 31240, 201608],
|
||||
[-np.inf, -100, -10, 0.5, 1, 1.5, 3.8, 101, 202, np.inf],
|
||||
pd.to_datetime(['20170101', '20170202', '20170303', '20170404']),
|
||||
pd.to_timedelta(['1ns', '2ms', '3s', '4M', '5H', '6D'])])
|
||||
def test_length(self, closed, breaks):
|
||||
# GH 18789
|
||||
index = IntervalIndex.from_breaks(breaks, closed=closed)
|
||||
result = index.length
|
||||
expected = Index(iv.length for iv in index)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# with NA
|
||||
index = index.insert(1, np.nan)
|
||||
result = index.length
|
||||
expected = Index(iv.length if notna(iv) else iv for iv in index)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
def test_with_nans(self, closed):
|
||||
index = self.create_index(closed=closed)
|
||||
assert not index.hasnans
|
||||
|
||||
result = index.isna()
|
||||
expected = np.repeat(False, len(index))
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
result = index.notna()
|
||||
expected = np.repeat(True, len(index))
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
index = self.create_index_with_nan(closed=closed)
|
||||
assert index.hasnans
|
||||
|
||||
result = index.isna()
|
||||
expected = np.array([False, True] + [False] * (len(index) - 2))
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
result = index.notna()
|
||||
expected = np.array([True, False] + [True] * (len(index) - 2))
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
def test_copy(self, closed):
|
||||
expected = self.create_index(closed=closed)
|
||||
|
||||
result = expected.copy()
|
||||
assert result.equals(expected)
|
||||
|
||||
result = expected.copy(deep=True)
|
||||
assert result.equals(expected)
|
||||
assert result.left is not expected.left
|
||||
|
||||
def test_ensure_copied_data(self, closed):
|
||||
# exercise the copy flag in the constructor
|
||||
|
||||
# not copying
|
||||
index = self.create_index(closed=closed)
|
||||
result = IntervalIndex(index, copy=False)
|
||||
tm.assert_numpy_array_equal(index.left.values, result.left.values,
|
||||
check_same='same')
|
||||
tm.assert_numpy_array_equal(index.right.values, result.right.values,
|
||||
check_same='same')
|
||||
|
||||
# by-definition make a copy
|
||||
result = IntervalIndex(index.values, copy=False)
|
||||
tm.assert_numpy_array_equal(index.left.values, result.left.values,
|
||||
check_same='copy')
|
||||
tm.assert_numpy_array_equal(index.right.values, result.right.values,
|
||||
check_same='copy')
|
||||
|
||||
def test_equals(self, closed):
|
||||
expected = IntervalIndex.from_breaks(np.arange(5), closed=closed)
|
||||
assert expected.equals(expected)
|
||||
assert expected.equals(expected.copy())
|
||||
|
||||
assert not expected.equals(expected.astype(object))
|
||||
assert not expected.equals(np.array(expected))
|
||||
assert not expected.equals(list(expected))
|
||||
|
||||
assert not expected.equals([1, 2])
|
||||
assert not expected.equals(np.array([1, 2]))
|
||||
assert not expected.equals(pd.date_range('20130101', periods=2))
|
||||
|
||||
expected_name1 = IntervalIndex.from_breaks(
|
||||
np.arange(5), closed=closed, name='foo')
|
||||
expected_name2 = IntervalIndex.from_breaks(
|
||||
np.arange(5), closed=closed, name='bar')
|
||||
assert expected.equals(expected_name1)
|
||||
assert expected_name1.equals(expected_name2)
|
||||
|
||||
for other_closed in {'left', 'right', 'both', 'neither'} - {closed}:
|
||||
expected_other_closed = IntervalIndex.from_breaks(
|
||||
np.arange(5), closed=other_closed)
|
||||
assert not expected.equals(expected_other_closed)
|
||||
|
||||
@pytest.mark.parametrize('klass', [list, tuple, np.array, pd.Series])
|
||||
def test_where(self, closed, klass):
|
||||
idx = self.create_index(closed=closed)
|
||||
cond = [True] * len(idx)
|
||||
expected = idx
|
||||
result = expected.where(klass(cond))
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
cond = [False] + [True] * len(idx[1:])
|
||||
expected = IntervalIndex([np.nan] + idx[1:].tolist())
|
||||
result = idx.where(klass(cond))
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
def test_delete(self, closed):
|
||||
expected = IntervalIndex.from_breaks(np.arange(1, 11), closed=closed)
|
||||
result = self.create_index(closed=closed).delete(0)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
@pytest.mark.parametrize('data', [
|
||||
interval_range(0, periods=10, closed='neither'),
|
||||
interval_range(1.7, periods=8, freq=2.5, closed='both'),
|
||||
interval_range(Timestamp('20170101'), periods=12, closed='left'),
|
||||
interval_range(Timedelta('1 day'), periods=6, closed='right')])
|
||||
def test_insert(self, data):
|
||||
item = data[0]
|
||||
idx_item = IntervalIndex([item])
|
||||
|
||||
# start
|
||||
expected = idx_item.append(data)
|
||||
result = data.insert(0, item)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# end
|
||||
expected = data.append(idx_item)
|
||||
result = data.insert(len(data), item)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# mid
|
||||
expected = data[:3].append(idx_item).append(data[3:])
|
||||
result = data.insert(3, item)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# invalid type
|
||||
msg = 'can only insert Interval objects and NA into an IntervalIndex'
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
data.insert(1, 'foo')
|
||||
|
||||
# invalid closed
|
||||
msg = 'inserted item must be closed on the same side as the index'
|
||||
for closed in {'left', 'right', 'both', 'neither'} - {item.closed}:
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
bad_item = Interval(item.left, item.right, closed=closed)
|
||||
data.insert(1, bad_item)
|
||||
|
||||
# GH 18295 (test missing)
|
||||
na_idx = IntervalIndex([np.nan], closed=data.closed)
|
||||
for na in (np.nan, pd.NaT, None):
|
||||
expected = data[:1].append(na_idx).append(data[1:])
|
||||
result = data.insert(1, na)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
def test_take(self, closed):
|
||||
index = self.create_index(closed=closed)
|
||||
|
||||
result = index.take(range(10))
|
||||
tm.assert_index_equal(result, index)
|
||||
|
||||
result = index.take([0, 0, 1])
|
||||
expected = IntervalIndex.from_arrays(
|
||||
[0, 0, 1], [1, 1, 2], closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
def test_unique(self, closed):
|
||||
# unique non-overlapping
|
||||
idx = IntervalIndex.from_tuples(
|
||||
[(0, 1), (2, 3), (4, 5)], closed=closed)
|
||||
assert idx.is_unique
|
||||
|
||||
# unique overlapping - distinct endpoints
|
||||
idx = IntervalIndex.from_tuples([(0, 1), (0.5, 1.5)], closed=closed)
|
||||
assert idx.is_unique
|
||||
|
||||
# unique overlapping - shared endpoints
|
||||
idx = pd.IntervalIndex.from_tuples(
|
||||
[(1, 2), (1, 3), (2, 3)], closed=closed)
|
||||
assert idx.is_unique
|
||||
|
||||
# unique nested
|
||||
idx = IntervalIndex.from_tuples([(-1, 1), (-2, 2)], closed=closed)
|
||||
assert idx.is_unique
|
||||
|
||||
# duplicate
|
||||
idx = IntervalIndex.from_tuples(
|
||||
[(0, 1), (0, 1), (2, 3)], closed=closed)
|
||||
assert not idx.is_unique
|
||||
|
||||
# empty
|
||||
idx = IntervalIndex([], closed=closed)
|
||||
assert idx.is_unique
|
||||
|
||||
def test_monotonic(self, closed):
|
||||
# increasing non-overlapping
|
||||
idx = IntervalIndex.from_tuples(
|
||||
[(0, 1), (2, 3), (4, 5)], closed=closed)
|
||||
assert idx.is_monotonic
|
||||
assert idx._is_strictly_monotonic_increasing
|
||||
assert not idx.is_monotonic_decreasing
|
||||
assert not idx._is_strictly_monotonic_decreasing
|
||||
|
||||
# decreasing non-overlapping
|
||||
idx = IntervalIndex.from_tuples(
|
||||
[(4, 5), (2, 3), (1, 2)], closed=closed)
|
||||
assert not idx.is_monotonic
|
||||
assert not idx._is_strictly_monotonic_increasing
|
||||
assert idx.is_monotonic_decreasing
|
||||
assert idx._is_strictly_monotonic_decreasing
|
||||
|
||||
# unordered non-overlapping
|
||||
idx = IntervalIndex.from_tuples(
|
||||
[(0, 1), (4, 5), (2, 3)], closed=closed)
|
||||
assert not idx.is_monotonic
|
||||
assert not idx._is_strictly_monotonic_increasing
|
||||
assert not idx.is_monotonic_decreasing
|
||||
assert not idx._is_strictly_monotonic_decreasing
|
||||
|
||||
# increasing overlapping
|
||||
idx = IntervalIndex.from_tuples(
|
||||
[(0, 2), (0.5, 2.5), (1, 3)], closed=closed)
|
||||
assert idx.is_monotonic
|
||||
assert idx._is_strictly_monotonic_increasing
|
||||
assert not idx.is_monotonic_decreasing
|
||||
assert not idx._is_strictly_monotonic_decreasing
|
||||
|
||||
# decreasing overlapping
|
||||
idx = IntervalIndex.from_tuples(
|
||||
[(1, 3), (0.5, 2.5), (0, 2)], closed=closed)
|
||||
assert not idx.is_monotonic
|
||||
assert not idx._is_strictly_monotonic_increasing
|
||||
assert idx.is_monotonic_decreasing
|
||||
assert idx._is_strictly_monotonic_decreasing
|
||||
|
||||
# unordered overlapping
|
||||
idx = IntervalIndex.from_tuples(
|
||||
[(0.5, 2.5), (0, 2), (1, 3)], closed=closed)
|
||||
assert not idx.is_monotonic
|
||||
assert not idx._is_strictly_monotonic_increasing
|
||||
assert not idx.is_monotonic_decreasing
|
||||
assert not idx._is_strictly_monotonic_decreasing
|
||||
|
||||
# increasing overlapping shared endpoints
|
||||
idx = pd.IntervalIndex.from_tuples(
|
||||
[(1, 2), (1, 3), (2, 3)], closed=closed)
|
||||
assert idx.is_monotonic
|
||||
assert idx._is_strictly_monotonic_increasing
|
||||
assert not idx.is_monotonic_decreasing
|
||||
assert not idx._is_strictly_monotonic_decreasing
|
||||
|
||||
# decreasing overlapping shared endpoints
|
||||
idx = pd.IntervalIndex.from_tuples(
|
||||
[(2, 3), (1, 3), (1, 2)], closed=closed)
|
||||
assert not idx.is_monotonic
|
||||
assert not idx._is_strictly_monotonic_increasing
|
||||
assert idx.is_monotonic_decreasing
|
||||
assert idx._is_strictly_monotonic_decreasing
|
||||
|
||||
# stationary
|
||||
idx = IntervalIndex.from_tuples([(0, 1), (0, 1)], closed=closed)
|
||||
assert idx.is_monotonic
|
||||
assert not idx._is_strictly_monotonic_increasing
|
||||
assert idx.is_monotonic_decreasing
|
||||
assert not idx._is_strictly_monotonic_decreasing
|
||||
|
||||
# empty
|
||||
idx = IntervalIndex([], closed=closed)
|
||||
assert idx.is_monotonic
|
||||
assert idx._is_strictly_monotonic_increasing
|
||||
assert idx.is_monotonic_decreasing
|
||||
assert idx._is_strictly_monotonic_decreasing
|
||||
|
||||
@pytest.mark.skip(reason='not a valid repr as we use interval notation')
|
||||
def test_repr(self):
|
||||
i = IntervalIndex.from_tuples([(0, 1), (1, 2)], closed='right')
|
||||
expected = ("IntervalIndex(left=[0, 1],"
|
||||
"\n right=[1, 2],"
|
||||
"\n closed='right',"
|
||||
"\n dtype='interval[int64]')")
|
||||
assert repr(i) == expected
|
||||
|
||||
i = IntervalIndex.from_tuples((Timestamp('20130101'),
|
||||
Timestamp('20130102')),
|
||||
(Timestamp('20130102'),
|
||||
Timestamp('20130103')),
|
||||
closed='right')
|
||||
expected = ("IntervalIndex(left=['2013-01-01', '2013-01-02'],"
|
||||
"\n right=['2013-01-02', '2013-01-03'],"
|
||||
"\n closed='right',"
|
||||
"\n dtype='interval[datetime64[ns]]')")
|
||||
assert repr(i) == expected
|
||||
|
||||
@pytest.mark.skip(reason='not a valid repr as we use interval notation')
|
||||
def test_repr_max_seq_item_setting(self):
|
||||
super(TestIntervalIndex, self).test_repr_max_seq_item_setting()
|
||||
|
||||
@pytest.mark.skip(reason='not a valid repr as we use interval notation')
|
||||
def test_repr_roundtrip(self):
|
||||
super(TestIntervalIndex, self).test_repr_roundtrip()
|
||||
|
||||
# TODO: check this behavior is consistent with test_interval_new.py
|
||||
def test_get_item(self, closed):
|
||||
i = IntervalIndex.from_arrays((0, 1, np.nan), (1, 2, np.nan),
|
||||
closed=closed)
|
||||
assert i[0] == Interval(0.0, 1.0, closed=closed)
|
||||
assert i[1] == Interval(1.0, 2.0, closed=closed)
|
||||
assert isna(i[2])
|
||||
|
||||
result = i[0:1]
|
||||
expected = IntervalIndex.from_arrays((0.,), (1.,), closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
result = i[0:2]
|
||||
expected = IntervalIndex.from_arrays((0., 1), (1., 2.), closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
result = i[1:3]
|
||||
expected = IntervalIndex.from_arrays((1., np.nan), (2., np.nan),
|
||||
closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# To be removed, replaced by test_interval_new.py (see #16316, #16386)
|
||||
def test_get_loc_value(self):
|
||||
pytest.raises(KeyError, self.index.get_loc, 0)
|
||||
assert self.index.get_loc(0.5) == 0
|
||||
assert self.index.get_loc(1) == 0
|
||||
assert self.index.get_loc(1.5) == 1
|
||||
assert self.index.get_loc(2) == 1
|
||||
pytest.raises(KeyError, self.index.get_loc, -1)
|
||||
pytest.raises(KeyError, self.index.get_loc, 3)
|
||||
|
||||
idx = IntervalIndex.from_tuples([(0, 2), (1, 3)])
|
||||
assert idx.get_loc(0.5) == 0
|
||||
assert idx.get_loc(1) == 0
|
||||
tm.assert_numpy_array_equal(idx.get_loc(1.5),
|
||||
np.array([0, 1], dtype='int64'))
|
||||
tm.assert_numpy_array_equal(np.sort(idx.get_loc(2)),
|
||||
np.array([0, 1], dtype='int64'))
|
||||
assert idx.get_loc(3) == 1
|
||||
pytest.raises(KeyError, idx.get_loc, 3.5)
|
||||
|
||||
idx = IntervalIndex.from_arrays([0, 2], [1, 3])
|
||||
pytest.raises(KeyError, idx.get_loc, 1.5)
|
||||
|
||||
# To be removed, replaced by test_interval_new.py (see #16316, #16386)
|
||||
def slice_locs_cases(self, breaks):
|
||||
# TODO: same tests for more index types
|
||||
index = IntervalIndex.from_breaks([0, 1, 2], closed='right')
|
||||
assert index.slice_locs() == (0, 2)
|
||||
assert index.slice_locs(0, 1) == (0, 1)
|
||||
assert index.slice_locs(1, 1) == (0, 1)
|
||||
assert index.slice_locs(0, 2) == (0, 2)
|
||||
assert index.slice_locs(0.5, 1.5) == (0, 2)
|
||||
assert index.slice_locs(0, 0.5) == (0, 1)
|
||||
assert index.slice_locs(start=1) == (0, 2)
|
||||
assert index.slice_locs(start=1.2) == (1, 2)
|
||||
assert index.slice_locs(end=1) == (0, 1)
|
||||
assert index.slice_locs(end=1.1) == (0, 2)
|
||||
assert index.slice_locs(end=1.0) == (0, 1)
|
||||
assert index.slice_locs(-1, -1) == (0, 0)
|
||||
|
||||
index = IntervalIndex.from_breaks([0, 1, 2], closed='neither')
|
||||
assert index.slice_locs(0, 1) == (0, 1)
|
||||
assert index.slice_locs(0, 2) == (0, 2)
|
||||
assert index.slice_locs(0.5, 1.5) == (0, 2)
|
||||
assert index.slice_locs(1, 1) == (1, 1)
|
||||
assert index.slice_locs(1, 2) == (1, 2)
|
||||
|
||||
index = IntervalIndex.from_tuples([(0, 1), (2, 3), (4, 5)],
|
||||
closed='both')
|
||||
assert index.slice_locs(1, 1) == (0, 1)
|
||||
assert index.slice_locs(1, 2) == (0, 2)
|
||||
|
||||
# To be removed, replaced by test_interval_new.py (see #16316, #16386)
|
||||
def test_slice_locs_int64(self):
|
||||
self.slice_locs_cases([0, 1, 2])
|
||||
|
||||
# To be removed, replaced by test_interval_new.py (see #16316, #16386)
|
||||
def test_slice_locs_float64(self):
|
||||
self.slice_locs_cases([0.0, 1.0, 2.0])
|
||||
|
||||
# To be removed, replaced by test_interval_new.py (see #16316, #16386)
|
||||
def slice_locs_decreasing_cases(self, tuples):
|
||||
index = IntervalIndex.from_tuples(tuples)
|
||||
assert index.slice_locs(1.5, 0.5) == (1, 3)
|
||||
assert index.slice_locs(2, 0) == (1, 3)
|
||||
assert index.slice_locs(2, 1) == (1, 3)
|
||||
assert index.slice_locs(3, 1.1) == (0, 3)
|
||||
assert index.slice_locs(3, 3) == (0, 2)
|
||||
assert index.slice_locs(3.5, 3.3) == (0, 1)
|
||||
assert index.slice_locs(1, -3) == (2, 3)
|
||||
|
||||
slice_locs = index.slice_locs(-1, -1)
|
||||
assert slice_locs[0] == slice_locs[1]
|
||||
|
||||
# To be removed, replaced by test_interval_new.py (see #16316, #16386)
|
||||
def test_slice_locs_decreasing_int64(self):
|
||||
self.slice_locs_cases([(2, 4), (1, 3), (0, 2)])
|
||||
|
||||
# To be removed, replaced by test_interval_new.py (see #16316, #16386)
|
||||
def test_slice_locs_decreasing_float64(self):
|
||||
self.slice_locs_cases([(2., 4.), (1., 3.), (0., 2.)])
|
||||
|
||||
# To be removed, replaced by test_interval_new.py (see #16316, #16386)
|
||||
def test_slice_locs_fails(self):
|
||||
index = IntervalIndex.from_tuples([(1, 2), (0, 1), (2, 3)])
|
||||
with pytest.raises(KeyError):
|
||||
index.slice_locs(1, 2)
|
||||
|
||||
# To be removed, replaced by test_interval_new.py (see #16316, #16386)
|
||||
def test_get_loc_interval(self):
|
||||
assert self.index.get_loc(Interval(0, 1)) == 0
|
||||
assert self.index.get_loc(Interval(0, 0.5)) == 0
|
||||
assert self.index.get_loc(Interval(0, 1, 'left')) == 0
|
||||
pytest.raises(KeyError, self.index.get_loc, Interval(2, 3))
|
||||
pytest.raises(KeyError, self.index.get_loc,
|
||||
Interval(-1, 0, 'left'))
|
||||
|
||||
# Make consistent with test_interval_new.py (see #16316, #16386)
|
||||
@pytest.mark.parametrize('item', [3, Interval(1, 4)])
|
||||
def test_get_loc_length_one(self, item, closed):
|
||||
# GH 20921
|
||||
index = IntervalIndex.from_tuples([(0, 5)], closed=closed)
|
||||
result = index.get_loc(item)
|
||||
assert result == 0
|
||||
|
||||
# To be removed, replaced by test_interval_new.py (see #16316, #16386)
|
||||
def test_get_indexer(self):
|
||||
actual = self.index.get_indexer([-1, 0, 0.5, 1, 1.5, 2, 3])
|
||||
expected = np.array([-1, -1, 0, 0, 1, 1, -1], dtype='intp')
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
actual = self.index.get_indexer(self.index)
|
||||
expected = np.array([0, 1], dtype='intp')
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
index = IntervalIndex.from_breaks([0, 1, 2], closed='left')
|
||||
actual = index.get_indexer([-1, 0, 0.5, 1, 1.5, 2, 3])
|
||||
expected = np.array([-1, 0, 0, 1, 1, -1, -1], dtype='intp')
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
actual = self.index.get_indexer(index[:1])
|
||||
expected = np.array([0], dtype='intp')
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
actual = self.index.get_indexer(index)
|
||||
expected = np.array([-1, 1], dtype='intp')
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
# To be removed, replaced by test_interval_new.py (see #16316, #16386)
|
||||
def test_get_indexer_subintervals(self):
|
||||
|
||||
# TODO: is this right?
|
||||
# return indexers for wholly contained subintervals
|
||||
target = IntervalIndex.from_breaks(np.linspace(0, 2, 5))
|
||||
actual = self.index.get_indexer(target)
|
||||
expected = np.array([0, 0, 1, 1], dtype='p')
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
target = IntervalIndex.from_breaks([0, 0.67, 1.33, 2])
|
||||
actual = self.index.get_indexer(target)
|
||||
expected = np.array([0, 0, 1, 1], dtype='intp')
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
actual = self.index.get_indexer(target[[0, -1]])
|
||||
expected = np.array([0, 1], dtype='intp')
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
target = IntervalIndex.from_breaks([0, 0.33, 0.67, 1], closed='left')
|
||||
actual = self.index.get_indexer(target)
|
||||
expected = np.array([0, 0, 0], dtype='intp')
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
# Make consistent with test_interval_new.py (see #16316, #16386)
|
||||
@pytest.mark.parametrize('item', [
|
||||
[3], np.arange(1, 5), [Interval(1, 4)], interval_range(1, 4)])
|
||||
def test_get_indexer_length_one(self, item, closed):
|
||||
# GH 17284
|
||||
index = IntervalIndex.from_tuples([(0, 5)], closed=closed)
|
||||
result = index.get_indexer(item)
|
||||
expected = np.array([0] * len(item), dtype='intp')
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
# To be removed, replaced by test_interval_new.py (see #16316, #16386)
|
||||
def test_contains(self):
|
||||
# Only endpoints are valid.
|
||||
i = IntervalIndex.from_arrays([0, 1], [1, 2])
|
||||
|
||||
# Invalid
|
||||
assert 0 not in i
|
||||
assert 1 not in i
|
||||
assert 2 not in i
|
||||
|
||||
# Valid
|
||||
assert Interval(0, 1) in i
|
||||
assert Interval(0, 2) in i
|
||||
assert Interval(0, 0.5) in i
|
||||
assert Interval(3, 5) not in i
|
||||
assert Interval(-1, 0, closed='left') not in i
|
||||
|
||||
# To be removed, replaced by test_interval_new.py (see #16316, #16386)
|
||||
def testcontains(self):
|
||||
# can select values that are IN the range of a value
|
||||
i = IntervalIndex.from_arrays([0, 1], [1, 2])
|
||||
|
||||
assert i.contains(0.1)
|
||||
assert i.contains(0.5)
|
||||
assert i.contains(1)
|
||||
assert i.contains(Interval(0, 1))
|
||||
assert i.contains(Interval(0, 2))
|
||||
|
||||
# these overlaps completely
|
||||
assert i.contains(Interval(0, 3))
|
||||
assert i.contains(Interval(1, 3))
|
||||
|
||||
assert not i.contains(20)
|
||||
assert not i.contains(-20)
|
||||
|
||||
def test_dropna(self, closed):
|
||||
|
||||
expected = IntervalIndex.from_tuples(
|
||||
[(0.0, 1.0), (1.0, 2.0)], closed=closed)
|
||||
|
||||
ii = IntervalIndex.from_tuples([(0, 1), (1, 2), np.nan], closed=closed)
|
||||
result = ii.dropna()
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
ii = IntervalIndex.from_arrays(
|
||||
[0, 1, np.nan], [1, 2, np.nan], closed=closed)
|
||||
result = ii.dropna()
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# TODO: check this behavior is consistent with test_interval_new.py
|
||||
def test_non_contiguous(self, closed):
|
||||
index = IntervalIndex.from_tuples([(0, 1), (2, 3)], closed=closed)
|
||||
target = [0.5, 1.5, 2.5]
|
||||
actual = index.get_indexer(target)
|
||||
expected = np.array([0, -1, 1], dtype='intp')
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
assert 1.5 not in index
|
||||
|
||||
def test_union(self, closed):
|
||||
index = self.create_index(closed=closed)
|
||||
other = IntervalIndex.from_breaks(range(5, 13), closed=closed)
|
||||
|
||||
expected = IntervalIndex.from_breaks(range(13), closed=closed)
|
||||
result = index.union(other)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
result = other.union(index)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
tm.assert_index_equal(index.union(index), index)
|
||||
tm.assert_index_equal(index.union(index[:1]), index)
|
||||
|
||||
# GH 19101: empty result, same dtype
|
||||
index = IntervalIndex(np.array([], dtype='int64'), closed=closed)
|
||||
result = index.union(index)
|
||||
tm.assert_index_equal(result, index)
|
||||
|
||||
# GH 19101: empty result, different dtypes
|
||||
other = IntervalIndex(np.array([], dtype='float64'), closed=closed)
|
||||
result = index.union(other)
|
||||
tm.assert_index_equal(result, index)
|
||||
|
||||
def test_intersection(self, closed):
|
||||
index = self.create_index(closed=closed)
|
||||
other = IntervalIndex.from_breaks(range(5, 13), closed=closed)
|
||||
|
||||
expected = IntervalIndex.from_breaks(range(5, 11), closed=closed)
|
||||
result = index.intersection(other)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
result = other.intersection(index)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
tm.assert_index_equal(index.intersection(index), index)
|
||||
|
||||
# GH 19101: empty result, same dtype
|
||||
other = IntervalIndex.from_breaks(range(300, 314), closed=closed)
|
||||
expected = IntervalIndex(np.array([], dtype='int64'), closed=closed)
|
||||
result = index.intersection(other)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# GH 19101: empty result, different dtypes
|
||||
breaks = np.arange(300, 314, dtype='float64')
|
||||
other = IntervalIndex.from_breaks(breaks, closed=closed)
|
||||
result = index.intersection(other)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
def test_difference(self, closed):
|
||||
index = self.create_index(closed=closed)
|
||||
tm.assert_index_equal(index.difference(index[:1]), index[1:])
|
||||
|
||||
# GH 19101: empty result, same dtype
|
||||
result = index.difference(index)
|
||||
expected = IntervalIndex(np.array([], dtype='int64'), closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# GH 19101: empty result, different dtypes
|
||||
other = IntervalIndex.from_arrays(index.left.astype('float64'),
|
||||
index.right, closed=closed)
|
||||
result = index.difference(other)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
def test_symmetric_difference(self, closed):
|
||||
index = self.create_index(closed=closed)
|
||||
result = index[1:].symmetric_difference(index[:-1])
|
||||
expected = IntervalIndex([index[0], index[-1]])
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# GH 19101: empty result, same dtype
|
||||
result = index.symmetric_difference(index)
|
||||
expected = IntervalIndex(np.array([], dtype='int64'), closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# GH 19101: empty result, different dtypes
|
||||
other = IntervalIndex.from_arrays(index.left.astype('float64'),
|
||||
index.right, closed=closed)
|
||||
result = index.symmetric_difference(other)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
@pytest.mark.parametrize('op_name', [
|
||||
'union', 'intersection', 'difference', 'symmetric_difference'])
|
||||
def test_set_operation_errors(self, closed, op_name):
|
||||
index = self.create_index(closed=closed)
|
||||
set_op = getattr(index, op_name)
|
||||
|
||||
# non-IntervalIndex
|
||||
msg = ('the other index needs to be an IntervalIndex too, but '
|
||||
'was type Int64Index')
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
set_op(Index([1, 2, 3]))
|
||||
|
||||
# mixed closed
|
||||
msg = ('can only do set operations between two IntervalIndex objects '
|
||||
'that are closed on the same side')
|
||||
for other_closed in {'right', 'left', 'both', 'neither'} - {closed}:
|
||||
other = self.create_index(closed=other_closed)
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
set_op(other)
|
||||
|
||||
# GH 19016: incompatible dtypes
|
||||
other = interval_range(Timestamp('20180101'), periods=9, closed=closed)
|
||||
msg = ('can only do {op} between two IntervalIndex objects that have '
|
||||
'compatible dtypes').format(op=op_name)
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
set_op(other)
|
||||
|
||||
def test_isin(self, closed):
|
||||
index = self.create_index(closed=closed)
|
||||
|
||||
expected = np.array([True] + [False] * (len(index) - 1))
|
||||
result = index.isin(index[:1])
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
result = index.isin([index[0]])
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
other = IntervalIndex.from_breaks(np.arange(-2, 10), closed=closed)
|
||||
expected = np.array([True] * (len(index) - 1) + [False])
|
||||
result = index.isin(other)
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
result = index.isin(other.tolist())
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
for other_closed in {'right', 'left', 'both', 'neither'}:
|
||||
other = self.create_index(closed=other_closed)
|
||||
expected = np.repeat(closed == other_closed, len(index))
|
||||
result = index.isin(other)
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
result = index.isin(other.tolist())
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
def test_comparison(self):
|
||||
actual = Interval(0, 1) < self.index
|
||||
expected = np.array([False, True])
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
actual = Interval(0.5, 1.5) < self.index
|
||||
expected = np.array([False, True])
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
actual = self.index > Interval(0.5, 1.5)
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
actual = self.index == self.index
|
||||
expected = np.array([True, True])
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
actual = self.index <= self.index
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
actual = self.index >= self.index
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
actual = self.index < self.index
|
||||
expected = np.array([False, False])
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
actual = self.index > self.index
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
actual = self.index == IntervalIndex.from_breaks([0, 1, 2], 'left')
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
actual = self.index == self.index.values
|
||||
tm.assert_numpy_array_equal(actual, np.array([True, True]))
|
||||
actual = self.index.values == self.index
|
||||
tm.assert_numpy_array_equal(actual, np.array([True, True]))
|
||||
actual = self.index <= self.index.values
|
||||
tm.assert_numpy_array_equal(actual, np.array([True, True]))
|
||||
actual = self.index != self.index.values
|
||||
tm.assert_numpy_array_equal(actual, np.array([False, False]))
|
||||
actual = self.index > self.index.values
|
||||
tm.assert_numpy_array_equal(actual, np.array([False, False]))
|
||||
actual = self.index.values > self.index
|
||||
tm.assert_numpy_array_equal(actual, np.array([False, False]))
|
||||
|
||||
# invalid comparisons
|
||||
actual = self.index == 0
|
||||
tm.assert_numpy_array_equal(actual, np.array([False, False]))
|
||||
actual = self.index == self.index.left
|
||||
tm.assert_numpy_array_equal(actual, np.array([False, False]))
|
||||
|
||||
with tm.assert_raises_regex(TypeError, 'unorderable types'):
|
||||
self.index > 0
|
||||
with tm.assert_raises_regex(TypeError, 'unorderable types'):
|
||||
self.index <= 0
|
||||
with pytest.raises(TypeError):
|
||||
self.index > np.arange(2)
|
||||
with pytest.raises(ValueError):
|
||||
self.index > np.arange(3)
|
||||
|
||||
def test_missing_values(self, closed):
|
||||
idx = Index([np.nan, Interval(0, 1, closed=closed),
|
||||
Interval(1, 2, closed=closed)])
|
||||
idx2 = IntervalIndex.from_arrays(
|
||||
[np.nan, 0, 1], [np.nan, 1, 2], closed=closed)
|
||||
assert idx.equals(idx2)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
IntervalIndex.from_arrays(
|
||||
[np.nan, 0, 1], np.array([0, 1, 2]), closed=closed)
|
||||
|
||||
tm.assert_numpy_array_equal(isna(idx),
|
||||
np.array([True, False, False]))
|
||||
|
||||
def test_sort_values(self, closed):
|
||||
index = self.create_index(closed=closed)
|
||||
|
||||
result = index.sort_values()
|
||||
tm.assert_index_equal(result, index)
|
||||
|
||||
result = index.sort_values(ascending=False)
|
||||
tm.assert_index_equal(result, index[::-1])
|
||||
|
||||
# with nan
|
||||
index = IntervalIndex([Interval(1, 2), np.nan, Interval(0, 1)])
|
||||
|
||||
result = index.sort_values()
|
||||
expected = IntervalIndex([Interval(0, 1), Interval(1, 2), np.nan])
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
result = index.sort_values(ascending=False)
|
||||
expected = IntervalIndex([np.nan, Interval(1, 2), Interval(0, 1)])
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
@pytest.mark.parametrize('tz', [None, 'US/Eastern'])
|
||||
def test_datetime(self, tz):
|
||||
start = Timestamp('2000-01-01', tz=tz)
|
||||
dates = date_range(start=start, periods=10)
|
||||
index = IntervalIndex.from_breaks(dates)
|
||||
|
||||
# test mid
|
||||
start = Timestamp('2000-01-01T12:00', tz=tz)
|
||||
expected = date_range(start=start, periods=9)
|
||||
tm.assert_index_equal(index.mid, expected)
|
||||
|
||||
# __contains__ doesn't check individual points
|
||||
assert Timestamp('2000-01-01', tz=tz) not in index
|
||||
assert Timestamp('2000-01-01T12', tz=tz) not in index
|
||||
assert Timestamp('2000-01-02', tz=tz) not in index
|
||||
iv_true = Interval(Timestamp('2000-01-01T08', tz=tz),
|
||||
Timestamp('2000-01-01T18', tz=tz))
|
||||
iv_false = Interval(Timestamp('1999-12-31', tz=tz),
|
||||
Timestamp('2000-01-01', tz=tz))
|
||||
assert iv_true in index
|
||||
assert iv_false not in index
|
||||
|
||||
# .contains does check individual points
|
||||
assert not index.contains(Timestamp('2000-01-01', tz=tz))
|
||||
assert index.contains(Timestamp('2000-01-01T12', tz=tz))
|
||||
assert index.contains(Timestamp('2000-01-02', tz=tz))
|
||||
assert index.contains(iv_true)
|
||||
assert not index.contains(iv_false)
|
||||
|
||||
# test get_indexer
|
||||
start = Timestamp('1999-12-31T12:00', tz=tz)
|
||||
target = date_range(start=start, periods=7, freq='12H')
|
||||
actual = index.get_indexer(target)
|
||||
expected = np.array([-1, -1, 0, 0, 1, 1, 2], dtype='intp')
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
start = Timestamp('2000-01-08T18:00', tz=tz)
|
||||
target = date_range(start=start, periods=7, freq='6H')
|
||||
actual = index.get_indexer(target)
|
||||
expected = np.array([7, 7, 8, 8, 8, 8, -1], dtype='intp')
|
||||
tm.assert_numpy_array_equal(actual, expected)
|
||||
|
||||
def test_append(self, closed):
|
||||
|
||||
index1 = IntervalIndex.from_arrays([0, 1], [1, 2], closed=closed)
|
||||
index2 = IntervalIndex.from_arrays([1, 2], [2, 3], closed=closed)
|
||||
|
||||
result = index1.append(index2)
|
||||
expected = IntervalIndex.from_arrays(
|
||||
[0, 1, 1, 2], [1, 2, 2, 3], closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
result = index1.append([index1, index2])
|
||||
expected = IntervalIndex.from_arrays(
|
||||
[0, 1, 0, 1, 1, 2], [1, 2, 1, 2, 2, 3], closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
msg = ('can only append two IntervalIndex objects that are closed '
|
||||
'on the same side')
|
||||
for other_closed in {'left', 'right', 'both', 'neither'} - {closed}:
|
||||
index_other_closed = IntervalIndex.from_arrays(
|
||||
[0, 1], [1, 2], closed=other_closed)
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
index1.append(index_other_closed)
|
||||
|
||||
def test_is_non_overlapping_monotonic(self, closed):
|
||||
# Should be True in all cases
|
||||
tpls = [(0, 1), (2, 3), (4, 5), (6, 7)]
|
||||
idx = IntervalIndex.from_tuples(tpls, closed=closed)
|
||||
assert idx.is_non_overlapping_monotonic is True
|
||||
|
||||
idx = IntervalIndex.from_tuples(tpls[::-1], closed=closed)
|
||||
assert idx.is_non_overlapping_monotonic is True
|
||||
|
||||
# Should be False in all cases (overlapping)
|
||||
tpls = [(0, 2), (1, 3), (4, 5), (6, 7)]
|
||||
idx = IntervalIndex.from_tuples(tpls, closed=closed)
|
||||
assert idx.is_non_overlapping_monotonic is False
|
||||
|
||||
idx = IntervalIndex.from_tuples(tpls[::-1], closed=closed)
|
||||
assert idx.is_non_overlapping_monotonic is False
|
||||
|
||||
# Should be False in all cases (non-monotonic)
|
||||
tpls = [(0, 1), (2, 3), (6, 7), (4, 5)]
|
||||
idx = IntervalIndex.from_tuples(tpls, closed=closed)
|
||||
assert idx.is_non_overlapping_monotonic is False
|
||||
|
||||
idx = IntervalIndex.from_tuples(tpls[::-1], closed=closed)
|
||||
assert idx.is_non_overlapping_monotonic is False
|
||||
|
||||
# Should be False for closed='both', otherwise True (GH16560)
|
||||
if closed == 'both':
|
||||
idx = IntervalIndex.from_breaks(range(4), closed=closed)
|
||||
assert idx.is_non_overlapping_monotonic is False
|
||||
else:
|
||||
idx = IntervalIndex.from_breaks(range(4), closed=closed)
|
||||
assert idx.is_non_overlapping_monotonic is True
|
||||
|
||||
@pytest.mark.parametrize('tuples', [
|
||||
lzip(range(10), range(1, 11)),
|
||||
lzip(date_range('20170101', periods=10),
|
||||
date_range('20170101', periods=10)),
|
||||
lzip(timedelta_range('0 days', periods=10),
|
||||
timedelta_range('1 day', periods=10))])
|
||||
def test_to_tuples(self, tuples):
|
||||
# GH 18756
|
||||
idx = IntervalIndex.from_tuples(tuples)
|
||||
result = idx.to_tuples()
|
||||
expected = Index(com._asarray_tuplesafe(tuples))
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
@pytest.mark.parametrize('tuples', [
|
||||
lzip(range(10), range(1, 11)) + [np.nan],
|
||||
lzip(date_range('20170101', periods=10),
|
||||
date_range('20170101', periods=10)) + [np.nan],
|
||||
lzip(timedelta_range('0 days', periods=10),
|
||||
timedelta_range('1 day', periods=10)) + [np.nan]])
|
||||
@pytest.mark.parametrize('na_tuple', [True, False])
|
||||
def test_to_tuples_na(self, tuples, na_tuple):
|
||||
# GH 18756
|
||||
idx = IntervalIndex.from_tuples(tuples)
|
||||
result = idx.to_tuples(na_tuple=na_tuple)
|
||||
|
||||
# check the non-NA portion
|
||||
expected_notna = Index(com._asarray_tuplesafe(tuples[:-1]))
|
||||
result_notna = result[:-1]
|
||||
tm.assert_index_equal(result_notna, expected_notna)
|
||||
|
||||
# check the NA portion
|
||||
result_na = result[-1]
|
||||
if na_tuple:
|
||||
assert isinstance(result_na, tuple)
|
||||
assert len(result_na) == 2
|
||||
assert all(isna(x) for x in result_na)
|
||||
else:
|
||||
assert isna(result_na)
|
||||
-315
@@ -1,315 +0,0 @@
|
||||
from __future__ import division
|
||||
|
||||
import pytest
|
||||
import numpy as np
|
||||
|
||||
from pandas import Interval, IntervalIndex, Int64Index
|
||||
import pandas.util.testing as tm
|
||||
|
||||
|
||||
pytestmark = pytest.mark.skip(reason="new indexing tests for issue 16316")
|
||||
|
||||
|
||||
class TestIntervalIndex(object):
|
||||
|
||||
def _compare_tuple_of_numpy_array(self, result, expected):
|
||||
lidx, ridx = result
|
||||
lidx_expected, ridx_expected = expected
|
||||
|
||||
tm.assert_numpy_array_equal(lidx, lidx_expected)
|
||||
tm.assert_numpy_array_equal(ridx, ridx_expected)
|
||||
|
||||
@pytest.mark.parametrize("idx_side", ['right', 'left', 'both', 'neither'])
|
||||
@pytest.mark.parametrize("side", ['right', 'left', 'both', 'neither'])
|
||||
def test_get_loc_interval(self, idx_side, side):
|
||||
|
||||
idx = IntervalIndex.from_tuples([(0, 1), (2, 3)], closed=idx_side)
|
||||
|
||||
for bound in [[0, 1], [1, 2], [2, 3], [3, 4],
|
||||
[0, 2], [2.5, 3], [-1, 4]]:
|
||||
# if get_loc is supplied an interval, it should only search
|
||||
# for exact matches, not overlaps or covers, else KeyError.
|
||||
if idx_side == side:
|
||||
if bound == [0, 1]:
|
||||
assert idx.get_loc(Interval(0, 1, closed=side)) == 0
|
||||
elif bound == [2, 3]:
|
||||
assert idx.get_loc(Interval(2, 3, closed=side)) == 1
|
||||
else:
|
||||
with pytest.raises(KeyError):
|
||||
idx.get_loc(Interval(*bound, closed=side))
|
||||
else:
|
||||
with pytest.raises(KeyError):
|
||||
idx.get_loc(Interval(*bound, closed=side))
|
||||
|
||||
@pytest.mark.parametrize("idx_side", ['right', 'left', 'both', 'neither'])
|
||||
@pytest.mark.parametrize("scalar", [-0.5, 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5])
|
||||
def test_get_loc_scalar(self, idx_side, scalar):
|
||||
|
||||
# correct = {side: {query: answer}}.
|
||||
# If query is not in the dict, that query should raise a KeyError
|
||||
correct = {'right': {0.5: 0, 1: 0, 2.5: 1, 3: 1},
|
||||
'left': {0: 0, 0.5: 0, 2: 1, 2.5: 1},
|
||||
'both': {0: 0, 0.5: 0, 1: 0, 2: 1, 2.5: 1, 3: 1},
|
||||
'neither': {0.5: 0, 2.5: 1}}
|
||||
|
||||
idx = IntervalIndex.from_tuples([(0, 1), (2, 3)], closed=idx_side)
|
||||
|
||||
# if get_loc is supplied a scalar, it should return the index of
|
||||
# the interval which contains the scalar, or KeyError.
|
||||
if scalar in correct[idx_side].keys():
|
||||
assert idx.get_loc(scalar) == correct[idx_side][scalar]
|
||||
else:
|
||||
pytest.raises(KeyError, idx.get_loc, scalar)
|
||||
|
||||
def test_slice_locs_with_interval(self):
|
||||
|
||||
# increasing monotonically
|
||||
index = IntervalIndex.from_tuples([(0, 2), (1, 3), (2, 4)])
|
||||
|
||||
assert index.slice_locs(
|
||||
start=Interval(0, 2), end=Interval(2, 4)) == (0, 3)
|
||||
assert index.slice_locs(start=Interval(0, 2)) == (0, 3)
|
||||
assert index.slice_locs(end=Interval(2, 4)) == (0, 3)
|
||||
assert index.slice_locs(end=Interval(0, 2)) == (0, 1)
|
||||
assert index.slice_locs(
|
||||
start=Interval(2, 4), end=Interval(0, 2)) == (2, 1)
|
||||
|
||||
# decreasing monotonically
|
||||
index = IntervalIndex.from_tuples([(2, 4), (1, 3), (0, 2)])
|
||||
|
||||
assert index.slice_locs(
|
||||
start=Interval(0, 2), end=Interval(2, 4)) == (2, 1)
|
||||
assert index.slice_locs(start=Interval(0, 2)) == (2, 3)
|
||||
assert index.slice_locs(end=Interval(2, 4)) == (0, 1)
|
||||
assert index.slice_locs(end=Interval(0, 2)) == (0, 3)
|
||||
assert index.slice_locs(
|
||||
start=Interval(2, 4), end=Interval(0, 2)) == (0, 3)
|
||||
|
||||
# sorted duplicates
|
||||
index = IntervalIndex.from_tuples([(0, 2), (0, 2), (2, 4)])
|
||||
|
||||
assert index.slice_locs(
|
||||
start=Interval(0, 2), end=Interval(2, 4)) == (0, 3)
|
||||
assert index.slice_locs(start=Interval(0, 2)) == (0, 3)
|
||||
assert index.slice_locs(end=Interval(2, 4)) == (0, 3)
|
||||
assert index.slice_locs(end=Interval(0, 2)) == (0, 2)
|
||||
assert index.slice_locs(
|
||||
start=Interval(2, 4), end=Interval(0, 2)) == (2, 2)
|
||||
|
||||
# unsorted duplicates
|
||||
index = IntervalIndex.from_tuples([(0, 2), (2, 4), (0, 2)])
|
||||
|
||||
pytest.raises(KeyError, index.slice_locs(
|
||||
start=Interval(0, 2), end=Interval(2, 4)))
|
||||
pytest.raises(KeyError, index.slice_locs(start=Interval(0, 2)))
|
||||
assert index.slice_locs(end=Interval(2, 4)) == (0, 2)
|
||||
pytest.raises(KeyError, index.slice_locs(end=Interval(0, 2)))
|
||||
pytest.raises(KeyError, index.slice_locs(
|
||||
start=Interval(2, 4), end=Interval(0, 2)))
|
||||
|
||||
# another unsorted duplicates
|
||||
index = IntervalIndex.from_tuples([(0, 2), (0, 2), (2, 4), (1, 3)])
|
||||
|
||||
assert index.slice_locs(
|
||||
start=Interval(0, 2), end=Interval(2, 4)) == (0, 3)
|
||||
assert index.slice_locs(start=Interval(0, 2)) == (0, 4)
|
||||
assert index.slice_locs(end=Interval(2, 4)) == (0, 3)
|
||||
assert index.slice_locs(end=Interval(0, 2)) == (0, 2)
|
||||
assert index.slice_locs(
|
||||
start=Interval(2, 4), end=Interval(0, 2)) == (2, 2)
|
||||
|
||||
def test_slice_locs_with_ints_and_floats_succeeds(self):
|
||||
|
||||
# increasing non-overlapping
|
||||
index = IntervalIndex.from_tuples([(0, 1), (1, 2), (3, 4)])
|
||||
|
||||
assert index.slice_locs(0, 1) == (0, 1)
|
||||
assert index.slice_locs(0, 2) == (0, 2)
|
||||
assert index.slice_locs(0, 3) == (0, 2)
|
||||
assert index.slice_locs(3, 1) == (2, 1)
|
||||
assert index.slice_locs(3, 4) == (2, 3)
|
||||
assert index.slice_locs(0, 4) == (0, 3)
|
||||
|
||||
# decreasing non-overlapping
|
||||
index = IntervalIndex.from_tuples([(3, 4), (1, 2), (0, 1)])
|
||||
assert index.slice_locs(0, 1) == (3, 2)
|
||||
assert index.slice_locs(0, 2) == (3, 1)
|
||||
assert index.slice_locs(0, 3) == (3, 1)
|
||||
assert index.slice_locs(3, 1) == (1, 2)
|
||||
assert index.slice_locs(3, 4) == (1, 0)
|
||||
assert index.slice_locs(0, 4) == (3, 0)
|
||||
|
||||
@pytest.mark.parametrize("query", [[0, 1], [0, 2], [0, 3],
|
||||
[3, 1], [3, 4], [0, 4]])
|
||||
def test_slice_locs_with_ints_and_floats_fails(self, query):
|
||||
|
||||
# increasing overlapping
|
||||
index = IntervalIndex.from_tuples([(0, 2), (1, 3), (2, 4)])
|
||||
pytest.raises(KeyError, index.slice_locs, query)
|
||||
|
||||
# decreasing overlapping
|
||||
index = IntervalIndex.from_tuples([(2, 4), (1, 3), (0, 2)])
|
||||
pytest.raises(KeyError, index.slice_locs, query)
|
||||
|
||||
# sorted duplicates
|
||||
index = IntervalIndex.from_tuples([(0, 2), (0, 2), (2, 4)])
|
||||
pytest.raises(KeyError, index.slice_locs, query)
|
||||
|
||||
# unsorted duplicates
|
||||
index = IntervalIndex.from_tuples([(0, 2), (2, 4), (0, 2)])
|
||||
pytest.raises(KeyError, index.slice_locs, query)
|
||||
|
||||
# another unsorted duplicates
|
||||
index = IntervalIndex.from_tuples([(0, 2), (0, 2), (2, 4), (1, 3)])
|
||||
pytest.raises(KeyError, index.slice_locs, query)
|
||||
|
||||
@pytest.mark.parametrize("query", [
|
||||
Interval(1, 3, closed='right'),
|
||||
Interval(1, 3, closed='left'),
|
||||
Interval(1, 3, closed='both'),
|
||||
Interval(1, 3, closed='neither'),
|
||||
Interval(1, 4, closed='right'),
|
||||
Interval(0, 4, closed='right'),
|
||||
Interval(1, 2, closed='right')])
|
||||
@pytest.mark.parametrize("expected_result", [1, -1, -1, -1, -1, -1, -1])
|
||||
def test_get_indexer_with_interval_single_queries(
|
||||
self, query, expected_result):
|
||||
|
||||
index = IntervalIndex.from_tuples(
|
||||
[(0, 2.5), (1, 3), (2, 4)], closed='right')
|
||||
|
||||
result = index.get_indexer([query])
|
||||
expect = np.array([expected_result], dtype='intp')
|
||||
tm.assert_numpy_array_equal(result, expect)
|
||||
|
||||
@pytest.mark.parametrize("query", [
|
||||
[Interval(2, 4, closed='right'), Interval(1, 3, closed='right')],
|
||||
[Interval(1, 3, closed='right'), Interval(0, 2, closed='right')],
|
||||
[Interval(1, 3, closed='right'), Interval(1, 3, closed='left')]])
|
||||
@pytest.mark.parametrize("expected_result", [[2, 1], [1, -1], [1, -1]])
|
||||
def test_get_indexer_with_interval_multiple_queries(
|
||||
self, query, expected_result):
|
||||
|
||||
index = IntervalIndex.from_tuples(
|
||||
[(0, 2.5), (1, 3), (2, 4)], closed='right')
|
||||
|
||||
result = index.get_indexer(query)
|
||||
expect = np.array(expected_result, dtype='intp')
|
||||
tm.assert_numpy_array_equal(result, expect)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"query",
|
||||
[-0.5, 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5])
|
||||
@pytest.mark.parametrize(
|
||||
"expected_result",
|
||||
[-1, -1, 0, 0, 1, 1, -1, -1, 2, 2, -1])
|
||||
def test_get_indexer_with_ints_and_floats_single_queries(
|
||||
self, query, expected_result):
|
||||
|
||||
index = IntervalIndex.from_tuples(
|
||||
[(0, 1), (1, 2), (3, 4)], closed='right')
|
||||
|
||||
result = index.get_indexer([query])
|
||||
expect = np.array([expected_result], dtype='intp')
|
||||
tm.assert_numpy_array_equal(result, expect)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"query",
|
||||
[[1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 2]])
|
||||
@pytest.mark.parametrize(
|
||||
"expected_result",
|
||||
[[0, 1], [0, 1, -1], [0, 1, -1, 2], [0, 1, -1, 2, 1]])
|
||||
def test_get_indexer_with_ints_and_floats_multiple_queries(
|
||||
self, query, expected_result):
|
||||
|
||||
index = IntervalIndex.from_tuples(
|
||||
[(0, 1), (1, 2), (3, 4)], closed='right')
|
||||
|
||||
result = index.get_indexer(query)
|
||||
expect = np.array(expected_result, dtype='intp')
|
||||
tm.assert_numpy_array_equal(result, expect)
|
||||
|
||||
index = IntervalIndex.from_tuples([(0, 2), (1, 3), (2, 4)])
|
||||
# TODO: @shoyer believes this should raise, master branch doesn't
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"query",
|
||||
[-0.5, 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5])
|
||||
@pytest.mark.parametrize("expected_result", [
|
||||
(Int64Index([], dtype='int64'), np.array([0])),
|
||||
(Int64Index([0], dtype='int64'), np.array([])),
|
||||
(Int64Index([0], dtype='int64'), np.array([])),
|
||||
(Int64Index([0, 1], dtype='int64'), np.array([])),
|
||||
(Int64Index([0, 1], dtype='int64'), np.array([])),
|
||||
(Int64Index([0, 1, 2], dtype='int64'), np.array([])),
|
||||
(Int64Index([1, 2], dtype='int64'), np.array([])),
|
||||
(Int64Index([2], dtype='int64'), np.array([])),
|
||||
(Int64Index([2], dtype='int64'), np.array([])),
|
||||
(Int64Index([], dtype='int64'), np.array([0])),
|
||||
(Int64Index([], dtype='int64'), np.array([0]))])
|
||||
def test_get_indexer_non_unique_with_ints_and_floats_single_queries(
|
||||
self, query, expected_result):
|
||||
|
||||
index = IntervalIndex.from_tuples(
|
||||
[(0, 2.5), (1, 3), (2, 4)], closed='left')
|
||||
|
||||
result = index.get_indexer_non_unique([query])
|
||||
tm.assert_numpy_array_equal(result, expected_result)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"query",
|
||||
[[1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 2]])
|
||||
@pytest.mark.parametrize("expected_result", [
|
||||
(Int64Index([0, 1, 0, 1, 2], dtype='int64'), np.array([])),
|
||||
(Int64Index([0, 1, 0, 1, 2, 2], dtype='int64'), np.array([])),
|
||||
(Int64Index([0, 1, 0, 1, 2, 2, -1], dtype='int64'), np.array([3])),
|
||||
(Int64Index([0, 1, 0, 1, 2, 2, -1, 0, 1, 2], dtype='int64'),
|
||||
np.array([3]))])
|
||||
def test_get_indexer_non_unique_with_ints_and_floats_multiple_queries(
|
||||
self, query, expected_result):
|
||||
|
||||
index = IntervalIndex.from_tuples(
|
||||
[(0, 2.5), (1, 3), (2, 4)], closed='left')
|
||||
|
||||
result = index.get_indexer_non_unique(query)
|
||||
tm.assert_numpy_array_equal(result, expected_result)
|
||||
|
||||
# TODO we may also want to test get_indexer for the case when
|
||||
# the intervals are duplicated, decreasing, non-monotonic, etc..
|
||||
|
||||
def test_contains(self):
|
||||
|
||||
index = IntervalIndex.from_arrays([0, 1], [1, 2], closed='right')
|
||||
|
||||
# __contains__ requires perfect matches to intervals.
|
||||
assert 0 not in index
|
||||
assert 1 not in index
|
||||
assert 2 not in index
|
||||
|
||||
assert Interval(0, 1, closed='right') in index
|
||||
assert Interval(0, 2, closed='right') not in index
|
||||
assert Interval(0, 0.5, closed='right') not in index
|
||||
assert Interval(3, 5, closed='right') not in index
|
||||
assert Interval(-1, 0, closed='left') not in index
|
||||
assert Interval(0, 1, closed='left') not in index
|
||||
assert Interval(0, 1, closed='both') not in index
|
||||
|
||||
def test_contains_method(self):
|
||||
|
||||
index = IntervalIndex.from_arrays([0, 1], [1, 2], closed='right')
|
||||
|
||||
assert not index.contains(0)
|
||||
assert index.contains(0.1)
|
||||
assert index.contains(0.5)
|
||||
assert index.contains(1)
|
||||
|
||||
assert index.contains(Interval(0, 1), closed='right')
|
||||
assert not index.contains(Interval(0, 1), closed='left')
|
||||
assert not index.contains(Interval(0, 1), closed='both')
|
||||
assert not index.contains(Interval(0, 2), closed='right')
|
||||
|
||||
assert not index.contains(Interval(0, 3), closed='right')
|
||||
assert not index.contains(Interval(1, 3), closed='right')
|
||||
|
||||
assert not index.contains(20)
|
||||
assert not index.contains(-20)
|
||||
-317
@@ -1,317 +0,0 @@
|
||||
from __future__ import division
|
||||
|
||||
import pytest
|
||||
import numpy as np
|
||||
from datetime import timedelta
|
||||
from pandas import (
|
||||
Interval, IntervalIndex, Timestamp, Timedelta, DateOffset,
|
||||
interval_range, date_range, timedelta_range)
|
||||
from pandas.core.dtypes.common import is_integer
|
||||
from pandas.tseries.offsets import Day
|
||||
import pandas.util.testing as tm
|
||||
|
||||
|
||||
@pytest.fixture(scope='class', params=['left', 'right', 'both', 'neither'])
|
||||
def closed(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(scope='class', params=[None, 'foo'])
|
||||
def name(request):
|
||||
return request.param
|
||||
|
||||
|
||||
class TestIntervalRange(object):
|
||||
|
||||
@pytest.mark.parametrize('freq, periods', [
|
||||
(1, 100), (2.5, 40), (5, 20), (25, 4)])
|
||||
def test_constructor_numeric(self, closed, name, freq, periods):
|
||||
start, end = 0, 100
|
||||
breaks = np.arange(101, step=freq)
|
||||
expected = IntervalIndex.from_breaks(breaks, name=name, closed=closed)
|
||||
|
||||
# defined from start/end/freq
|
||||
result = interval_range(
|
||||
start=start, end=end, freq=freq, name=name, closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# defined from start/periods/freq
|
||||
result = interval_range(
|
||||
start=start, periods=periods, freq=freq, name=name, closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# defined from end/periods/freq
|
||||
result = interval_range(
|
||||
end=end, periods=periods, freq=freq, name=name, closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# GH 20976: linspace behavior defined from start/end/periods
|
||||
result = interval_range(
|
||||
start=start, end=end, periods=periods, name=name, closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
@pytest.mark.parametrize('tz', [None, 'US/Eastern'])
|
||||
@pytest.mark.parametrize('freq, periods', [
|
||||
('D', 364), ('2D', 182), ('22D18H', 16), ('M', 11)])
|
||||
def test_constructor_timestamp(self, closed, name, freq, periods, tz):
|
||||
start, end = Timestamp('20180101', tz=tz), Timestamp('20181231', tz=tz)
|
||||
breaks = date_range(start=start, end=end, freq=freq)
|
||||
expected = IntervalIndex.from_breaks(breaks, name=name, closed=closed)
|
||||
|
||||
# defined from start/end/freq
|
||||
result = interval_range(
|
||||
start=start, end=end, freq=freq, name=name, closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# defined from start/periods/freq
|
||||
result = interval_range(
|
||||
start=start, periods=periods, freq=freq, name=name, closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# defined from end/periods/freq
|
||||
result = interval_range(
|
||||
end=end, periods=periods, freq=freq, name=name, closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# GH 20976: linspace behavior defined from start/end/periods
|
||||
if not breaks.freq.isAnchored() and tz is None:
|
||||
# matches expected only for non-anchored offsets and tz naive
|
||||
# (anchored/DST transitions cause unequal spacing in expected)
|
||||
result = interval_range(start=start, end=end, periods=periods,
|
||||
name=name, closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
@pytest.mark.parametrize('freq, periods', [
|
||||
('D', 100), ('2D12H', 40), ('5D', 20), ('25D', 4)])
|
||||
def test_constructor_timedelta(self, closed, name, freq, periods):
|
||||
start, end = Timedelta('0 days'), Timedelta('100 days')
|
||||
breaks = timedelta_range(start=start, end=end, freq=freq)
|
||||
expected = IntervalIndex.from_breaks(breaks, name=name, closed=closed)
|
||||
|
||||
# defined from start/end/freq
|
||||
result = interval_range(
|
||||
start=start, end=end, freq=freq, name=name, closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# defined from start/periods/freq
|
||||
result = interval_range(
|
||||
start=start, periods=periods, freq=freq, name=name, closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# defined from end/periods/freq
|
||||
result = interval_range(
|
||||
end=end, periods=periods, freq=freq, name=name, closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# GH 20976: linspace behavior defined from start/end/periods
|
||||
result = interval_range(
|
||||
start=start, end=end, periods=periods, name=name, closed=closed)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
@pytest.mark.parametrize('start, end, freq, expected_endpoint', [
|
||||
(0, 10, 3, 9),
|
||||
(0, 10, 1.5, 9),
|
||||
(0.5, 10, 3, 9.5),
|
||||
(Timedelta('0D'), Timedelta('10D'), '2D4H', Timedelta('8D16H')),
|
||||
(Timestamp('2018-01-01'),
|
||||
Timestamp('2018-02-09'),
|
||||
'MS',
|
||||
Timestamp('2018-02-01')),
|
||||
(Timestamp('2018-01-01', tz='US/Eastern'),
|
||||
Timestamp('2018-01-20', tz='US/Eastern'),
|
||||
'5D12H',
|
||||
Timestamp('2018-01-17 12:00:00', tz='US/Eastern'))])
|
||||
def test_early_truncation(self, start, end, freq, expected_endpoint):
|
||||
# index truncates early if freq causes end to be skipped
|
||||
result = interval_range(start=start, end=end, freq=freq)
|
||||
result_endpoint = result.right[-1]
|
||||
assert result_endpoint == expected_endpoint
|
||||
|
||||
@pytest.mark.parametrize('start, end, freq', [
|
||||
(0.5, None, None),
|
||||
(None, 4.5, None),
|
||||
(0.5, None, 1.5),
|
||||
(None, 6.5, 1.5)])
|
||||
def test_no_invalid_float_truncation(self, start, end, freq):
|
||||
# GH 21161
|
||||
if freq is None:
|
||||
breaks = [0.5, 1.5, 2.5, 3.5, 4.5]
|
||||
else:
|
||||
breaks = [0.5, 2.0, 3.5, 5.0, 6.5]
|
||||
expected = IntervalIndex.from_breaks(breaks)
|
||||
|
||||
result = interval_range(start=start, end=end, periods=4, freq=freq)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
@pytest.mark.parametrize('start, mid, end', [
|
||||
(Timestamp('2018-03-10', tz='US/Eastern'),
|
||||
Timestamp('2018-03-10 23:30:00', tz='US/Eastern'),
|
||||
Timestamp('2018-03-12', tz='US/Eastern')),
|
||||
(Timestamp('2018-11-03', tz='US/Eastern'),
|
||||
Timestamp('2018-11-04 00:30:00', tz='US/Eastern'),
|
||||
Timestamp('2018-11-05', tz='US/Eastern'))])
|
||||
def test_linspace_dst_transition(self, start, mid, end):
|
||||
# GH 20976: linspace behavior defined from start/end/periods
|
||||
# accounts for the hour gained/lost during DST transition
|
||||
result = interval_range(start=start, end=end, periods=2)
|
||||
expected = IntervalIndex.from_breaks([start, mid, end])
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
@pytest.mark.parametrize('freq', [2, 2.0])
|
||||
@pytest.mark.parametrize('end', [10, 10.0])
|
||||
@pytest.mark.parametrize('start', [0, 0.0])
|
||||
def test_float_subtype(self, start, end, freq):
|
||||
# Has float subtype if any of start/end/freq are float, even if all
|
||||
# resulting endpoints can safely be upcast to integers
|
||||
|
||||
# defined from start/end/freq
|
||||
index = interval_range(start=start, end=end, freq=freq)
|
||||
result = index.dtype.subtype
|
||||
expected = 'int64' if is_integer(start + end + freq) else 'float64'
|
||||
assert result == expected
|
||||
|
||||
# defined from start/periods/freq
|
||||
index = interval_range(start=start, periods=5, freq=freq)
|
||||
result = index.dtype.subtype
|
||||
expected = 'int64' if is_integer(start + freq) else 'float64'
|
||||
assert result == expected
|
||||
|
||||
# defined from end/periods/freq
|
||||
index = interval_range(end=end, periods=5, freq=freq)
|
||||
result = index.dtype.subtype
|
||||
expected = 'int64' if is_integer(end + freq) else 'float64'
|
||||
assert result == expected
|
||||
|
||||
# GH 20976: linspace behavior defined from start/end/periods
|
||||
index = interval_range(start=start, end=end, periods=5)
|
||||
result = index.dtype.subtype
|
||||
expected = 'int64' if is_integer(start + end) else 'float64'
|
||||
assert result == expected
|
||||
|
||||
def test_constructor_coverage(self):
|
||||
# float value for periods
|
||||
expected = interval_range(start=0, periods=10)
|
||||
result = interval_range(start=0, periods=10.5)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# equivalent timestamp-like start/end
|
||||
start, end = Timestamp('2017-01-01'), Timestamp('2017-01-15')
|
||||
expected = interval_range(start=start, end=end)
|
||||
|
||||
result = interval_range(start=start.to_pydatetime(),
|
||||
end=end.to_pydatetime())
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
result = interval_range(start=start.asm8, end=end.asm8)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# equivalent freq with timestamp
|
||||
equiv_freq = ['D', Day(), Timedelta(days=1), timedelta(days=1),
|
||||
DateOffset(days=1)]
|
||||
for freq in equiv_freq:
|
||||
result = interval_range(start=start, end=end, freq=freq)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# equivalent timedelta-like start/end
|
||||
start, end = Timedelta(days=1), Timedelta(days=10)
|
||||
expected = interval_range(start=start, end=end)
|
||||
|
||||
result = interval_range(start=start.to_pytimedelta(),
|
||||
end=end.to_pytimedelta())
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
result = interval_range(start=start.asm8, end=end.asm8)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# equivalent freq with timedelta
|
||||
equiv_freq = ['D', Day(), Timedelta(days=1), timedelta(days=1)]
|
||||
for freq in equiv_freq:
|
||||
result = interval_range(start=start, end=end, freq=freq)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
def test_errors(self):
|
||||
# not enough params
|
||||
msg = ('Of the four parameters: start, end, periods, and freq, '
|
||||
'exactly three must be specified')
|
||||
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
interval_range(start=0)
|
||||
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
interval_range(end=5)
|
||||
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
interval_range(periods=2)
|
||||
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
interval_range()
|
||||
|
||||
# too many params
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
interval_range(start=0, end=5, periods=6, freq=1.5)
|
||||
|
||||
# mixed units
|
||||
msg = 'start, end, freq need to be type compatible'
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
interval_range(start=0, end=Timestamp('20130101'), freq=2)
|
||||
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
interval_range(start=0, end=Timedelta('1 day'), freq=2)
|
||||
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
interval_range(start=0, end=10, freq='D')
|
||||
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
interval_range(start=Timestamp('20130101'), end=10, freq='D')
|
||||
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
interval_range(start=Timestamp('20130101'),
|
||||
end=Timedelta('1 day'), freq='D')
|
||||
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
interval_range(start=Timestamp('20130101'),
|
||||
end=Timestamp('20130110'), freq=2)
|
||||
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
interval_range(start=Timedelta('1 day'), end=10, freq='D')
|
||||
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
interval_range(start=Timedelta('1 day'),
|
||||
end=Timestamp('20130110'), freq='D')
|
||||
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
interval_range(start=Timedelta('1 day'),
|
||||
end=Timedelta('10 days'), freq=2)
|
||||
|
||||
# invalid periods
|
||||
msg = 'periods must be a number, got foo'
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
interval_range(start=0, periods='foo')
|
||||
|
||||
# invalid start
|
||||
msg = 'start must be numeric or datetime-like, got foo'
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
interval_range(start='foo', periods=10)
|
||||
|
||||
# invalid end
|
||||
msg = r'end must be numeric or datetime-like, got \(0, 1\]'
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
interval_range(end=Interval(0, 1), periods=10)
|
||||
|
||||
# invalid freq for datetime-like
|
||||
msg = 'freq must be numeric or convertible to DateOffset, got foo'
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
interval_range(start=0, end=10, freq='foo')
|
||||
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
interval_range(start=Timestamp('20130101'), periods=10, freq='foo')
|
||||
|
||||
with tm.assert_raises_regex(ValueError, msg):
|
||||
interval_range(end=Timedelta('1 day'), periods=10, freq='foo')
|
||||
|
||||
# mixed tz
|
||||
start = Timestamp('2017-01-01', tz='US/Eastern')
|
||||
end = Timestamp('2017-01-07', tz='US/Pacific')
|
||||
msg = 'Start and end cannot both be tz-aware with different timezones'
|
||||
with tm.assert_raises_regex(TypeError, msg):
|
||||
interval_range(start=start, end=end)
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
from __future__ import division
|
||||
|
||||
import pytest
|
||||
import numpy as np
|
||||
from pandas import compat
|
||||
from pandas._libs.interval import IntervalTree
|
||||
import pandas.util.testing as tm
|
||||
|
||||
|
||||
@pytest.fixture(scope='class', params=['left', 'right', 'both', 'neither'])
|
||||
def closed(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope='class', params=['int32', 'int64', 'float32', 'float64', 'uint64'])
|
||||
def dtype(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def tree(dtype):
|
||||
left = np.arange(5, dtype=dtype)
|
||||
return IntervalTree(left, left + 2)
|
||||
|
||||
|
||||
class TestIntervalTree(object):
|
||||
|
||||
def test_get_loc(self, tree):
|
||||
tm.assert_numpy_array_equal(tree.get_loc(1),
|
||||
np.array([0], dtype='int64'))
|
||||
tm.assert_numpy_array_equal(np.sort(tree.get_loc(2)),
|
||||
np.array([0, 1], dtype='int64'))
|
||||
with pytest.raises(KeyError):
|
||||
tree.get_loc(-1)
|
||||
|
||||
def test_get_indexer(self, tree):
|
||||
tm.assert_numpy_array_equal(
|
||||
tree.get_indexer(np.array([1.0, 5.5, 6.5])),
|
||||
np.array([0, 4, -1], dtype='int64'))
|
||||
with pytest.raises(KeyError):
|
||||
tree.get_indexer(np.array([3.0]))
|
||||
|
||||
def test_get_indexer_non_unique(self, tree):
|
||||
indexer, missing = tree.get_indexer_non_unique(
|
||||
np.array([1.0, 2.0, 6.5]))
|
||||
tm.assert_numpy_array_equal(indexer[:1],
|
||||
np.array([0], dtype='int64'))
|
||||
tm.assert_numpy_array_equal(np.sort(indexer[1:3]),
|
||||
np.array([0, 1], dtype='int64'))
|
||||
tm.assert_numpy_array_equal(np.sort(indexer[3:]),
|
||||
np.array([-1], dtype='int64'))
|
||||
tm.assert_numpy_array_equal(missing, np.array([2], dtype='int64'))
|
||||
|
||||
def test_duplicates(self, dtype):
|
||||
left = np.array([0, 0, 0], dtype=dtype)
|
||||
tree = IntervalTree(left, left + 1)
|
||||
tm.assert_numpy_array_equal(np.sort(tree.get_loc(0.5)),
|
||||
np.array([0, 1, 2], dtype='int64'))
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
tree.get_indexer(np.array([0.5]))
|
||||
|
||||
indexer, missing = tree.get_indexer_non_unique(np.array([0.5]))
|
||||
tm.assert_numpy_array_equal(np.sort(indexer),
|
||||
np.array([0, 1, 2], dtype='int64'))
|
||||
tm.assert_numpy_array_equal(missing, np.array([], dtype='int64'))
|
||||
|
||||
def test_get_loc_closed(self, closed):
|
||||
tree = IntervalTree([0], [1], closed=closed)
|
||||
for p, errors in [(0, tree.open_left),
|
||||
(1, tree.open_right)]:
|
||||
if errors:
|
||||
with pytest.raises(KeyError):
|
||||
tree.get_loc(p)
|
||||
else:
|
||||
tm.assert_numpy_array_equal(tree.get_loc(p),
|
||||
np.array([0], dtype='int64'))
|
||||
|
||||
@pytest.mark.skipif(compat.is_platform_32bit(),
|
||||
reason="int type mismatch on 32bit")
|
||||
@pytest.mark.parametrize('leaf_size', [1, 10, 100, 10000])
|
||||
def test_get_indexer_closed(self, closed, leaf_size):
|
||||
x = np.arange(1000, dtype='float64')
|
||||
found = x.astype('intp')
|
||||
not_found = (-1 * np.ones(1000)).astype('intp')
|
||||
|
||||
tree = IntervalTree(x, x + 0.5, closed=closed, leaf_size=leaf_size)
|
||||
tm.assert_numpy_array_equal(found, tree.get_indexer(x + 0.25))
|
||||
|
||||
expected = found if tree.closed_left else not_found
|
||||
tm.assert_numpy_array_equal(expected, tree.get_indexer(x + 0.0))
|
||||
|
||||
expected = found if tree.closed_right else not_found
|
||||
tm.assert_numpy_array_equal(expected, tree.get_indexer(x + 0.5))
|
||||
Reference in New Issue
Block a user