|
| 1 | +import numpy as np |
| 2 | +import pytest |
| 3 | + |
| 4 | +from pandas import Float64Index, Int64Index, Series, UInt64Index |
| 5 | +import pandas._testing as tm |
| 6 | + |
| 7 | + |
| 8 | +@pytest.fixture |
| 9 | +def index_large(): |
| 10 | + # large values used in UInt64Index tests where no compat needed with Int64/Float64 |
| 11 | + large = [2 ** 63, 2 ** 63 + 10, 2 ** 63 + 15, 2 ** 63 + 20, 2 ** 63 + 25] |
| 12 | + return UInt64Index(large) |
| 13 | + |
| 14 | + |
| 15 | +class TestGetLoc: |
| 16 | + def test_get_loc_float64(self): |
| 17 | + idx = Float64Index([0.0, 1.0, 2.0]) |
| 18 | + for method in [None, "pad", "backfill", "nearest"]: |
| 19 | + assert idx.get_loc(1, method) == 1 |
| 20 | + if method is not None: |
| 21 | + assert idx.get_loc(1, method, tolerance=0) == 1 |
| 22 | + |
| 23 | + for method, loc in [("pad", 1), ("backfill", 2), ("nearest", 1)]: |
| 24 | + assert idx.get_loc(1.1, method) == loc |
| 25 | + assert idx.get_loc(1.1, method, tolerance=0.9) == loc |
| 26 | + |
| 27 | + with pytest.raises(KeyError, match="^'foo'$"): |
| 28 | + idx.get_loc("foo") |
| 29 | + with pytest.raises(KeyError, match=r"^1\.5$"): |
| 30 | + idx.get_loc(1.5) |
| 31 | + with pytest.raises(KeyError, match=r"^1\.5$"): |
| 32 | + idx.get_loc(1.5, method="pad", tolerance=0.1) |
| 33 | + with pytest.raises(KeyError, match="^True$"): |
| 34 | + idx.get_loc(True) |
| 35 | + with pytest.raises(KeyError, match="^False$"): |
| 36 | + idx.get_loc(False) |
| 37 | + |
| 38 | + with pytest.raises(ValueError, match="must be numeric"): |
| 39 | + idx.get_loc(1.4, method="nearest", tolerance="foo") |
| 40 | + |
| 41 | + with pytest.raises(ValueError, match="must contain numeric elements"): |
| 42 | + idx.get_loc(1.4, method="nearest", tolerance=np.array(["foo"])) |
| 43 | + |
| 44 | + with pytest.raises( |
| 45 | + ValueError, match="tolerance size must match target index size" |
| 46 | + ): |
| 47 | + idx.get_loc(1.4, method="nearest", tolerance=np.array([1, 2])) |
| 48 | + |
| 49 | + def test_get_loc_na(self): |
| 50 | + idx = Float64Index([np.nan, 1, 2]) |
| 51 | + assert idx.get_loc(1) == 1 |
| 52 | + assert idx.get_loc(np.nan) == 0 |
| 53 | + |
| 54 | + idx = Float64Index([np.nan, 1, np.nan]) |
| 55 | + assert idx.get_loc(1) == 1 |
| 56 | + |
| 57 | + # FIXME: dont leave commented-out |
| 58 | + # representable by slice [0:2:2] |
| 59 | + # pytest.raises(KeyError, idx.slice_locs, np.nan) |
| 60 | + sliced = idx.slice_locs(np.nan) |
| 61 | + assert isinstance(sliced, tuple) |
| 62 | + assert sliced == (0, 3) |
| 63 | + |
| 64 | + # not representable by slice |
| 65 | + idx = Float64Index([np.nan, 1, np.nan, np.nan]) |
| 66 | + assert idx.get_loc(1) == 1 |
| 67 | + msg = "'Cannot get left slice bound for non-unique label: nan" |
| 68 | + with pytest.raises(KeyError, match=msg): |
| 69 | + idx.slice_locs(np.nan) |
| 70 | + |
| 71 | + def test_get_loc_missing_nan(self): |
| 72 | + # GH#8569 |
| 73 | + idx = Float64Index([1, 2]) |
| 74 | + assert idx.get_loc(1) == 0 |
| 75 | + with pytest.raises(KeyError, match=r"^3$"): |
| 76 | + idx.get_loc(3) |
| 77 | + with pytest.raises(KeyError, match="^nan$"): |
| 78 | + idx.get_loc(np.nan) |
| 79 | + with pytest.raises(TypeError, match=r"'\[nan\]' is an invalid key"): |
| 80 | + # listlike/non-hashable raises TypeError |
| 81 | + idx.get_loc([np.nan]) |
| 82 | + |
| 83 | + |
| 84 | +class TestGetIndexer: |
| 85 | + def test_get_indexer_float64(self): |
| 86 | + idx = Float64Index([0.0, 1.0, 2.0]) |
| 87 | + tm.assert_numpy_array_equal( |
| 88 | + idx.get_indexer(idx), np.array([0, 1, 2], dtype=np.intp) |
| 89 | + ) |
| 90 | + |
| 91 | + target = [-0.1, 0.5, 1.1] |
| 92 | + tm.assert_numpy_array_equal( |
| 93 | + idx.get_indexer(target, "pad"), np.array([-1, 0, 1], dtype=np.intp) |
| 94 | + ) |
| 95 | + tm.assert_numpy_array_equal( |
| 96 | + idx.get_indexer(target, "backfill"), np.array([0, 1, 2], dtype=np.intp) |
| 97 | + ) |
| 98 | + tm.assert_numpy_array_equal( |
| 99 | + idx.get_indexer(target, "nearest"), np.array([0, 1, 1], dtype=np.intp) |
| 100 | + ) |
| 101 | + |
| 102 | + def test_get_indexer_nan(self): |
| 103 | + # GH#7820 |
| 104 | + result = Float64Index([1, 2, np.nan]).get_indexer([np.nan]) |
| 105 | + expected = np.array([2], dtype=np.intp) |
| 106 | + tm.assert_numpy_array_equal(result, expected) |
| 107 | + |
| 108 | + def test_get_indexer_int64(self): |
| 109 | + index = Int64Index(range(0, 20, 2)) |
| 110 | + target = Int64Index(np.arange(10)) |
| 111 | + indexer = index.get_indexer(target) |
| 112 | + expected = np.array([0, -1, 1, -1, 2, -1, 3, -1, 4, -1], dtype=np.intp) |
| 113 | + tm.assert_numpy_array_equal(indexer, expected) |
| 114 | + |
| 115 | + target = Int64Index(np.arange(10)) |
| 116 | + indexer = index.get_indexer(target, method="pad") |
| 117 | + expected = np.array([0, 0, 1, 1, 2, 2, 3, 3, 4, 4], dtype=np.intp) |
| 118 | + tm.assert_numpy_array_equal(indexer, expected) |
| 119 | + |
| 120 | + target = Int64Index(np.arange(10)) |
| 121 | + indexer = index.get_indexer(target, method="backfill") |
| 122 | + expected = np.array([0, 1, 1, 2, 2, 3, 3, 4, 4, 5], dtype=np.intp) |
| 123 | + tm.assert_numpy_array_equal(indexer, expected) |
| 124 | + |
| 125 | + def test_get_indexer_uint64(self, index_large): |
| 126 | + target = UInt64Index(np.arange(10).astype("uint64") * 5 + 2 ** 63) |
| 127 | + indexer = index_large.get_indexer(target) |
| 128 | + expected = np.array([0, -1, 1, 2, 3, 4, -1, -1, -1, -1], dtype=np.intp) |
| 129 | + tm.assert_numpy_array_equal(indexer, expected) |
| 130 | + |
| 131 | + target = UInt64Index(np.arange(10).astype("uint64") * 5 + 2 ** 63) |
| 132 | + indexer = index_large.get_indexer(target, method="pad") |
| 133 | + expected = np.array([0, 0, 1, 2, 3, 4, 4, 4, 4, 4], dtype=np.intp) |
| 134 | + tm.assert_numpy_array_equal(indexer, expected) |
| 135 | + |
| 136 | + target = UInt64Index(np.arange(10).astype("uint64") * 5 + 2 ** 63) |
| 137 | + indexer = index_large.get_indexer(target, method="backfill") |
| 138 | + expected = np.array([0, 1, 1, 2, 3, 4, -1, -1, -1, -1], dtype=np.intp) |
| 139 | + tm.assert_numpy_array_equal(indexer, expected) |
| 140 | + |
| 141 | + |
| 142 | +class TestWhere: |
| 143 | + @pytest.mark.parametrize( |
| 144 | + "index", |
| 145 | + [ |
| 146 | + Float64Index(np.arange(5, dtype="float64")), |
| 147 | + Int64Index(range(0, 20, 2)), |
| 148 | + UInt64Index(np.arange(5, dtype="uint64")), |
| 149 | + ], |
| 150 | + ) |
| 151 | + @pytest.mark.parametrize("klass", [list, tuple, np.array, Series]) |
| 152 | + def test_where(self, klass, index): |
| 153 | + cond = [True] * len(index) |
| 154 | + expected = index |
| 155 | + result = index.where(klass(cond)) |
| 156 | + |
| 157 | + cond = [False] + [True] * (len(index) - 1) |
| 158 | + expected = Float64Index([index._na_value] + index[1:].tolist()) |
| 159 | + result = index.where(klass(cond)) |
| 160 | + tm.assert_index_equal(result, expected) |
| 161 | + |
| 162 | + |
| 163 | +class TestTake: |
| 164 | + @pytest.mark.parametrize("klass", [Float64Index, Int64Index, UInt64Index]) |
| 165 | + def test_take_preserve_name(self, klass): |
| 166 | + index = klass([1, 2, 3, 4], name="foo") |
| 167 | + taken = index.take([3, 0, 1]) |
| 168 | + assert index.name == taken.name |
| 169 | + |
| 170 | + def test_take_fill_value_float64(self): |
| 171 | + # GH 12631 |
| 172 | + idx = Float64Index([1.0, 2.0, 3.0], name="xxx") |
| 173 | + result = idx.take(np.array([1, 0, -1])) |
| 174 | + expected = Float64Index([2.0, 1.0, 3.0], name="xxx") |
| 175 | + tm.assert_index_equal(result, expected) |
| 176 | + |
| 177 | + # fill_value |
| 178 | + result = idx.take(np.array([1, 0, -1]), fill_value=True) |
| 179 | + expected = Float64Index([2.0, 1.0, np.nan], name="xxx") |
| 180 | + tm.assert_index_equal(result, expected) |
| 181 | + |
| 182 | + # allow_fill=False |
| 183 | + result = idx.take(np.array([1, 0, -1]), allow_fill=False, fill_value=True) |
| 184 | + expected = Float64Index([2.0, 1.0, 3.0], name="xxx") |
| 185 | + tm.assert_index_equal(result, expected) |
| 186 | + |
| 187 | + msg = ( |
| 188 | + "When allow_fill=True and fill_value is not None, " |
| 189 | + "all indices must be >= -1" |
| 190 | + ) |
| 191 | + with pytest.raises(ValueError, match=msg): |
| 192 | + idx.take(np.array([1, 0, -2]), fill_value=True) |
| 193 | + with pytest.raises(ValueError, match=msg): |
| 194 | + idx.take(np.array([1, 0, -5]), fill_value=True) |
| 195 | + |
| 196 | + msg = "index -5 is out of bounds for (axis 0 with )?size 3" |
| 197 | + with pytest.raises(IndexError, match=msg): |
| 198 | + idx.take(np.array([1, -5])) |
| 199 | + |
| 200 | + @pytest.mark.parametrize("klass", [Int64Index, UInt64Index]) |
| 201 | + def test_take_fill_value_ints(self, klass): |
| 202 | + # see gh-12631 |
| 203 | + idx = klass([1, 2, 3], name="xxx") |
| 204 | + result = idx.take(np.array([1, 0, -1])) |
| 205 | + expected = klass([2, 1, 3], name="xxx") |
| 206 | + tm.assert_index_equal(result, expected) |
| 207 | + |
| 208 | + name = klass.__name__ |
| 209 | + msg = f"Unable to fill values because {name} cannot contain NA" |
| 210 | + |
| 211 | + # fill_value=True |
| 212 | + with pytest.raises(ValueError, match=msg): |
| 213 | + idx.take(np.array([1, 0, -1]), fill_value=True) |
| 214 | + |
| 215 | + # allow_fill=False |
| 216 | + result = idx.take(np.array([1, 0, -1]), allow_fill=False, fill_value=True) |
| 217 | + expected = klass([2, 1, 3], name="xxx") |
| 218 | + tm.assert_index_equal(result, expected) |
| 219 | + |
| 220 | + with pytest.raises(ValueError, match=msg): |
| 221 | + idx.take(np.array([1, 0, -2]), fill_value=True) |
| 222 | + with pytest.raises(ValueError, match=msg): |
| 223 | + idx.take(np.array([1, 0, -5]), fill_value=True) |
| 224 | + |
| 225 | + msg = "index -5 is out of bounds for (axis 0 with )?size 3" |
| 226 | + with pytest.raises(IndexError, match=msg): |
| 227 | + idx.take(np.array([1, -5])) |
| 228 | + |
| 229 | + |
| 230 | +class TestContains: |
| 231 | + def test_contains_float64_nans(self): |
| 232 | + index = Float64Index([1.0, 2.0, np.nan]) |
| 233 | + assert np.nan in index |
| 234 | + |
| 235 | + def test_contains_float64_not_nans(self): |
| 236 | + index = Float64Index([1.0, 2.0, np.nan]) |
| 237 | + assert 1.0 in index |
0 commit comments