from __future__ import annotations from datetime import datetime import weakref import numpy as np import pytest from pandas._config import using_pyarrow_string_dtype from pandas._libs.tslibs import Timestamp from pandas.core.dtypes.common import ( is_integer_dtype, is_numeric_dtype, ) from pandas.core.dtypes.dtypes import CategoricalDtype import pandas as pd from pandas import ( CategoricalIndex, DatetimeIndex, DatetimeTZDtype, Index, IntervalIndex, MultiIndex, PeriodIndex, RangeIndex, Series, TimedeltaIndex, isna, period_range, ) import pandas._testing as tm import pandas.core.algorithms as algos from pandas.core.arrays import BaseMaskedArray class TestBase: @pytest.fixture( params=[ RangeIndex(start=0, stop=20, step=2), Index(np.arange(5, dtype=np.float64)), Index(np.arange(5, dtype=np.float32)), Index(np.arange(5, dtype=np.uint64)), Index(range(0, 20, 2), dtype=np.int64), Index(range(0, 20, 2), dtype=np.int32), Index(range(0, 20, 2), dtype=np.int16), Index(range(0, 20, 2), dtype=np.int8), Index(list("abcde")), Index([0, "a", 1, "b", 2, "c"]), period_range("20130101", periods=5, freq="D"), TimedeltaIndex( [ "0 days 01:00:00", "1 days 01:00:00", "2 days 01:00:00", "3 days 01:00:00", "4 days 01:00:00", ], dtype="timedelta64[ns]", freq="D", ), DatetimeIndex( ["2013-01-01", "2013-01-02", "2013-01-03", "2013-01-04", "2013-01-05"], dtype="datetime64[ns]", freq="D", ), IntervalIndex.from_breaks(range(11), closed="right"), ] ) def simple_index(self, request): return request.param def test_pickle_compat_construction(self, simple_index): # need an object to create with if isinstance(simple_index, RangeIndex): pytest.skip("RangeIndex() is a valid constructor") msg = "|".join( [ r"Index\(\.\.\.\) must be called with a collection of some " r"kind, None was passed", r"DatetimeIndex\(\) must be called with a collection of some " r"kind, None was passed", r"TimedeltaIndex\(\) must be called with a collection of some " r"kind, None was passed", r"__new__\(\) missing 1 required positional argument: 'data'", r"__new__\(\) takes at least 2 arguments \(1 given\)", ] ) with pytest.raises(TypeError, match=msg): type(simple_index)() def test_shift(self, simple_index): # GH8083 test the base class for shift if isinstance(simple_index, (DatetimeIndex, TimedeltaIndex, PeriodIndex)): pytest.skip("Tested in test_ops/test_arithmetic") idx = simple_index msg = ( f"This method is only implemented for DatetimeIndex, PeriodIndex and " f"TimedeltaIndex; Got type {type(idx).__name__}" ) with pytest.raises(NotImplementedError, match=msg): idx.shift(1) with pytest.raises(NotImplementedError, match=msg): idx.shift(1, 2) def test_constructor_name_unhashable(self, simple_index): # GH#29069 check that name is hashable # See also same-named test in tests.series.test_constructors idx = simple_index with pytest.raises(TypeError, match="Index.name must be a hashable type"): type(idx)(idx, name=[]) def test_create_index_existing_name(self, simple_index): # GH11193, when an existing index is passed, and a new name is not # specified, the new index should inherit the previous object name expected = simple_index.copy() if not isinstance(expected, MultiIndex): expected.name = "foo" result = Index(expected) tm.assert_index_equal(result, expected) result = Index(expected, name="bar") expected.name = "bar" tm.assert_index_equal(result, expected) else: expected.names = ["foo", "bar"] result = Index(expected) tm.assert_index_equal( result, Index( Index( [ ("foo", "one"), ("foo", "two"), ("bar", "one"), ("baz", "two"), ("qux", "one"), ("qux", "two"), ], dtype="object", ), names=["foo", "bar"], ), ) result = Index(expected, names=["A", "B"]) tm.assert_index_equal( result, Index( Index( [ ("foo", "one"), ("foo", "two"), ("bar", "one"), ("baz", "two"), ("qux", "one"), ("qux", "two"), ], dtype="object", ), names=["A", "B"], ), ) def test_numeric_compat(self, simple_index): idx = simple_index # Check that this doesn't cover MultiIndex case, if/when it does, # we can remove multi.test_compat.test_numeric_compat assert not isinstance(idx, MultiIndex) if type(idx) is Index: pytest.skip("Not applicable for Index") if is_numeric_dtype(simple_index.dtype) or isinstance( simple_index, TimedeltaIndex ): pytest.skip("Tested elsewhere.") typ = type(idx._data).__name__ cls = type(idx).__name__ lmsg = "|".join( [ rf"unsupported operand type\(s\) for \*: '{typ}' and 'int'", "cannot perform (__mul__|__truediv__|__floordiv__) with " f"this index type: ({cls}|{typ})", ] ) with pytest.raises(TypeError, match=lmsg): idx * 1 rmsg = "|".join( [ rf"unsupported operand type\(s\) for \*: 'int' and '{typ}'", "cannot perform (__rmul__|__rtruediv__|__rfloordiv__) with " f"this index type: ({cls}|{typ})", ] ) with pytest.raises(TypeError, match=rmsg): 1 * idx div_err = lmsg.replace("*", "/") with pytest.raises(TypeError, match=div_err): idx / 1 div_err = rmsg.replace("*", "/") with pytest.raises(TypeError, match=div_err): 1 / idx floordiv_err = lmsg.replace("*", "//") with pytest.raises(TypeError, match=floordiv_err): idx // 1 floordiv_err = rmsg.replace("*", "//") with pytest.raises(TypeError, match=floordiv_err): 1 // idx def test_logical_compat(self, simple_index): if simple_index.dtype in (object, "string"): pytest.skip("Tested elsewhere.") idx = simple_index if idx.dtype.kind in "iufcbm": assert idx.all() == idx._values.all() assert idx.all() == idx.to_series().all() assert idx.any() == idx._values.any() assert idx.any() == idx.to_series().any() else: msg = "cannot perform (any|all)" if isinstance(idx, IntervalIndex): msg = ( r"'IntervalArray' with dtype interval\[.*\] does " "not support reduction '(any|all)'" ) with pytest.raises(TypeError, match=msg): idx.all() with pytest.raises(TypeError, match=msg): idx.any() def test_repr_roundtrip(self, simple_index): if isinstance(simple_index, IntervalIndex): pytest.skip(f"Not a valid repr for {type(simple_index).__name__}") idx = simple_index tm.assert_index_equal(eval(repr(idx)), idx) def test_repr_max_seq_item_setting(self, simple_index): # GH10182 if isinstance(simple_index, IntervalIndex): pytest.skip(f"Not a valid repr for {type(simple_index).__name__}") idx = simple_index idx = idx.repeat(50) with pd.option_context("display.max_seq_items", None): repr(idx) assert "..." not in str(idx) @pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning") def test_ensure_copied_data(self, index): # Check the "copy" argument of each Index.__new__ is honoured # GH12309 init_kwargs = {} if isinstance(index, PeriodIndex): # Needs "freq" specification: init_kwargs["freq"] = index.freq elif isinstance(index, (RangeIndex, MultiIndex, CategoricalIndex)): pytest.skip( "RangeIndex cannot be initialized from data, " "MultiIndex and CategoricalIndex are tested separately" ) elif index.dtype == object and index.inferred_type == "boolean": init_kwargs["dtype"] = index.dtype index_type = type(index) result = index_type(index.values, copy=True, **init_kwargs) if isinstance(index.dtype, DatetimeTZDtype): result = result.tz_localize("UTC").tz_convert(index.tz) if isinstance(index, (DatetimeIndex, TimedeltaIndex)): index = index._with_freq(None) tm.assert_index_equal(index, result) if isinstance(index, PeriodIndex): # .values an object array of Period, thus copied depr_msg = "The 'ordinal' keyword in PeriodIndex is deprecated" with tm.assert_produces_warning(FutureWarning, match=depr_msg): result = index_type(ordinal=index.asi8, copy=False, **init_kwargs) tm.assert_numpy_array_equal(index.asi8, result.asi8, check_same="same") elif isinstance(index, IntervalIndex): # checked in test_interval.py pass elif type(index) is Index and not isinstance(index.dtype, np.dtype): result = index_type(index.values, copy=False, **init_kwargs) tm.assert_index_equal(result, index) if isinstance(index._values, BaseMaskedArray): assert np.shares_memory(index._values._data, result._values._data) tm.assert_numpy_array_equal( index._values._data, result._values._data, check_same="same" ) assert np.shares_memory(index._values._mask, result._values._mask) tm.assert_numpy_array_equal( index._values._mask, result._values._mask, check_same="same" ) elif index.dtype == "string[python]": assert np.shares_memory(index._values._ndarray, result._values._ndarray) tm.assert_numpy_array_equal( index._values._ndarray, result._values._ndarray, check_same="same" ) elif index.dtype in ("string[pyarrow]", "string[pyarrow_numpy]"): assert tm.shares_memory(result._values, index._values) else: raise NotImplementedError(index.dtype) else: result = index_type(index.values, copy=False, **init_kwargs) tm.assert_numpy_array_equal(index.values, result.values, check_same="same") def test_memory_usage(self, index): index._engine.clear_mapping() result = index.memory_usage() if index.empty: # we report 0 for no-length assert result == 0 return # non-zero length index.get_loc(index[0]) result2 = index.memory_usage() result3 = index.memory_usage(deep=True) # RangeIndex, IntervalIndex # don't have engines # Index[EA] has engine but it does not have a Hashtable .mapping if not isinstance(index, (RangeIndex, IntervalIndex)) and not ( type(index) is Index and not isinstance(index.dtype, np.dtype) ): assert result2 > result if index.inferred_type == "object": assert result3 > result2 def test_argsort(self, index): if isinstance(index, CategoricalIndex): pytest.skip(f"{type(self).__name__} separately tested") result = index.argsort() expected = np.array(index).argsort() tm.assert_numpy_array_equal(result, expected, check_dtype=False) def test_numpy_argsort(self, index): result = np.argsort(index) expected = index.argsort() tm.assert_numpy_array_equal(result, expected) result = np.argsort(index, kind="mergesort") expected = index.argsort(kind="mergesort") tm.assert_numpy_array_equal(result, expected) # these are the only two types that perform # pandas compatibility input validation - the # rest already perform separate (or no) such # validation via their 'values' attribute as # defined in pandas.core.indexes/base.py - they # cannot be changed at the moment due to # backwards compatibility concerns if isinstance(index, (CategoricalIndex, RangeIndex)): msg = "the 'axis' parameter is not supported" with pytest.raises(ValueError, match=msg): np.argsort(index, axis=1) msg = "the 'order' parameter is not supported" with pytest.raises(ValueError, match=msg): np.argsort(index, order=("a", "b")) def test_repeat(self, simple_index): rep = 2 idx = simple_index.copy() new_index_cls = idx._constructor expected = new_index_cls(idx.values.repeat(rep), name=idx.name) tm.assert_index_equal(idx.repeat(rep), expected) idx = simple_index rep = np.arange(len(idx)) expected = new_index_cls(idx.values.repeat(rep), name=idx.name) tm.assert_index_equal(idx.repeat(rep), expected) def test_numpy_repeat(self, simple_index): rep = 2 idx = simple_index expected = idx.repeat(rep) tm.assert_index_equal(np.repeat(idx, rep), expected) msg = "the 'axis' parameter is not supported" with pytest.raises(ValueError, match=msg): np.repeat(idx, rep, axis=0) def test_where(self, listlike_box, simple_index): if isinstance(simple_index, (IntervalIndex, PeriodIndex)) or is_numeric_dtype( simple_index.dtype ): pytest.skip("Tested elsewhere.") klass = listlike_box idx = simple_index if isinstance(idx, (DatetimeIndex, TimedeltaIndex)): # where does not preserve freq idx = idx._with_freq(None) cond = [True] * len(idx) result = idx.where(klass(cond)) expected = idx tm.assert_index_equal(result, expected) cond = [False] + [True] * len(idx[1:]) expected = Index([idx._na_value] + idx[1:].tolist(), dtype=idx.dtype) result = idx.where(klass(cond)) tm.assert_index_equal(result, expected) def test_insert_base(self, index): trimmed = index[1:4] if not len(index): pytest.skip("Not applicable for empty index") # test 0th element warn = None if index.dtype == object and index.inferred_type == "boolean": # GH#51363 warn = FutureWarning msg = "The behavior of Index.insert with object-dtype is deprecated" with tm.assert_produces_warning(warn, match=msg): result = trimmed.insert(0, index[0]) assert index[0:4].equals(result) @pytest.mark.skipif( using_pyarrow_string_dtype(), reason="completely different behavior, tested elsewher", ) def test_insert_out_of_bounds(self, index): # TypeError/IndexError matches what np.insert raises in these cases if len(index) > 0: err = TypeError else: err = IndexError if len(index) == 0: # 0 vs 0.5 in error message varies with numpy version msg = "index (0|0.5) is out of bounds for axis 0 with size 0" else: msg = "slice indices must be integers or None or have an __index__ method" with pytest.raises(err, match=msg): index.insert(0.5, "foo") msg = "|".join( [ r"index -?\d+ is out of bounds for axis 0 with size \d+", "loc must be an integer between", ] ) with pytest.raises(IndexError, match=msg): index.insert(len(index) + 1, 1) with pytest.raises(IndexError, match=msg): index.insert(-len(index) - 1, 1) def test_delete_base(self, index): if not len(index): pytest.skip("Not applicable for empty index") if isinstance(index, RangeIndex): # tested in class pytest.skip(f"{type(self).__name__} tested elsewhere") expected = index[1:] result = index.delete(0) assert result.equals(expected) assert result.name == expected.name expected = index[:-1] result = index.delete(-1) assert result.equals(expected) assert result.name == expected.name length = len(index) msg = f"index {length} is out of bounds for axis 0 with size {length}" with pytest.raises(IndexError, match=msg): index.delete(length) @pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning") def test_equals(self, index): if isinstance(index, IntervalIndex): pytest.skip(f"{type(index).__name__} tested elsewhere") is_ea_idx = type(index) is Index and not isinstance(index.dtype, np.dtype) assert index.equals(index) assert index.equals(index.copy()) if not is_ea_idx: # doesn't hold for e.g. IntegerDtype assert index.equals(index.astype(object)) assert not index.equals(list(index)) assert not index.equals(np.array(index)) # Cannot pass in non-int64 dtype to RangeIndex if not isinstance(index, RangeIndex) and not is_ea_idx: same_values = Index(index, dtype=object) assert index.equals(same_values) assert same_values.equals(index) if index.nlevels == 1: # do not test MultiIndex assert not index.equals(Series(index)) def test_equals_op(self, simple_index): # GH9947, GH10637 index_a = simple_index n = len(index_a) index_b = index_a[0:-1] index_c = index_a[0:-1].append(index_a[-2:-1]) index_d = index_a[0:1] msg = "Lengths must match|could not be broadcast" with pytest.raises(ValueError, match=msg): index_a == index_b expected1 = np.array([True] * n) expected2 = np.array([True] * (n - 1) + [False]) tm.assert_numpy_array_equal(index_a == index_a, expected1) tm.assert_numpy_array_equal(index_a == index_c, expected2) # test comparisons with numpy arrays array_a = np.array(index_a) array_b = np.array(index_a[0:-1]) array_c = np.array(index_a[0:-1].append(index_a[-2:-1])) array_d = np.array(index_a[0:1]) with pytest.raises(ValueError, match=msg): index_a == array_b tm.assert_numpy_array_equal(index_a == array_a, expected1) tm.assert_numpy_array_equal(index_a == array_c, expected2) # test comparisons with Series series_a = Series(array_a) series_b = Series(array_b) series_c = Series(array_c) series_d = Series(array_d) with pytest.raises(ValueError, match=msg): index_a == series_b tm.assert_numpy_array_equal(index_a == series_a, expected1) tm.assert_numpy_array_equal(index_a == series_c, expected2) # cases where length is 1 for one of them with pytest.raises(ValueError, match="Lengths must match"): index_a == index_d with pytest.raises(ValueError, match="Lengths must match"): index_a == series_d with pytest.raises(ValueError, match="Lengths must match"): index_a == array_d msg = "Can only compare identically-labeled Series objects" with pytest.raises(ValueError, match=msg): series_a == series_d with pytest.raises(ValueError, match="Lengths must match"): series_a == array_d # comparing with a scalar should broadcast; note that we are excluding # MultiIndex because in this case each item in the index is a tuple of # length 2, and therefore is considered an array of length 2 in the # comparison instead of a scalar if not isinstance(index_a, MultiIndex): expected3 = np.array([False] * (len(index_a) - 2) + [True, False]) # assuming the 2nd to last item is unique in the data item = index_a[-2] tm.assert_numpy_array_equal(index_a == item, expected3) tm.assert_series_equal(series_a == item, Series(expected3)) def test_format(self, simple_index): # GH35439 if is_numeric_dtype(simple_index.dtype) or isinstance( simple_index, DatetimeIndex ): pytest.skip("Tested elsewhere.") idx = simple_index expected = [str(x) for x in idx] msg = r"Index\.format is deprecated" with tm.assert_produces_warning(FutureWarning, match=msg): assert idx.format() == expected def test_format_empty(self, simple_index): # GH35712 if isinstance(simple_index, (PeriodIndex, RangeIndex)): pytest.skip("Tested elsewhere") empty_idx = type(simple_index)([]) msg = r"Index\.format is deprecated" with tm.assert_produces_warning(FutureWarning, match=msg): assert empty_idx.format() == [] with tm.assert_produces_warning(FutureWarning, match=msg): assert empty_idx.format(name=True) == [""] def test_fillna(self, index): # GH 11343 if len(index) == 0: pytest.skip("Not relevant for empty index") elif index.dtype == bool: pytest.skip(f"{index.dtype} cannot hold NAs") elif isinstance(index, Index) and is_integer_dtype(index.dtype): pytest.skip(f"Not relevant for Index with {index.dtype}") elif isinstance(index, MultiIndex): idx = index.copy(deep=True) msg = "isna is not defined for MultiIndex" with pytest.raises(NotImplementedError, match=msg): idx.fillna(idx[0]) else: idx = index.copy(deep=True) result = idx.fillna(idx[0]) tm.assert_index_equal(result, idx) assert result is not idx msg = "'value' must be a scalar, passed: " with pytest.raises(TypeError, match=msg): idx.fillna([idx[0]]) idx = index.copy(deep=True) values = idx._values values[1] = np.nan idx = type(index)(values) msg = "does not support 'downcast'" msg2 = r"The 'downcast' keyword in .*Index\.fillna is deprecated" with tm.assert_produces_warning(FutureWarning, match=msg2): with pytest.raises(NotImplementedError, match=msg): # For now at least, we only raise if there are NAs present idx.fillna(idx[0], downcast="infer") expected = np.array([False] * len(idx), dtype=bool) expected[1] = True tm.assert_numpy_array_equal(idx._isnan, expected) assert idx.hasnans is True def test_nulls(self, index): # this is really a smoke test for the methods # as these are adequately tested for function elsewhere if len(index) == 0: tm.assert_numpy_array_equal(index.isna(), np.array([], dtype=bool)) elif isinstance(index, MultiIndex): idx = index.copy() msg = "isna is not defined for MultiIndex" with pytest.raises(NotImplementedError, match=msg): idx.isna() elif not index.hasnans: tm.assert_numpy_array_equal(index.isna(), np.zeros(len(index), dtype=bool)) tm.assert_numpy_array_equal(index.notna(), np.ones(len(index), dtype=bool)) else: result = isna(index) tm.assert_numpy_array_equal(index.isna(), result) tm.assert_numpy_array_equal(index.notna(), ~result) def test_empty(self, simple_index): # GH 15270 idx = simple_index assert not idx.empty assert idx[:0].empty def test_join_self_unique(self, join_type, simple_index): idx = simple_index if idx.is_unique: joined = idx.join(idx, how=join_type) expected = simple_index if join_type == "outer": expected = algos.safe_sort(expected) tm.assert_index_equal(joined, expected) def test_map(self, simple_index): # callable if isinstance(simple_index, (TimedeltaIndex, PeriodIndex)): pytest.skip("Tested elsewhere.") idx = simple_index result = idx.map(lambda x: x) # RangeIndex are equivalent to the similar Index with int64 dtype tm.assert_index_equal(result, idx, exact="equiv") @pytest.mark.parametrize( "mapper", [ lambda values, index: {i: e for e, i in zip(values, index)}, lambda values, index: Series(values, index), ], ) @pytest.mark.filterwarnings(r"ignore:PeriodDtype\[B\] is deprecated:FutureWarning") def test_map_dictlike(self, mapper, simple_index, request): idx = simple_index if isinstance(idx, (DatetimeIndex, TimedeltaIndex, PeriodIndex)): pytest.skip("Tested elsewhere.") identity = mapper(idx.values, idx) result = idx.map(identity) # RangeIndex are equivalent to the similar Index with int64 dtype tm.assert_index_equal(result, idx, exact="equiv") # empty mappable dtype = None if idx.dtype.kind == "f": dtype = idx.dtype expected = Index([np.nan] * len(idx), dtype=dtype) result = idx.map(mapper(expected, idx)) tm.assert_index_equal(result, expected) def test_map_str(self, simple_index): # GH 31202 if isinstance(simple_index, CategoricalIndex): pytest.skip("See test_map.py") idx = simple_index result = idx.map(str) expected = Index([str(x) for x in idx]) tm.assert_index_equal(result, expected) @pytest.mark.parametrize("copy", [True, False]) @pytest.mark.parametrize("name", [None, "foo"]) @pytest.mark.parametrize("ordered", [True, False]) def test_astype_category(self, copy, name, ordered, simple_index): # GH 18630 idx = simple_index if name: idx = idx.rename(name) # standard categories dtype = CategoricalDtype(ordered=ordered) result = idx.astype(dtype, copy=copy) expected = CategoricalIndex(idx, name=name, ordered=ordered) tm.assert_index_equal(result, expected, exact=True) # non-standard categories dtype = CategoricalDtype(idx.unique().tolist()[:-1], ordered) result = idx.astype(dtype, copy=copy) expected = CategoricalIndex(idx, name=name, dtype=dtype) tm.assert_index_equal(result, expected, exact=True) if ordered is False: # dtype='category' defaults to ordered=False, so only test once result = idx.astype("category", copy=copy) expected = CategoricalIndex(idx, name=name) tm.assert_index_equal(result, expected, exact=True) def test_is_unique(self, simple_index): # initialize a unique index index = simple_index.drop_duplicates() assert index.is_unique is True # empty index should be unique index_empty = index[:0] assert index_empty.is_unique is True # test basic dupes index_dup = index.insert(0, index[0]) assert index_dup.is_unique is False # single NA should be unique index_na = index.insert(0, np.nan) assert index_na.is_unique is True # multiple NA should not be unique index_na_dup = index_na.insert(0, np.nan) assert index_na_dup.is_unique is False @pytest.mark.arm_slow def test_engine_reference_cycle(self, simple_index): # GH27585 index = simple_index.copy() ref = weakref.ref(index) index._engine del index assert ref() is None def test_getitem_2d_deprecated(self, simple_index): # GH#30588, GH#31479 if isinstance(simple_index, IntervalIndex): pytest.skip("Tested elsewhere") idx = simple_index msg = "Multi-dimensional indexing|too many|only" with pytest.raises((ValueError, IndexError), match=msg): idx[:, None] if not isinstance(idx, RangeIndex): # GH#44051 RangeIndex already raised pre-2.0 with a different message with pytest.raises((ValueError, IndexError), match=msg): idx[True] with pytest.raises((ValueError, IndexError), match=msg): idx[False] else: msg = "only integers, slices" with pytest.raises(IndexError, match=msg): idx[True] with pytest.raises(IndexError, match=msg): idx[False] def test_copy_shares_cache(self, simple_index): # GH32898, GH36840 idx = simple_index idx.get_loc(idx[0]) # populates the _cache. copy = idx.copy() assert copy._cache is idx._cache def test_shallow_copy_shares_cache(self, simple_index): # GH32669, GH36840 idx = simple_index idx.get_loc(idx[0]) # populates the _cache. shallow_copy = idx._view() assert shallow_copy._cache is idx._cache shallow_copy = idx._shallow_copy(idx._data) assert shallow_copy._cache is not idx._cache assert shallow_copy._cache == {} def test_index_groupby(self, simple_index): idx = simple_index[:5] to_groupby = np.array([1, 2, np.nan, 2, 1]) tm.assert_dict_equal( idx.groupby(to_groupby), {1.0: idx[[0, 4]], 2.0: idx[[1, 3]]} ) to_groupby = DatetimeIndex( [ datetime(2011, 11, 1), datetime(2011, 12, 1), pd.NaT, datetime(2011, 12, 1), datetime(2011, 11, 1), ], tz="UTC", ).values ex_keys = [Timestamp("2011-11-01"), Timestamp("2011-12-01")] expected = {ex_keys[0]: idx[[0, 4]], ex_keys[1]: idx[[1, 3]]} tm.assert_dict_equal(idx.groupby(to_groupby), expected) def test_append_preserves_dtype(self, simple_index): # In particular Index with dtype float32 index = simple_index N = len(index) result = index.append(index) assert result.dtype == index.dtype tm.assert_index_equal(result[:N], index, check_exact=True) tm.assert_index_equal(result[N:], index, check_exact=True) alt = index.take(list(range(N)) * 2) tm.assert_index_equal(result, alt, check_exact=True) def test_inv(self, simple_index, using_infer_string): idx = simple_index if idx.dtype.kind in ["i", "u"]: res = ~idx expected = Index(~idx.values, name=idx.name) tm.assert_index_equal(res, expected) # check that we are matching Series behavior res2 = ~Series(idx) tm.assert_series_equal(res2, Series(expected)) else: if idx.dtype.kind == "f": err = TypeError msg = "ufunc 'invert' not supported for the input types" elif using_infer_string and idx.dtype == "string": import pyarrow as pa err = pa.lib.ArrowNotImplementedError msg = "has no kernel" else: err = TypeError msg = "bad operand" with pytest.raises(err, match=msg): ~idx # check that we get the same behavior with Series with pytest.raises(err, match=msg): ~Series(idx) def test_is_boolean_is_deprecated(self, simple_index): # GH50042 idx = simple_index with tm.assert_produces_warning(FutureWarning): idx.is_boolean() def test_is_floating_is_deprecated(self, simple_index): # GH50042 idx = simple_index with tm.assert_produces_warning(FutureWarning): idx.is_floating() def test_is_integer_is_deprecated(self, simple_index): # GH50042 idx = simple_index with tm.assert_produces_warning(FutureWarning): idx.is_integer() def test_holds_integer_deprecated(self, simple_index): # GH50243 idx = simple_index msg = f"{type(idx).__name__}.holds_integer is deprecated. " with tm.assert_produces_warning(FutureWarning, match=msg): idx.holds_integer() def test_is_numeric_is_deprecated(self, simple_index): # GH50042 idx = simple_index with tm.assert_produces_warning( FutureWarning, match=f"{type(idx).__name__}.is_numeric is deprecated. ", ): idx.is_numeric() def test_is_categorical_is_deprecated(self, simple_index): # GH50042 idx = simple_index with tm.assert_produces_warning( FutureWarning, match=r"Use pandas\.api\.types\.is_categorical_dtype instead", ): idx.is_categorical() def test_is_interval_is_deprecated(self, simple_index): # GH50042 idx = simple_index with tm.assert_produces_warning(FutureWarning): idx.is_interval() def test_is_object_is_deprecated(self, simple_index): # GH50042 idx = simple_index with tm.assert_produces_warning(FutureWarning): idx.is_object() class TestNumericBase: @pytest.fixture( params=[ RangeIndex(start=0, stop=20, step=2), Index(np.arange(5, dtype=np.float64)), Index(np.arange(5, dtype=np.float32)), Index(np.arange(5, dtype=np.uint64)), Index(range(0, 20, 2), dtype=np.int64), Index(range(0, 20, 2), dtype=np.int32), Index(range(0, 20, 2), dtype=np.int16), Index(range(0, 20, 2), dtype=np.int8), ] ) def simple_index(self, request): return request.param def test_constructor_unwraps_index(self, simple_index): if isinstance(simple_index, RangeIndex): pytest.skip("Tested elsewhere.") index_cls = type(simple_index) dtype = simple_index.dtype idx = Index([1, 2], dtype=dtype) result = index_cls(idx) expected = np.array([1, 2], dtype=idx.dtype) tm.assert_numpy_array_equal(result._data, expected) def test_can_hold_identifiers(self, simple_index): idx = simple_index key = idx[0] assert idx._can_hold_identifiers_and_holds_name(key) is False def test_view(self, simple_index): if isinstance(simple_index, RangeIndex): pytest.skip("Tested elsewhere.") index_cls = type(simple_index) dtype = simple_index.dtype idx = index_cls([], dtype=dtype, name="Foo") idx_view = idx.view() assert idx_view.name == "Foo" idx_view = idx.view(dtype) tm.assert_index_equal(idx, index_cls(idx_view, name="Foo"), exact=True) msg = "Passing a type in .*Index.view is deprecated" with tm.assert_produces_warning(FutureWarning, match=msg): idx_view = idx.view(index_cls) tm.assert_index_equal(idx, index_cls(idx_view, name="Foo"), exact=True) def test_format(self, simple_index): # GH35439 if isinstance(simple_index, DatetimeIndex): pytest.skip("Tested elsewhere") idx = simple_index max_width = max(len(str(x)) for x in idx) expected = [str(x).ljust(max_width) for x in idx] msg = r"Index\.format is deprecated" with tm.assert_produces_warning(FutureWarning, match=msg): assert idx.format() == expected def test_insert_non_na(self, simple_index): # GH#43921 inserting an element that we know we can hold should # not change dtype or type (except for RangeIndex) index = simple_index result = index.insert(0, index[0]) expected = Index([index[0]] + list(index), dtype=index.dtype) tm.assert_index_equal(result, expected, exact=True) def test_insert_na(self, nulls_fixture, simple_index): # GH 18295 (test missing) index = simple_index na_val = nulls_fixture if na_val is pd.NaT: expected = Index([index[0], pd.NaT] + list(index[1:]), dtype=object) else: expected = Index([index[0], np.nan] + list(index[1:])) # GH#43921 we preserve float dtype if index.dtype.kind == "f": expected = Index(expected, dtype=index.dtype) result = index.insert(1, na_val) tm.assert_index_equal(result, expected, exact=True) def test_arithmetic_explicit_conversions(self, simple_index): # GH 8608 # add/sub are overridden explicitly for Float/Int Index index_cls = type(simple_index) if index_cls is RangeIndex: idx = RangeIndex(5) else: idx = index_cls(np.arange(5, dtype="int64")) # float conversions arr = np.arange(5, dtype="int64") * 3.2 expected = Index(arr, dtype=np.float64) fidx = idx * 3.2 tm.assert_index_equal(fidx, expected) fidx = 3.2 * idx tm.assert_index_equal(fidx, expected) # interops with numpy arrays expected = Index(arr, dtype=np.float64) a = np.zeros(5, dtype="float64") result = fidx - a tm.assert_index_equal(result, expected) expected = Index(-arr, dtype=np.float64) a = np.zeros(5, dtype="float64") result = a - fidx tm.assert_index_equal(result, expected) @pytest.mark.parametrize("complex_dtype", [np.complex64, np.complex128]) def test_astype_to_complex(self, complex_dtype, simple_index): result = simple_index.astype(complex_dtype) assert type(result) is Index and result.dtype == complex_dtype def test_cast_string(self, simple_index): if isinstance(simple_index, RangeIndex): pytest.skip("casting of strings not relevant for RangeIndex") result = type(simple_index)(["0", "1", "2"], dtype=simple_index.dtype) expected = type(simple_index)([0, 1, 2], dtype=simple_index.dtype) tm.assert_index_equal(result, expected)