Add files using upload-large-folder tool
Browse files- py311/lib/python3.11/site-packages/pandas/tests/frame/constructors/__init__.py +0 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/constructors/test_from_dict.py +223 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/constructors/test_from_records.py +503 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_add_prefix_suffix.py +49 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_align.py +484 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_at_time.py +132 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_between_time.py +227 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_combine.py +47 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_combine_first.py +556 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_droplevel.py +36 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_fillna.py +916 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_filter.py +153 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_iterrows.py +16 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_join.py +576 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_reindex.py +1327 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_rename.py +415 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_reorder_levels.py +74 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_shift.py +764 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_swaplevel.py +36 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_to_dict_of_blocks.py +79 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_tz_convert.py +131 -0
- py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_values.py +280 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/interval/test_arithmetic.py +192 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/period/__init__.py +0 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/period/test_arithmetic.py +486 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/period/test_asfreq.py +828 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/period/test_period.py +1158 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/__init__.py +0 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/methods/__init__.py +0 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/methods/test_as_unit.py +80 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/methods/test_round.py +187 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/test_arithmetic.py +1183 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/test_constructors.py +698 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/test_formats.py +109 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/test_timedelta.py +666 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/__init__.py +0 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/__init__.py +0 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_as_unit.py +86 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_normalize.py +22 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_replace.py +193 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_round.py +383 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_timestamp_method.py +31 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_to_julian_date.py +28 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_to_pydatetime.py +81 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_tz_convert.py +51 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_tz_localize.py +351 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/test_arithmetic.py +334 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/test_comparisons.py +313 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/test_constructors.py +1077 -0
- py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/test_timestamp.py +928 -0
py311/lib/python3.11/site-packages/pandas/tests/frame/constructors/__init__.py
ADDED
|
File without changes
|
py311/lib/python3.11/site-packages/pandas/tests/frame/constructors/test_from_dict.py
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections import OrderedDict
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pytest
|
| 5 |
+
|
| 6 |
+
from pandas import (
|
| 7 |
+
DataFrame,
|
| 8 |
+
Index,
|
| 9 |
+
MultiIndex,
|
| 10 |
+
RangeIndex,
|
| 11 |
+
Series,
|
| 12 |
+
)
|
| 13 |
+
import pandas._testing as tm
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class TestFromDict:
|
| 17 |
+
# Note: these tests are specific to the from_dict method, not for
|
| 18 |
+
# passing dictionaries to DataFrame.__init__
|
| 19 |
+
|
| 20 |
+
def test_constructor_list_of_odicts(self):
|
| 21 |
+
data = [
|
| 22 |
+
OrderedDict([["a", 1.5], ["b", 3], ["c", 4], ["d", 6]]),
|
| 23 |
+
OrderedDict([["a", 1.5], ["b", 3], ["d", 6]]),
|
| 24 |
+
OrderedDict([["a", 1.5], ["d", 6]]),
|
| 25 |
+
OrderedDict(),
|
| 26 |
+
OrderedDict([["a", 1.5], ["b", 3], ["c", 4]]),
|
| 27 |
+
OrderedDict([["b", 3], ["c", 4], ["d", 6]]),
|
| 28 |
+
]
|
| 29 |
+
|
| 30 |
+
result = DataFrame(data)
|
| 31 |
+
expected = DataFrame.from_dict(
|
| 32 |
+
dict(zip(range(len(data)), data)), orient="index"
|
| 33 |
+
)
|
| 34 |
+
tm.assert_frame_equal(result, expected.reindex(result.index))
|
| 35 |
+
|
| 36 |
+
def test_constructor_single_row(self):
|
| 37 |
+
data = [OrderedDict([["a", 1.5], ["b", 3], ["c", 4], ["d", 6]])]
|
| 38 |
+
|
| 39 |
+
result = DataFrame(data)
|
| 40 |
+
expected = DataFrame.from_dict(dict(zip([0], data)), orient="index").reindex(
|
| 41 |
+
result.index
|
| 42 |
+
)
|
| 43 |
+
tm.assert_frame_equal(result, expected)
|
| 44 |
+
|
| 45 |
+
def test_constructor_list_of_series(self):
|
| 46 |
+
data = [
|
| 47 |
+
OrderedDict([["a", 1.5], ["b", 3.0], ["c", 4.0]]),
|
| 48 |
+
OrderedDict([["a", 1.5], ["b", 3.0], ["c", 6.0]]),
|
| 49 |
+
]
|
| 50 |
+
sdict = OrderedDict(zip(["x", "y"], data))
|
| 51 |
+
idx = Index(["a", "b", "c"])
|
| 52 |
+
|
| 53 |
+
# all named
|
| 54 |
+
data2 = [
|
| 55 |
+
Series([1.5, 3, 4], idx, dtype="O", name="x"),
|
| 56 |
+
Series([1.5, 3, 6], idx, name="y"),
|
| 57 |
+
]
|
| 58 |
+
result = DataFrame(data2)
|
| 59 |
+
expected = DataFrame.from_dict(sdict, orient="index")
|
| 60 |
+
tm.assert_frame_equal(result, expected)
|
| 61 |
+
|
| 62 |
+
# some unnamed
|
| 63 |
+
data2 = [
|
| 64 |
+
Series([1.5, 3, 4], idx, dtype="O", name="x"),
|
| 65 |
+
Series([1.5, 3, 6], idx),
|
| 66 |
+
]
|
| 67 |
+
result = DataFrame(data2)
|
| 68 |
+
|
| 69 |
+
sdict = OrderedDict(zip(["x", "Unnamed 0"], data))
|
| 70 |
+
expected = DataFrame.from_dict(sdict, orient="index")
|
| 71 |
+
tm.assert_frame_equal(result, expected)
|
| 72 |
+
|
| 73 |
+
# none named
|
| 74 |
+
data = [
|
| 75 |
+
OrderedDict([["a", 1.5], ["b", 3], ["c", 4], ["d", 6]]),
|
| 76 |
+
OrderedDict([["a", 1.5], ["b", 3], ["d", 6]]),
|
| 77 |
+
OrderedDict([["a", 1.5], ["d", 6]]),
|
| 78 |
+
OrderedDict(),
|
| 79 |
+
OrderedDict([["a", 1.5], ["b", 3], ["c", 4]]),
|
| 80 |
+
OrderedDict([["b", 3], ["c", 4], ["d", 6]]),
|
| 81 |
+
]
|
| 82 |
+
data = [Series(d) for d in data]
|
| 83 |
+
|
| 84 |
+
result = DataFrame(data)
|
| 85 |
+
sdict = OrderedDict(zip(range(len(data)), data))
|
| 86 |
+
expected = DataFrame.from_dict(sdict, orient="index")
|
| 87 |
+
tm.assert_frame_equal(result, expected.reindex(result.index))
|
| 88 |
+
|
| 89 |
+
result2 = DataFrame(data, index=np.arange(6, dtype=np.int64))
|
| 90 |
+
tm.assert_frame_equal(result, result2)
|
| 91 |
+
|
| 92 |
+
result = DataFrame([Series(dtype=object)])
|
| 93 |
+
expected = DataFrame(index=[0])
|
| 94 |
+
tm.assert_frame_equal(result, expected)
|
| 95 |
+
|
| 96 |
+
data = [
|
| 97 |
+
OrderedDict([["a", 1.5], ["b", 3.0], ["c", 4.0]]),
|
| 98 |
+
OrderedDict([["a", 1.5], ["b", 3.0], ["c", 6.0]]),
|
| 99 |
+
]
|
| 100 |
+
sdict = OrderedDict(zip(range(len(data)), data))
|
| 101 |
+
|
| 102 |
+
idx = Index(["a", "b", "c"])
|
| 103 |
+
data2 = [Series([1.5, 3, 4], idx, dtype="O"), Series([1.5, 3, 6], idx)]
|
| 104 |
+
result = DataFrame(data2)
|
| 105 |
+
expected = DataFrame.from_dict(sdict, orient="index")
|
| 106 |
+
tm.assert_frame_equal(result, expected)
|
| 107 |
+
|
| 108 |
+
def test_constructor_orient(self, float_string_frame):
|
| 109 |
+
data_dict = float_string_frame.T._series
|
| 110 |
+
recons = DataFrame.from_dict(data_dict, orient="index")
|
| 111 |
+
expected = float_string_frame.reindex(index=recons.index)
|
| 112 |
+
tm.assert_frame_equal(recons, expected)
|
| 113 |
+
|
| 114 |
+
# dict of sequence
|
| 115 |
+
a = {"hi": [32, 3, 3], "there": [3, 5, 3]}
|
| 116 |
+
rs = DataFrame.from_dict(a, orient="index")
|
| 117 |
+
xp = DataFrame.from_dict(a).T.reindex(list(a.keys()))
|
| 118 |
+
tm.assert_frame_equal(rs, xp)
|
| 119 |
+
|
| 120 |
+
def test_constructor_from_ordered_dict(self):
|
| 121 |
+
# GH#8425
|
| 122 |
+
a = OrderedDict(
|
| 123 |
+
[
|
| 124 |
+
("one", OrderedDict([("col_a", "foo1"), ("col_b", "bar1")])),
|
| 125 |
+
("two", OrderedDict([("col_a", "foo2"), ("col_b", "bar2")])),
|
| 126 |
+
("three", OrderedDict([("col_a", "foo3"), ("col_b", "bar3")])),
|
| 127 |
+
]
|
| 128 |
+
)
|
| 129 |
+
expected = DataFrame.from_dict(a, orient="columns").T
|
| 130 |
+
result = DataFrame.from_dict(a, orient="index")
|
| 131 |
+
tm.assert_frame_equal(result, expected)
|
| 132 |
+
|
| 133 |
+
def test_from_dict_columns_parameter(self):
|
| 134 |
+
# GH#18529
|
| 135 |
+
# Test new columns parameter for from_dict that was added to make
|
| 136 |
+
# from_items(..., orient='index', columns=[...]) easier to replicate
|
| 137 |
+
result = DataFrame.from_dict(
|
| 138 |
+
OrderedDict([("A", [1, 2]), ("B", [4, 5])]),
|
| 139 |
+
orient="index",
|
| 140 |
+
columns=["one", "two"],
|
| 141 |
+
)
|
| 142 |
+
expected = DataFrame([[1, 2], [4, 5]], index=["A", "B"], columns=["one", "two"])
|
| 143 |
+
tm.assert_frame_equal(result, expected)
|
| 144 |
+
|
| 145 |
+
msg = "cannot use columns parameter with orient='columns'"
|
| 146 |
+
with pytest.raises(ValueError, match=msg):
|
| 147 |
+
DataFrame.from_dict(
|
| 148 |
+
{"A": [1, 2], "B": [4, 5]},
|
| 149 |
+
orient="columns",
|
| 150 |
+
columns=["one", "two"],
|
| 151 |
+
)
|
| 152 |
+
with pytest.raises(ValueError, match=msg):
|
| 153 |
+
DataFrame.from_dict({"A": [1, 2], "B": [4, 5]}, columns=["one", "two"])
|
| 154 |
+
|
| 155 |
+
@pytest.mark.parametrize(
|
| 156 |
+
"data_dict, orient, expected",
|
| 157 |
+
[
|
| 158 |
+
({}, "index", RangeIndex(0)),
|
| 159 |
+
(
|
| 160 |
+
[{("a",): 1}, {("a",): 2}],
|
| 161 |
+
"columns",
|
| 162 |
+
Index([("a",)], tupleize_cols=False),
|
| 163 |
+
),
|
| 164 |
+
(
|
| 165 |
+
[OrderedDict([(("a",), 1), (("b",), 2)])],
|
| 166 |
+
"columns",
|
| 167 |
+
Index([("a",), ("b",)], tupleize_cols=False),
|
| 168 |
+
),
|
| 169 |
+
([{("a", "b"): 1}], "columns", Index([("a", "b")], tupleize_cols=False)),
|
| 170 |
+
],
|
| 171 |
+
)
|
| 172 |
+
def test_constructor_from_dict_tuples(self, data_dict, orient, expected):
|
| 173 |
+
# GH#16769
|
| 174 |
+
df = DataFrame.from_dict(data_dict, orient)
|
| 175 |
+
result = df.columns
|
| 176 |
+
tm.assert_index_equal(result, expected)
|
| 177 |
+
|
| 178 |
+
def test_frame_dict_constructor_empty_series(self):
|
| 179 |
+
s1 = Series(
|
| 180 |
+
[1, 2, 3, 4], index=MultiIndex.from_tuples([(1, 2), (1, 3), (2, 2), (2, 4)])
|
| 181 |
+
)
|
| 182 |
+
s2 = Series(
|
| 183 |
+
[1, 2, 3, 4], index=MultiIndex.from_tuples([(1, 2), (1, 3), (3, 2), (3, 4)])
|
| 184 |
+
)
|
| 185 |
+
s3 = Series(dtype=object)
|
| 186 |
+
|
| 187 |
+
# it works!
|
| 188 |
+
DataFrame({"foo": s1, "bar": s2, "baz": s3})
|
| 189 |
+
DataFrame.from_dict({"foo": s1, "baz": s3, "bar": s2})
|
| 190 |
+
|
| 191 |
+
def test_from_dict_scalars_requires_index(self):
|
| 192 |
+
msg = "If using all scalar values, you must pass an index"
|
| 193 |
+
with pytest.raises(ValueError, match=msg):
|
| 194 |
+
DataFrame.from_dict(OrderedDict([("b", 8), ("a", 5), ("a", 6)]))
|
| 195 |
+
|
| 196 |
+
def test_from_dict_orient_invalid(self):
|
| 197 |
+
msg = (
|
| 198 |
+
"Expected 'index', 'columns' or 'tight' for orient parameter. "
|
| 199 |
+
"Got 'abc' instead"
|
| 200 |
+
)
|
| 201 |
+
with pytest.raises(ValueError, match=msg):
|
| 202 |
+
DataFrame.from_dict({"foo": 1, "baz": 3, "bar": 2}, orient="abc")
|
| 203 |
+
|
| 204 |
+
def test_from_dict_order_with_single_column(self):
|
| 205 |
+
data = {
|
| 206 |
+
"alpha": {
|
| 207 |
+
"value2": 123,
|
| 208 |
+
"value1": 532,
|
| 209 |
+
"animal": 222,
|
| 210 |
+
"plant": False,
|
| 211 |
+
"name": "test",
|
| 212 |
+
}
|
| 213 |
+
}
|
| 214 |
+
result = DataFrame.from_dict(
|
| 215 |
+
data,
|
| 216 |
+
orient="columns",
|
| 217 |
+
)
|
| 218 |
+
expected = DataFrame(
|
| 219 |
+
[[123], [532], [222], [False], ["test"]],
|
| 220 |
+
index=["value2", "value1", "animal", "plant", "name"],
|
| 221 |
+
columns=["alpha"],
|
| 222 |
+
)
|
| 223 |
+
tm.assert_frame_equal(result, expected)
|
py311/lib/python3.11/site-packages/pandas/tests/frame/constructors/test_from_records.py
ADDED
|
@@ -0,0 +1,503 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections.abc import Iterator
|
| 2 |
+
from datetime import datetime
|
| 3 |
+
from decimal import Decimal
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
import pytest
|
| 7 |
+
import pytz
|
| 8 |
+
|
| 9 |
+
from pandas._config import using_string_dtype
|
| 10 |
+
|
| 11 |
+
from pandas.compat import is_platform_little_endian
|
| 12 |
+
|
| 13 |
+
from pandas import (
|
| 14 |
+
CategoricalIndex,
|
| 15 |
+
DataFrame,
|
| 16 |
+
Index,
|
| 17 |
+
Interval,
|
| 18 |
+
RangeIndex,
|
| 19 |
+
Series,
|
| 20 |
+
date_range,
|
| 21 |
+
)
|
| 22 |
+
import pandas._testing as tm
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class TestFromRecords:
|
| 26 |
+
def test_from_records_dt64tz_frame(self):
|
| 27 |
+
# GH#51162 don't lose tz when calling from_records with DataFrame input
|
| 28 |
+
dti = date_range("2016-01-01", periods=10, tz="US/Pacific")
|
| 29 |
+
df = DataFrame({i: dti for i in range(4)})
|
| 30 |
+
with tm.assert_produces_warning(FutureWarning):
|
| 31 |
+
res = DataFrame.from_records(df)
|
| 32 |
+
tm.assert_frame_equal(res, df)
|
| 33 |
+
|
| 34 |
+
def test_from_records_with_datetimes(self):
|
| 35 |
+
# this may fail on certain platforms because of a numpy issue
|
| 36 |
+
# related GH#6140
|
| 37 |
+
if not is_platform_little_endian():
|
| 38 |
+
pytest.skip("known failure of test on non-little endian")
|
| 39 |
+
|
| 40 |
+
# construction with a null in a recarray
|
| 41 |
+
# GH#6140
|
| 42 |
+
expected = DataFrame({"EXPIRY": [datetime(2005, 3, 1, 0, 0), None]})
|
| 43 |
+
|
| 44 |
+
arrdata = [np.array([datetime(2005, 3, 1, 0, 0), None])]
|
| 45 |
+
dtypes = [("EXPIRY", "<M8[ns]")]
|
| 46 |
+
|
| 47 |
+
recarray = np.rec.fromarrays(arrdata, dtype=dtypes)
|
| 48 |
+
|
| 49 |
+
result = DataFrame.from_records(recarray)
|
| 50 |
+
tm.assert_frame_equal(result, expected)
|
| 51 |
+
|
| 52 |
+
# coercion should work too
|
| 53 |
+
arrdata = [np.array([datetime(2005, 3, 1, 0, 0), None])]
|
| 54 |
+
dtypes = [("EXPIRY", "<M8[m]")]
|
| 55 |
+
recarray = np.rec.fromarrays(arrdata, dtype=dtypes)
|
| 56 |
+
result = DataFrame.from_records(recarray)
|
| 57 |
+
# we get the closest supported unit, "s"
|
| 58 |
+
expected["EXPIRY"] = expected["EXPIRY"].astype("M8[s]")
|
| 59 |
+
tm.assert_frame_equal(result, expected)
|
| 60 |
+
|
| 61 |
+
@pytest.mark.xfail(using_string_dtype(), reason="dtype checking logic doesn't work")
|
| 62 |
+
def test_from_records_sequencelike(self):
|
| 63 |
+
df = DataFrame(
|
| 64 |
+
{
|
| 65 |
+
"A": np.array(
|
| 66 |
+
np.random.default_rng(2).standard_normal(6), dtype=np.float64
|
| 67 |
+
),
|
| 68 |
+
"A1": np.array(
|
| 69 |
+
np.random.default_rng(2).standard_normal(6), dtype=np.float64
|
| 70 |
+
),
|
| 71 |
+
"B": np.array(np.arange(6), dtype=np.int64),
|
| 72 |
+
"C": ["foo"] * 6,
|
| 73 |
+
"D": np.array([True, False] * 3, dtype=bool),
|
| 74 |
+
"E": np.array(
|
| 75 |
+
np.random.default_rng(2).standard_normal(6), dtype=np.float32
|
| 76 |
+
),
|
| 77 |
+
"E1": np.array(
|
| 78 |
+
np.random.default_rng(2).standard_normal(6), dtype=np.float32
|
| 79 |
+
),
|
| 80 |
+
"F": np.array(np.arange(6), dtype=np.int32),
|
| 81 |
+
}
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
# this is actually tricky to create the recordlike arrays and
|
| 85 |
+
# have the dtypes be intact
|
| 86 |
+
blocks = df._to_dict_of_blocks()
|
| 87 |
+
tuples = []
|
| 88 |
+
columns = []
|
| 89 |
+
dtypes = []
|
| 90 |
+
for dtype, b in blocks.items():
|
| 91 |
+
columns.extend(b.columns)
|
| 92 |
+
dtypes.extend([(c, np.dtype(dtype).descr[0][1]) for c in b.columns])
|
| 93 |
+
for i in range(len(df.index)):
|
| 94 |
+
tup = []
|
| 95 |
+
for _, b in blocks.items():
|
| 96 |
+
tup.extend(b.iloc[i].values)
|
| 97 |
+
tuples.append(tuple(tup))
|
| 98 |
+
|
| 99 |
+
recarray = np.array(tuples, dtype=dtypes).view(np.rec.recarray)
|
| 100 |
+
recarray2 = df.to_records()
|
| 101 |
+
lists = [list(x) for x in tuples]
|
| 102 |
+
|
| 103 |
+
# tuples (lose the dtype info)
|
| 104 |
+
result = DataFrame.from_records(tuples, columns=columns).reindex(
|
| 105 |
+
columns=df.columns
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
# created recarray and with to_records recarray (have dtype info)
|
| 109 |
+
result2 = DataFrame.from_records(recarray, columns=columns).reindex(
|
| 110 |
+
columns=df.columns
|
| 111 |
+
)
|
| 112 |
+
result3 = DataFrame.from_records(recarray2, columns=columns).reindex(
|
| 113 |
+
columns=df.columns
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
# list of tuples (no dtype info)
|
| 117 |
+
result4 = DataFrame.from_records(lists, columns=columns).reindex(
|
| 118 |
+
columns=df.columns
|
| 119 |
+
)
|
| 120 |
+
|
| 121 |
+
tm.assert_frame_equal(result, df, check_dtype=False)
|
| 122 |
+
tm.assert_frame_equal(result2, df)
|
| 123 |
+
tm.assert_frame_equal(result3, df)
|
| 124 |
+
tm.assert_frame_equal(result4, df, check_dtype=False)
|
| 125 |
+
|
| 126 |
+
# tuples is in the order of the columns
|
| 127 |
+
result = DataFrame.from_records(tuples)
|
| 128 |
+
tm.assert_index_equal(result.columns, RangeIndex(8))
|
| 129 |
+
|
| 130 |
+
# test exclude parameter & we are casting the results here (as we don't
|
| 131 |
+
# have dtype info to recover)
|
| 132 |
+
columns_to_test = [columns.index("C"), columns.index("E1")]
|
| 133 |
+
|
| 134 |
+
exclude = list(set(range(8)) - set(columns_to_test))
|
| 135 |
+
result = DataFrame.from_records(tuples, exclude=exclude)
|
| 136 |
+
result.columns = [columns[i] for i in sorted(columns_to_test)]
|
| 137 |
+
tm.assert_series_equal(result["C"], df["C"])
|
| 138 |
+
tm.assert_series_equal(result["E1"], df["E1"])
|
| 139 |
+
|
| 140 |
+
def test_from_records_sequencelike_empty(self):
|
| 141 |
+
# empty case
|
| 142 |
+
result = DataFrame.from_records([], columns=["foo", "bar", "baz"])
|
| 143 |
+
assert len(result) == 0
|
| 144 |
+
tm.assert_index_equal(result.columns, Index(["foo", "bar", "baz"]))
|
| 145 |
+
|
| 146 |
+
result = DataFrame.from_records([])
|
| 147 |
+
assert len(result) == 0
|
| 148 |
+
assert len(result.columns) == 0
|
| 149 |
+
|
| 150 |
+
def test_from_records_dictlike(self):
|
| 151 |
+
# test the dict methods
|
| 152 |
+
df = DataFrame(
|
| 153 |
+
{
|
| 154 |
+
"A": np.array(
|
| 155 |
+
np.random.default_rng(2).standard_normal(6), dtype=np.float64
|
| 156 |
+
),
|
| 157 |
+
"A1": np.array(
|
| 158 |
+
np.random.default_rng(2).standard_normal(6), dtype=np.float64
|
| 159 |
+
),
|
| 160 |
+
"B": np.array(np.arange(6), dtype=np.int64),
|
| 161 |
+
"C": ["foo"] * 6,
|
| 162 |
+
"D": np.array([True, False] * 3, dtype=bool),
|
| 163 |
+
"E": np.array(
|
| 164 |
+
np.random.default_rng(2).standard_normal(6), dtype=np.float32
|
| 165 |
+
),
|
| 166 |
+
"E1": np.array(
|
| 167 |
+
np.random.default_rng(2).standard_normal(6), dtype=np.float32
|
| 168 |
+
),
|
| 169 |
+
"F": np.array(np.arange(6), dtype=np.int32),
|
| 170 |
+
}
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
# columns is in a different order here than the actual items iterated
|
| 174 |
+
# from the dict
|
| 175 |
+
blocks = df._to_dict_of_blocks()
|
| 176 |
+
columns = []
|
| 177 |
+
for b in blocks.values():
|
| 178 |
+
columns.extend(b.columns)
|
| 179 |
+
|
| 180 |
+
asdict = dict(df.items())
|
| 181 |
+
asdict2 = {x: y.values for x, y in df.items()}
|
| 182 |
+
|
| 183 |
+
# dict of series & dict of ndarrays (have dtype info)
|
| 184 |
+
results = []
|
| 185 |
+
results.append(DataFrame.from_records(asdict).reindex(columns=df.columns))
|
| 186 |
+
results.append(
|
| 187 |
+
DataFrame.from_records(asdict, columns=columns).reindex(columns=df.columns)
|
| 188 |
+
)
|
| 189 |
+
results.append(
|
| 190 |
+
DataFrame.from_records(asdict2, columns=columns).reindex(columns=df.columns)
|
| 191 |
+
)
|
| 192 |
+
|
| 193 |
+
for r in results:
|
| 194 |
+
tm.assert_frame_equal(r, df)
|
| 195 |
+
|
| 196 |
+
def test_from_records_with_index_data(self):
|
| 197 |
+
df = DataFrame(
|
| 198 |
+
np.random.default_rng(2).standard_normal((10, 3)), columns=["A", "B", "C"]
|
| 199 |
+
)
|
| 200 |
+
|
| 201 |
+
data = np.random.default_rng(2).standard_normal(10)
|
| 202 |
+
with tm.assert_produces_warning(FutureWarning):
|
| 203 |
+
df1 = DataFrame.from_records(df, index=data)
|
| 204 |
+
tm.assert_index_equal(df1.index, Index(data))
|
| 205 |
+
|
| 206 |
+
def test_from_records_bad_index_column(self):
|
| 207 |
+
df = DataFrame(
|
| 208 |
+
np.random.default_rng(2).standard_normal((10, 3)), columns=["A", "B", "C"]
|
| 209 |
+
)
|
| 210 |
+
|
| 211 |
+
# should pass
|
| 212 |
+
with tm.assert_produces_warning(FutureWarning):
|
| 213 |
+
df1 = DataFrame.from_records(df, index=["C"])
|
| 214 |
+
tm.assert_index_equal(df1.index, Index(df.C))
|
| 215 |
+
|
| 216 |
+
with tm.assert_produces_warning(FutureWarning):
|
| 217 |
+
df1 = DataFrame.from_records(df, index="C")
|
| 218 |
+
tm.assert_index_equal(df1.index, Index(df.C))
|
| 219 |
+
|
| 220 |
+
# should fail
|
| 221 |
+
msg = "|".join(
|
| 222 |
+
[
|
| 223 |
+
r"'None of \[2\] are in the columns'",
|
| 224 |
+
]
|
| 225 |
+
)
|
| 226 |
+
with pytest.raises(KeyError, match=msg):
|
| 227 |
+
with tm.assert_produces_warning(FutureWarning):
|
| 228 |
+
DataFrame.from_records(df, index=[2])
|
| 229 |
+
with pytest.raises(KeyError, match=msg):
|
| 230 |
+
with tm.assert_produces_warning(FutureWarning):
|
| 231 |
+
DataFrame.from_records(df, index=2)
|
| 232 |
+
|
| 233 |
+
def test_from_records_non_tuple(self):
|
| 234 |
+
class Record:
|
| 235 |
+
def __init__(self, *args) -> None:
|
| 236 |
+
self.args = args
|
| 237 |
+
|
| 238 |
+
def __getitem__(self, i):
|
| 239 |
+
return self.args[i]
|
| 240 |
+
|
| 241 |
+
def __iter__(self) -> Iterator:
|
| 242 |
+
return iter(self.args)
|
| 243 |
+
|
| 244 |
+
recs = [Record(1, 2, 3), Record(4, 5, 6), Record(7, 8, 9)]
|
| 245 |
+
tups = [tuple(rec) for rec in recs]
|
| 246 |
+
|
| 247 |
+
result = DataFrame.from_records(recs)
|
| 248 |
+
expected = DataFrame.from_records(tups)
|
| 249 |
+
tm.assert_frame_equal(result, expected)
|
| 250 |
+
|
| 251 |
+
def test_from_records_len0_with_columns(self):
|
| 252 |
+
# GH#2633
|
| 253 |
+
result = DataFrame.from_records([], index="foo", columns=["foo", "bar"])
|
| 254 |
+
expected = Index(["bar"])
|
| 255 |
+
|
| 256 |
+
assert len(result) == 0
|
| 257 |
+
assert result.index.name == "foo"
|
| 258 |
+
tm.assert_index_equal(result.columns, expected)
|
| 259 |
+
|
| 260 |
+
def test_from_records_series_list_dict(self):
|
| 261 |
+
# GH#27358
|
| 262 |
+
expected = DataFrame([[{"a": 1, "b": 2}, {"a": 3, "b": 4}]]).T
|
| 263 |
+
data = Series([[{"a": 1, "b": 2}], [{"a": 3, "b": 4}]])
|
| 264 |
+
result = DataFrame.from_records(data)
|
| 265 |
+
tm.assert_frame_equal(result, expected)
|
| 266 |
+
|
| 267 |
+
def test_from_records_series_categorical_index(self):
|
| 268 |
+
# GH#32805
|
| 269 |
+
index = CategoricalIndex(
|
| 270 |
+
[Interval(-20, -10), Interval(-10, 0), Interval(0, 10)]
|
| 271 |
+
)
|
| 272 |
+
series_of_dicts = Series([{"a": 1}, {"a": 2}, {"b": 3}], index=index)
|
| 273 |
+
frame = DataFrame.from_records(series_of_dicts, index=index)
|
| 274 |
+
expected = DataFrame(
|
| 275 |
+
{"a": [1, 2, np.nan], "b": [np.nan, np.nan, 3]}, index=index
|
| 276 |
+
)
|
| 277 |
+
tm.assert_frame_equal(frame, expected)
|
| 278 |
+
|
| 279 |
+
def test_frame_from_records_utc(self):
|
| 280 |
+
rec = {"datum": 1.5, "begin_time": datetime(2006, 4, 27, tzinfo=pytz.utc)}
|
| 281 |
+
|
| 282 |
+
# it works
|
| 283 |
+
DataFrame.from_records([rec], index="begin_time")
|
| 284 |
+
|
| 285 |
+
def test_from_records_to_records(self):
|
| 286 |
+
# from numpy documentation
|
| 287 |
+
arr = np.zeros((2,), dtype=("i4,f4,S10"))
|
| 288 |
+
arr[:] = [(1, 2.0, "Hello"), (2, 3.0, "World")]
|
| 289 |
+
|
| 290 |
+
DataFrame.from_records(arr)
|
| 291 |
+
|
| 292 |
+
index = Index(np.arange(len(arr))[::-1])
|
| 293 |
+
indexed_frame = DataFrame.from_records(arr, index=index)
|
| 294 |
+
tm.assert_index_equal(indexed_frame.index, index)
|
| 295 |
+
|
| 296 |
+
# without names, it should go to last ditch
|
| 297 |
+
arr2 = np.zeros((2, 3))
|
| 298 |
+
tm.assert_frame_equal(DataFrame.from_records(arr2), DataFrame(arr2))
|
| 299 |
+
|
| 300 |
+
# wrong length
|
| 301 |
+
msg = "|".join(
|
| 302 |
+
[
|
| 303 |
+
r"Length of values \(2\) does not match length of index \(1\)",
|
| 304 |
+
]
|
| 305 |
+
)
|
| 306 |
+
with pytest.raises(ValueError, match=msg):
|
| 307 |
+
DataFrame.from_records(arr, index=index[:-1])
|
| 308 |
+
|
| 309 |
+
indexed_frame = DataFrame.from_records(arr, index="f1")
|
| 310 |
+
|
| 311 |
+
# what to do?
|
| 312 |
+
records = indexed_frame.to_records()
|
| 313 |
+
assert len(records.dtype.names) == 3
|
| 314 |
+
|
| 315 |
+
records = indexed_frame.to_records(index=False)
|
| 316 |
+
assert len(records.dtype.names) == 2
|
| 317 |
+
assert "index" not in records.dtype.names
|
| 318 |
+
|
| 319 |
+
def test_from_records_nones(self):
|
| 320 |
+
tuples = [(1, 2, None, 3), (1, 2, None, 3), (None, 2, 5, 3)]
|
| 321 |
+
|
| 322 |
+
df = DataFrame.from_records(tuples, columns=["a", "b", "c", "d"])
|
| 323 |
+
assert np.isnan(df["c"][0])
|
| 324 |
+
|
| 325 |
+
def test_from_records_iterator(self):
|
| 326 |
+
arr = np.array(
|
| 327 |
+
[(1.0, 1.0, 2, 2), (3.0, 3.0, 4, 4), (5.0, 5.0, 6, 6), (7.0, 7.0, 8, 8)],
|
| 328 |
+
dtype=[
|
| 329 |
+
("x", np.float64),
|
| 330 |
+
("u", np.float32),
|
| 331 |
+
("y", np.int64),
|
| 332 |
+
("z", np.int32),
|
| 333 |
+
],
|
| 334 |
+
)
|
| 335 |
+
df = DataFrame.from_records(iter(arr), nrows=2)
|
| 336 |
+
xp = DataFrame(
|
| 337 |
+
{
|
| 338 |
+
"x": np.array([1.0, 3.0], dtype=np.float64),
|
| 339 |
+
"u": np.array([1.0, 3.0], dtype=np.float32),
|
| 340 |
+
"y": np.array([2, 4], dtype=np.int64),
|
| 341 |
+
"z": np.array([2, 4], dtype=np.int32),
|
| 342 |
+
}
|
| 343 |
+
)
|
| 344 |
+
tm.assert_frame_equal(df.reindex_like(xp), xp)
|
| 345 |
+
|
| 346 |
+
# no dtypes specified here, so just compare with the default
|
| 347 |
+
arr = [(1.0, 2), (3.0, 4), (5.0, 6), (7.0, 8)]
|
| 348 |
+
df = DataFrame.from_records(iter(arr), columns=["x", "y"], nrows=2)
|
| 349 |
+
tm.assert_frame_equal(df, xp.reindex(columns=["x", "y"]), check_dtype=False)
|
| 350 |
+
|
| 351 |
+
def test_from_records_tuples_generator(self):
|
| 352 |
+
def tuple_generator(length):
|
| 353 |
+
for i in range(length):
|
| 354 |
+
letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
| 355 |
+
yield (i, letters[i % len(letters)], i / length)
|
| 356 |
+
|
| 357 |
+
columns_names = ["Integer", "String", "Float"]
|
| 358 |
+
columns = [
|
| 359 |
+
[i[j] for i in tuple_generator(10)] for j in range(len(columns_names))
|
| 360 |
+
]
|
| 361 |
+
data = {"Integer": columns[0], "String": columns[1], "Float": columns[2]}
|
| 362 |
+
expected = DataFrame(data, columns=columns_names)
|
| 363 |
+
|
| 364 |
+
generator = tuple_generator(10)
|
| 365 |
+
result = DataFrame.from_records(generator, columns=columns_names)
|
| 366 |
+
tm.assert_frame_equal(result, expected)
|
| 367 |
+
|
| 368 |
+
def test_from_records_lists_generator(self):
|
| 369 |
+
def list_generator(length):
|
| 370 |
+
for i in range(length):
|
| 371 |
+
letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
| 372 |
+
yield [i, letters[i % len(letters)], i / length]
|
| 373 |
+
|
| 374 |
+
columns_names = ["Integer", "String", "Float"]
|
| 375 |
+
columns = [
|
| 376 |
+
[i[j] for i in list_generator(10)] for j in range(len(columns_names))
|
| 377 |
+
]
|
| 378 |
+
data = {"Integer": columns[0], "String": columns[1], "Float": columns[2]}
|
| 379 |
+
expected = DataFrame(data, columns=columns_names)
|
| 380 |
+
|
| 381 |
+
generator = list_generator(10)
|
| 382 |
+
result = DataFrame.from_records(generator, columns=columns_names)
|
| 383 |
+
tm.assert_frame_equal(result, expected)
|
| 384 |
+
|
| 385 |
+
def test_from_records_columns_not_modified(self):
|
| 386 |
+
tuples = [(1, 2, 3), (1, 2, 3), (2, 5, 3)]
|
| 387 |
+
|
| 388 |
+
columns = ["a", "b", "c"]
|
| 389 |
+
original_columns = list(columns)
|
| 390 |
+
|
| 391 |
+
DataFrame.from_records(tuples, columns=columns, index="a")
|
| 392 |
+
|
| 393 |
+
assert columns == original_columns
|
| 394 |
+
|
| 395 |
+
def test_from_records_decimal(self):
|
| 396 |
+
tuples = [(Decimal("1.5"),), (Decimal("2.5"),), (None,)]
|
| 397 |
+
|
| 398 |
+
df = DataFrame.from_records(tuples, columns=["a"])
|
| 399 |
+
assert df["a"].dtype == object
|
| 400 |
+
|
| 401 |
+
df = DataFrame.from_records(tuples, columns=["a"], coerce_float=True)
|
| 402 |
+
assert df["a"].dtype == np.float64
|
| 403 |
+
assert np.isnan(df["a"].values[-1])
|
| 404 |
+
|
| 405 |
+
def test_from_records_duplicates(self):
|
| 406 |
+
result = DataFrame.from_records([(1, 2, 3), (4, 5, 6)], columns=["a", "b", "a"])
|
| 407 |
+
|
| 408 |
+
expected = DataFrame([(1, 2, 3), (4, 5, 6)], columns=["a", "b", "a"])
|
| 409 |
+
|
| 410 |
+
tm.assert_frame_equal(result, expected)
|
| 411 |
+
|
| 412 |
+
def test_from_records_set_index_name(self):
|
| 413 |
+
def create_dict(order_id):
|
| 414 |
+
return {
|
| 415 |
+
"order_id": order_id,
|
| 416 |
+
"quantity": np.random.default_rng(2).integers(1, 10),
|
| 417 |
+
"price": np.random.default_rng(2).integers(1, 10),
|
| 418 |
+
}
|
| 419 |
+
|
| 420 |
+
documents = [create_dict(i) for i in range(10)]
|
| 421 |
+
# demo missing data
|
| 422 |
+
documents.append({"order_id": 10, "quantity": 5})
|
| 423 |
+
|
| 424 |
+
result = DataFrame.from_records(documents, index="order_id")
|
| 425 |
+
assert result.index.name == "order_id"
|
| 426 |
+
|
| 427 |
+
# MultiIndex
|
| 428 |
+
result = DataFrame.from_records(documents, index=["order_id", "quantity"])
|
| 429 |
+
assert result.index.names == ("order_id", "quantity")
|
| 430 |
+
|
| 431 |
+
def test_from_records_misc_brokenness(self):
|
| 432 |
+
# GH#2179
|
| 433 |
+
|
| 434 |
+
data = {1: ["foo"], 2: ["bar"]}
|
| 435 |
+
|
| 436 |
+
result = DataFrame.from_records(data, columns=["a", "b"])
|
| 437 |
+
exp = DataFrame(data, columns=["a", "b"])
|
| 438 |
+
tm.assert_frame_equal(result, exp)
|
| 439 |
+
|
| 440 |
+
# overlap in index/index_names
|
| 441 |
+
|
| 442 |
+
data = {"a": [1, 2, 3], "b": [4, 5, 6]}
|
| 443 |
+
|
| 444 |
+
result = DataFrame.from_records(data, index=["a", "b", "c"])
|
| 445 |
+
exp = DataFrame(data, index=["a", "b", "c"])
|
| 446 |
+
tm.assert_frame_equal(result, exp)
|
| 447 |
+
|
| 448 |
+
def test_from_records_misc_brokenness2(self):
|
| 449 |
+
# GH#2623
|
| 450 |
+
rows = []
|
| 451 |
+
rows.append([datetime(2010, 1, 1), 1])
|
| 452 |
+
rows.append([datetime(2010, 1, 2), "hi"]) # test col upconverts to obj
|
| 453 |
+
result = DataFrame.from_records(rows, columns=["date", "test"])
|
| 454 |
+
expected = DataFrame(
|
| 455 |
+
{"date": [row[0] for row in rows], "test": [row[1] for row in rows]}
|
| 456 |
+
)
|
| 457 |
+
tm.assert_frame_equal(result, expected)
|
| 458 |
+
assert result.dtypes["test"] == np.dtype(object)
|
| 459 |
+
|
| 460 |
+
def test_from_records_misc_brokenness3(self):
|
| 461 |
+
rows = []
|
| 462 |
+
rows.append([datetime(2010, 1, 1), 1])
|
| 463 |
+
rows.append([datetime(2010, 1, 2), 1])
|
| 464 |
+
result = DataFrame.from_records(rows, columns=["date", "test"])
|
| 465 |
+
expected = DataFrame(
|
| 466 |
+
{"date": [row[0] for row in rows], "test": [row[1] for row in rows]}
|
| 467 |
+
)
|
| 468 |
+
tm.assert_frame_equal(result, expected)
|
| 469 |
+
|
| 470 |
+
def test_from_records_empty(self):
|
| 471 |
+
# GH#3562
|
| 472 |
+
result = DataFrame.from_records([], columns=["a", "b", "c"])
|
| 473 |
+
expected = DataFrame(columns=["a", "b", "c"])
|
| 474 |
+
tm.assert_frame_equal(result, expected)
|
| 475 |
+
|
| 476 |
+
result = DataFrame.from_records([], columns=["a", "b", "b"])
|
| 477 |
+
expected = DataFrame(columns=["a", "b", "b"])
|
| 478 |
+
tm.assert_frame_equal(result, expected)
|
| 479 |
+
|
| 480 |
+
def test_from_records_empty_with_nonempty_fields_gh3682(self):
|
| 481 |
+
a = np.array([(1, 2)], dtype=[("id", np.int64), ("value", np.int64)])
|
| 482 |
+
df = DataFrame.from_records(a, index="id")
|
| 483 |
+
|
| 484 |
+
ex_index = Index([1], name="id")
|
| 485 |
+
expected = DataFrame({"value": [2]}, index=ex_index, columns=["value"])
|
| 486 |
+
tm.assert_frame_equal(df, expected)
|
| 487 |
+
|
| 488 |
+
b = a[:0]
|
| 489 |
+
df2 = DataFrame.from_records(b, index="id")
|
| 490 |
+
tm.assert_frame_equal(df2, df.iloc[:0])
|
| 491 |
+
|
| 492 |
+
def test_from_records_empty2(self):
|
| 493 |
+
# GH#42456
|
| 494 |
+
dtype = [("prop", int)]
|
| 495 |
+
shape = (0, len(dtype))
|
| 496 |
+
arr = np.empty(shape, dtype=dtype)
|
| 497 |
+
|
| 498 |
+
result = DataFrame.from_records(arr)
|
| 499 |
+
expected = DataFrame({"prop": np.array([], dtype=int)})
|
| 500 |
+
tm.assert_frame_equal(result, expected)
|
| 501 |
+
|
| 502 |
+
alt = DataFrame(arr)
|
| 503 |
+
tm.assert_frame_equal(alt, expected)
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_add_prefix_suffix.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
|
| 3 |
+
from pandas import Index
|
| 4 |
+
import pandas._testing as tm
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def test_add_prefix_suffix(float_frame):
|
| 8 |
+
with_prefix = float_frame.add_prefix("foo#")
|
| 9 |
+
expected = Index([f"foo#{c}" for c in float_frame.columns])
|
| 10 |
+
tm.assert_index_equal(with_prefix.columns, expected)
|
| 11 |
+
|
| 12 |
+
with_suffix = float_frame.add_suffix("#foo")
|
| 13 |
+
expected = Index([f"{c}#foo" for c in float_frame.columns])
|
| 14 |
+
tm.assert_index_equal(with_suffix.columns, expected)
|
| 15 |
+
|
| 16 |
+
with_pct_prefix = float_frame.add_prefix("%")
|
| 17 |
+
expected = Index([f"%{c}" for c in float_frame.columns])
|
| 18 |
+
tm.assert_index_equal(with_pct_prefix.columns, expected)
|
| 19 |
+
|
| 20 |
+
with_pct_suffix = float_frame.add_suffix("%")
|
| 21 |
+
expected = Index([f"{c}%" for c in float_frame.columns])
|
| 22 |
+
tm.assert_index_equal(with_pct_suffix.columns, expected)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def test_add_prefix_suffix_axis(float_frame):
|
| 26 |
+
# GH 47819
|
| 27 |
+
with_prefix = float_frame.add_prefix("foo#", axis=0)
|
| 28 |
+
expected = Index([f"foo#{c}" for c in float_frame.index])
|
| 29 |
+
tm.assert_index_equal(with_prefix.index, expected)
|
| 30 |
+
|
| 31 |
+
with_prefix = float_frame.add_prefix("foo#", axis=1)
|
| 32 |
+
expected = Index([f"foo#{c}" for c in float_frame.columns])
|
| 33 |
+
tm.assert_index_equal(with_prefix.columns, expected)
|
| 34 |
+
|
| 35 |
+
with_pct_suffix = float_frame.add_suffix("#foo", axis=0)
|
| 36 |
+
expected = Index([f"{c}#foo" for c in float_frame.index])
|
| 37 |
+
tm.assert_index_equal(with_pct_suffix.index, expected)
|
| 38 |
+
|
| 39 |
+
with_pct_suffix = float_frame.add_suffix("#foo", axis=1)
|
| 40 |
+
expected = Index([f"{c}#foo" for c in float_frame.columns])
|
| 41 |
+
tm.assert_index_equal(with_pct_suffix.columns, expected)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def test_add_prefix_suffix_invalid_axis(float_frame):
|
| 45 |
+
with pytest.raises(ValueError, match="No axis named 2 for object type DataFrame"):
|
| 46 |
+
float_frame.add_prefix("foo#", axis=2)
|
| 47 |
+
|
| 48 |
+
with pytest.raises(ValueError, match="No axis named 2 for object type DataFrame"):
|
| 49 |
+
float_frame.add_suffix("foo#", axis=2)
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_align.py
ADDED
|
@@ -0,0 +1,484 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import timezone
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pytest
|
| 5 |
+
|
| 6 |
+
import pandas as pd
|
| 7 |
+
from pandas import (
|
| 8 |
+
DataFrame,
|
| 9 |
+
Index,
|
| 10 |
+
Series,
|
| 11 |
+
date_range,
|
| 12 |
+
)
|
| 13 |
+
import pandas._testing as tm
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class TestDataFrameAlign:
|
| 17 |
+
def test_align_asfreq_method_raises(self):
|
| 18 |
+
df = DataFrame({"A": [1, np.nan, 2]})
|
| 19 |
+
msg = "Invalid fill method"
|
| 20 |
+
msg2 = "The 'method', 'limit', and 'fill_axis' keywords"
|
| 21 |
+
with pytest.raises(ValueError, match=msg):
|
| 22 |
+
with tm.assert_produces_warning(FutureWarning, match=msg2):
|
| 23 |
+
df.align(df.iloc[::-1], method="asfreq")
|
| 24 |
+
|
| 25 |
+
def test_frame_align_aware(self):
|
| 26 |
+
idx1 = date_range("2001", periods=5, freq="h", tz="US/Eastern")
|
| 27 |
+
idx2 = date_range("2001", periods=5, freq="2h", tz="US/Eastern")
|
| 28 |
+
df1 = DataFrame(np.random.default_rng(2).standard_normal((len(idx1), 3)), idx1)
|
| 29 |
+
df2 = DataFrame(np.random.default_rng(2).standard_normal((len(idx2), 3)), idx2)
|
| 30 |
+
new1, new2 = df1.align(df2)
|
| 31 |
+
assert df1.index.tz == new1.index.tz
|
| 32 |
+
assert df2.index.tz == new2.index.tz
|
| 33 |
+
|
| 34 |
+
# different timezones convert to UTC
|
| 35 |
+
|
| 36 |
+
# frame with frame
|
| 37 |
+
df1_central = df1.tz_convert("US/Central")
|
| 38 |
+
new1, new2 = df1.align(df1_central)
|
| 39 |
+
assert new1.index.tz is timezone.utc
|
| 40 |
+
assert new2.index.tz is timezone.utc
|
| 41 |
+
|
| 42 |
+
# frame with Series
|
| 43 |
+
new1, new2 = df1.align(df1_central[0], axis=0)
|
| 44 |
+
assert new1.index.tz is timezone.utc
|
| 45 |
+
assert new2.index.tz is timezone.utc
|
| 46 |
+
|
| 47 |
+
df1[0].align(df1_central, axis=0)
|
| 48 |
+
assert new1.index.tz is timezone.utc
|
| 49 |
+
assert new2.index.tz is timezone.utc
|
| 50 |
+
|
| 51 |
+
def test_align_float(self, float_frame, using_copy_on_write):
|
| 52 |
+
af, bf = float_frame.align(float_frame)
|
| 53 |
+
assert af._mgr is not float_frame._mgr
|
| 54 |
+
|
| 55 |
+
af, bf = float_frame.align(float_frame, copy=False)
|
| 56 |
+
if not using_copy_on_write:
|
| 57 |
+
assert af._mgr is float_frame._mgr
|
| 58 |
+
else:
|
| 59 |
+
assert af._mgr is not float_frame._mgr
|
| 60 |
+
|
| 61 |
+
# axis = 0
|
| 62 |
+
other = float_frame.iloc[:-5, :3]
|
| 63 |
+
af, bf = float_frame.align(other, axis=0, fill_value=-1)
|
| 64 |
+
|
| 65 |
+
tm.assert_index_equal(bf.columns, other.columns)
|
| 66 |
+
|
| 67 |
+
# test fill value
|
| 68 |
+
join_idx = float_frame.index.join(other.index)
|
| 69 |
+
diff_a = float_frame.index.difference(join_idx)
|
| 70 |
+
diff_a_vals = af.reindex(diff_a).values
|
| 71 |
+
assert (diff_a_vals == -1).all()
|
| 72 |
+
|
| 73 |
+
af, bf = float_frame.align(other, join="right", axis=0)
|
| 74 |
+
tm.assert_index_equal(bf.columns, other.columns)
|
| 75 |
+
tm.assert_index_equal(bf.index, other.index)
|
| 76 |
+
tm.assert_index_equal(af.index, other.index)
|
| 77 |
+
|
| 78 |
+
# axis = 1
|
| 79 |
+
other = float_frame.iloc[:-5, :3].copy()
|
| 80 |
+
af, bf = float_frame.align(other, axis=1)
|
| 81 |
+
tm.assert_index_equal(bf.columns, float_frame.columns)
|
| 82 |
+
tm.assert_index_equal(bf.index, other.index)
|
| 83 |
+
|
| 84 |
+
# test fill value
|
| 85 |
+
join_idx = float_frame.index.join(other.index)
|
| 86 |
+
diff_a = float_frame.index.difference(join_idx)
|
| 87 |
+
diff_a_vals = af.reindex(diff_a).values
|
| 88 |
+
|
| 89 |
+
assert (diff_a_vals == -1).all()
|
| 90 |
+
|
| 91 |
+
af, bf = float_frame.align(other, join="inner", axis=1)
|
| 92 |
+
tm.assert_index_equal(bf.columns, other.columns)
|
| 93 |
+
|
| 94 |
+
msg = (
|
| 95 |
+
"The 'method', 'limit', and 'fill_axis' keywords in DataFrame.align "
|
| 96 |
+
"are deprecated"
|
| 97 |
+
)
|
| 98 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 99 |
+
af, bf = float_frame.align(other, join="inner", axis=1, method="pad")
|
| 100 |
+
tm.assert_index_equal(bf.columns, other.columns)
|
| 101 |
+
|
| 102 |
+
msg = (
|
| 103 |
+
"The 'method', 'limit', and 'fill_axis' keywords in DataFrame.align "
|
| 104 |
+
"are deprecated"
|
| 105 |
+
)
|
| 106 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 107 |
+
af, bf = float_frame.align(
|
| 108 |
+
other.iloc[:, 0], join="inner", axis=1, method=None, fill_value=None
|
| 109 |
+
)
|
| 110 |
+
tm.assert_index_equal(bf.index, Index([]).astype(bf.index.dtype))
|
| 111 |
+
|
| 112 |
+
msg = (
|
| 113 |
+
"The 'method', 'limit', and 'fill_axis' keywords in DataFrame.align "
|
| 114 |
+
"are deprecated"
|
| 115 |
+
)
|
| 116 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 117 |
+
af, bf = float_frame.align(
|
| 118 |
+
other.iloc[:, 0], join="inner", axis=1, method=None, fill_value=0
|
| 119 |
+
)
|
| 120 |
+
tm.assert_index_equal(bf.index, Index([]).astype(bf.index.dtype))
|
| 121 |
+
|
| 122 |
+
# Try to align DataFrame to Series along bad axis
|
| 123 |
+
msg = "No axis named 2 for object type DataFrame"
|
| 124 |
+
with pytest.raises(ValueError, match=msg):
|
| 125 |
+
float_frame.align(af.iloc[0, :3], join="inner", axis=2)
|
| 126 |
+
|
| 127 |
+
def test_align_frame_with_series(self, float_frame):
|
| 128 |
+
# align dataframe to series with broadcast or not
|
| 129 |
+
idx = float_frame.index
|
| 130 |
+
s = Series(range(len(idx)), index=idx)
|
| 131 |
+
|
| 132 |
+
left, right = float_frame.align(s, axis=0)
|
| 133 |
+
tm.assert_index_equal(left.index, float_frame.index)
|
| 134 |
+
tm.assert_index_equal(right.index, float_frame.index)
|
| 135 |
+
assert isinstance(right, Series)
|
| 136 |
+
|
| 137 |
+
msg = "The 'broadcast_axis' keyword in DataFrame.align is deprecated"
|
| 138 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 139 |
+
left, right = float_frame.align(s, broadcast_axis=1)
|
| 140 |
+
tm.assert_index_equal(left.index, float_frame.index)
|
| 141 |
+
expected = {c: s for c in float_frame.columns}
|
| 142 |
+
expected = DataFrame(
|
| 143 |
+
expected, index=float_frame.index, columns=float_frame.columns
|
| 144 |
+
)
|
| 145 |
+
tm.assert_frame_equal(right, expected)
|
| 146 |
+
|
| 147 |
+
def test_align_series_condition(self):
|
| 148 |
+
# see gh-9558
|
| 149 |
+
df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
|
| 150 |
+
result = df[df["a"] == 2]
|
| 151 |
+
expected = DataFrame([[2, 5]], index=[1], columns=["a", "b"])
|
| 152 |
+
tm.assert_frame_equal(result, expected)
|
| 153 |
+
|
| 154 |
+
result = df.where(df["a"] == 2, 0)
|
| 155 |
+
expected = DataFrame({"a": [0, 2, 0], "b": [0, 5, 0]})
|
| 156 |
+
tm.assert_frame_equal(result, expected)
|
| 157 |
+
|
| 158 |
+
def test_align_int(self, int_frame):
|
| 159 |
+
# test other non-float types
|
| 160 |
+
other = DataFrame(index=range(5), columns=["A", "B", "C"])
|
| 161 |
+
|
| 162 |
+
msg = (
|
| 163 |
+
"The 'method', 'limit', and 'fill_axis' keywords in DataFrame.align "
|
| 164 |
+
"are deprecated"
|
| 165 |
+
)
|
| 166 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 167 |
+
af, bf = int_frame.align(other, join="inner", axis=1, method="pad")
|
| 168 |
+
tm.assert_index_equal(bf.columns, other.columns)
|
| 169 |
+
|
| 170 |
+
def test_align_mixed_type(self, float_string_frame):
|
| 171 |
+
msg = (
|
| 172 |
+
"The 'method', 'limit', and 'fill_axis' keywords in DataFrame.align "
|
| 173 |
+
"are deprecated"
|
| 174 |
+
)
|
| 175 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 176 |
+
af, bf = float_string_frame.align(
|
| 177 |
+
float_string_frame, join="inner", axis=1, method="pad"
|
| 178 |
+
)
|
| 179 |
+
tm.assert_index_equal(bf.columns, float_string_frame.columns)
|
| 180 |
+
|
| 181 |
+
def test_align_mixed_float(self, mixed_float_frame):
|
| 182 |
+
# mixed floats/ints
|
| 183 |
+
other = DataFrame(index=range(5), columns=["A", "B", "C"])
|
| 184 |
+
|
| 185 |
+
msg = (
|
| 186 |
+
"The 'method', 'limit', and 'fill_axis' keywords in DataFrame.align "
|
| 187 |
+
"are deprecated"
|
| 188 |
+
)
|
| 189 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 190 |
+
af, bf = mixed_float_frame.align(
|
| 191 |
+
other.iloc[:, 0], join="inner", axis=1, method=None, fill_value=0
|
| 192 |
+
)
|
| 193 |
+
tm.assert_index_equal(bf.index, Index([]))
|
| 194 |
+
|
| 195 |
+
def test_align_mixed_int(self, mixed_int_frame):
|
| 196 |
+
other = DataFrame(index=range(5), columns=["A", "B", "C"])
|
| 197 |
+
|
| 198 |
+
msg = (
|
| 199 |
+
"The 'method', 'limit', and 'fill_axis' keywords in DataFrame.align "
|
| 200 |
+
"are deprecated"
|
| 201 |
+
)
|
| 202 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 203 |
+
af, bf = mixed_int_frame.align(
|
| 204 |
+
other.iloc[:, 0], join="inner", axis=1, method=None, fill_value=0
|
| 205 |
+
)
|
| 206 |
+
tm.assert_index_equal(bf.index, Index([]))
|
| 207 |
+
|
| 208 |
+
@pytest.mark.parametrize(
|
| 209 |
+
"l_ordered,r_ordered,expected",
|
| 210 |
+
[
|
| 211 |
+
[True, True, pd.CategoricalIndex],
|
| 212 |
+
[True, False, Index],
|
| 213 |
+
[False, True, Index],
|
| 214 |
+
[False, False, pd.CategoricalIndex],
|
| 215 |
+
],
|
| 216 |
+
)
|
| 217 |
+
def test_align_categorical(self, l_ordered, r_ordered, expected):
|
| 218 |
+
# GH-28397
|
| 219 |
+
df_1 = DataFrame(
|
| 220 |
+
{
|
| 221 |
+
"A": np.arange(6, dtype="int64"),
|
| 222 |
+
"B": Series(list("aabbca")).astype(
|
| 223 |
+
pd.CategoricalDtype(list("cab"), ordered=l_ordered)
|
| 224 |
+
),
|
| 225 |
+
}
|
| 226 |
+
).set_index("B")
|
| 227 |
+
df_2 = DataFrame(
|
| 228 |
+
{
|
| 229 |
+
"A": np.arange(5, dtype="int64"),
|
| 230 |
+
"B": Series(list("babca")).astype(
|
| 231 |
+
pd.CategoricalDtype(list("cab"), ordered=r_ordered)
|
| 232 |
+
),
|
| 233 |
+
}
|
| 234 |
+
).set_index("B")
|
| 235 |
+
|
| 236 |
+
aligned_1, aligned_2 = df_1.align(df_2)
|
| 237 |
+
assert isinstance(aligned_1.index, expected)
|
| 238 |
+
assert isinstance(aligned_2.index, expected)
|
| 239 |
+
tm.assert_index_equal(aligned_1.index, aligned_2.index)
|
| 240 |
+
|
| 241 |
+
def test_align_multiindex(self):
|
| 242 |
+
# GH#10665
|
| 243 |
+
# same test cases as test_align_multiindex in test_series.py
|
| 244 |
+
|
| 245 |
+
midx = pd.MultiIndex.from_product(
|
| 246 |
+
[range(2), range(3), range(2)], names=("a", "b", "c")
|
| 247 |
+
)
|
| 248 |
+
idx = Index(range(2), name="b")
|
| 249 |
+
df1 = DataFrame(np.arange(12, dtype="int64"), index=midx)
|
| 250 |
+
df2 = DataFrame(np.arange(2, dtype="int64"), index=idx)
|
| 251 |
+
|
| 252 |
+
# these must be the same results (but flipped)
|
| 253 |
+
res1l, res1r = df1.align(df2, join="left")
|
| 254 |
+
res2l, res2r = df2.align(df1, join="right")
|
| 255 |
+
|
| 256 |
+
expl = df1
|
| 257 |
+
tm.assert_frame_equal(expl, res1l)
|
| 258 |
+
tm.assert_frame_equal(expl, res2r)
|
| 259 |
+
expr = DataFrame([0, 0, 1, 1, np.nan, np.nan] * 2, index=midx)
|
| 260 |
+
tm.assert_frame_equal(expr, res1r)
|
| 261 |
+
tm.assert_frame_equal(expr, res2l)
|
| 262 |
+
|
| 263 |
+
res1l, res1r = df1.align(df2, join="right")
|
| 264 |
+
res2l, res2r = df2.align(df1, join="left")
|
| 265 |
+
|
| 266 |
+
exp_idx = pd.MultiIndex.from_product(
|
| 267 |
+
[range(2), range(2), range(2)], names=("a", "b", "c")
|
| 268 |
+
)
|
| 269 |
+
expl = DataFrame([0, 1, 2, 3, 6, 7, 8, 9], index=exp_idx)
|
| 270 |
+
tm.assert_frame_equal(expl, res1l)
|
| 271 |
+
tm.assert_frame_equal(expl, res2r)
|
| 272 |
+
expr = DataFrame([0, 0, 1, 1] * 2, index=exp_idx)
|
| 273 |
+
tm.assert_frame_equal(expr, res1r)
|
| 274 |
+
tm.assert_frame_equal(expr, res2l)
|
| 275 |
+
|
| 276 |
+
def test_align_series_combinations(self):
|
| 277 |
+
df = DataFrame({"a": [1, 3, 5], "b": [1, 3, 5]}, index=list("ACE"))
|
| 278 |
+
s = Series([1, 2, 4], index=list("ABD"), name="x")
|
| 279 |
+
|
| 280 |
+
# frame + series
|
| 281 |
+
res1, res2 = df.align(s, axis=0)
|
| 282 |
+
exp1 = DataFrame(
|
| 283 |
+
{"a": [1, np.nan, 3, np.nan, 5], "b": [1, np.nan, 3, np.nan, 5]},
|
| 284 |
+
index=list("ABCDE"),
|
| 285 |
+
)
|
| 286 |
+
exp2 = Series([1, 2, np.nan, 4, np.nan], index=list("ABCDE"), name="x")
|
| 287 |
+
|
| 288 |
+
tm.assert_frame_equal(res1, exp1)
|
| 289 |
+
tm.assert_series_equal(res2, exp2)
|
| 290 |
+
|
| 291 |
+
# series + frame
|
| 292 |
+
res1, res2 = s.align(df)
|
| 293 |
+
tm.assert_series_equal(res1, exp2)
|
| 294 |
+
tm.assert_frame_equal(res2, exp1)
|
| 295 |
+
|
| 296 |
+
def test_multiindex_align_to_series_with_common_index_level(self):
|
| 297 |
+
# GH-46001
|
| 298 |
+
foo_index = Index([1, 2, 3], name="foo")
|
| 299 |
+
bar_index = Index([1, 2], name="bar")
|
| 300 |
+
|
| 301 |
+
series = Series([1, 2], index=bar_index, name="foo_series")
|
| 302 |
+
df = DataFrame(
|
| 303 |
+
{"col": np.arange(6)},
|
| 304 |
+
index=pd.MultiIndex.from_product([foo_index, bar_index]),
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
expected_r = Series([1, 2] * 3, index=df.index, name="foo_series")
|
| 308 |
+
result_l, result_r = df.align(series, axis=0)
|
| 309 |
+
|
| 310 |
+
tm.assert_frame_equal(result_l, df)
|
| 311 |
+
tm.assert_series_equal(result_r, expected_r)
|
| 312 |
+
|
| 313 |
+
def test_multiindex_align_to_series_with_common_index_level_missing_in_left(self):
|
| 314 |
+
# GH-46001
|
| 315 |
+
foo_index = Index([1, 2, 3], name="foo")
|
| 316 |
+
bar_index = Index([1, 2], name="bar")
|
| 317 |
+
|
| 318 |
+
series = Series(
|
| 319 |
+
[1, 2, 3, 4], index=Index([1, 2, 3, 4], name="bar"), name="foo_series"
|
| 320 |
+
)
|
| 321 |
+
df = DataFrame(
|
| 322 |
+
{"col": np.arange(6)},
|
| 323 |
+
index=pd.MultiIndex.from_product([foo_index, bar_index]),
|
| 324 |
+
)
|
| 325 |
+
|
| 326 |
+
expected_r = Series([1, 2] * 3, index=df.index, name="foo_series")
|
| 327 |
+
result_l, result_r = df.align(series, axis=0)
|
| 328 |
+
|
| 329 |
+
tm.assert_frame_equal(result_l, df)
|
| 330 |
+
tm.assert_series_equal(result_r, expected_r)
|
| 331 |
+
|
| 332 |
+
def test_multiindex_align_to_series_with_common_index_level_missing_in_right(self):
|
| 333 |
+
# GH-46001
|
| 334 |
+
foo_index = Index([1, 2, 3], name="foo")
|
| 335 |
+
bar_index = Index([1, 2, 3, 4], name="bar")
|
| 336 |
+
|
| 337 |
+
series = Series([1, 2], index=Index([1, 2], name="bar"), name="foo_series")
|
| 338 |
+
df = DataFrame(
|
| 339 |
+
{"col": np.arange(12)},
|
| 340 |
+
index=pd.MultiIndex.from_product([foo_index, bar_index]),
|
| 341 |
+
)
|
| 342 |
+
|
| 343 |
+
expected_r = Series(
|
| 344 |
+
[1, 2, np.nan, np.nan] * 3, index=df.index, name="foo_series"
|
| 345 |
+
)
|
| 346 |
+
result_l, result_r = df.align(series, axis=0)
|
| 347 |
+
|
| 348 |
+
tm.assert_frame_equal(result_l, df)
|
| 349 |
+
tm.assert_series_equal(result_r, expected_r)
|
| 350 |
+
|
| 351 |
+
def test_multiindex_align_to_series_with_common_index_level_missing_in_both(self):
|
| 352 |
+
# GH-46001
|
| 353 |
+
foo_index = Index([1, 2, 3], name="foo")
|
| 354 |
+
bar_index = Index([1, 3, 4], name="bar")
|
| 355 |
+
|
| 356 |
+
series = Series(
|
| 357 |
+
[1, 2, 3], index=Index([1, 2, 4], name="bar"), name="foo_series"
|
| 358 |
+
)
|
| 359 |
+
df = DataFrame(
|
| 360 |
+
{"col": np.arange(9)},
|
| 361 |
+
index=pd.MultiIndex.from_product([foo_index, bar_index]),
|
| 362 |
+
)
|
| 363 |
+
|
| 364 |
+
expected_r = Series([1, np.nan, 3] * 3, index=df.index, name="foo_series")
|
| 365 |
+
result_l, result_r = df.align(series, axis=0)
|
| 366 |
+
|
| 367 |
+
tm.assert_frame_equal(result_l, df)
|
| 368 |
+
tm.assert_series_equal(result_r, expected_r)
|
| 369 |
+
|
| 370 |
+
def test_multiindex_align_to_series_with_common_index_level_non_unique_cols(self):
|
| 371 |
+
# GH-46001
|
| 372 |
+
foo_index = Index([1, 2, 3], name="foo")
|
| 373 |
+
bar_index = Index([1, 2], name="bar")
|
| 374 |
+
|
| 375 |
+
series = Series([1, 2], index=bar_index, name="foo_series")
|
| 376 |
+
df = DataFrame(
|
| 377 |
+
np.arange(18).reshape(6, 3),
|
| 378 |
+
index=pd.MultiIndex.from_product([foo_index, bar_index]),
|
| 379 |
+
)
|
| 380 |
+
df.columns = ["cfoo", "cbar", "cfoo"]
|
| 381 |
+
|
| 382 |
+
expected = Series([1, 2] * 3, index=df.index, name="foo_series")
|
| 383 |
+
result_left, result_right = df.align(series, axis=0)
|
| 384 |
+
|
| 385 |
+
tm.assert_series_equal(result_right, expected)
|
| 386 |
+
tm.assert_index_equal(result_left.columns, df.columns)
|
| 387 |
+
|
| 388 |
+
def test_missing_axis_specification_exception(self):
|
| 389 |
+
df = DataFrame(np.arange(50).reshape((10, 5)))
|
| 390 |
+
series = Series(np.arange(5))
|
| 391 |
+
|
| 392 |
+
with pytest.raises(ValueError, match=r"axis=0 or 1"):
|
| 393 |
+
df.align(series)
|
| 394 |
+
|
| 395 |
+
@pytest.mark.parametrize("method", ["pad", "bfill"])
|
| 396 |
+
@pytest.mark.parametrize("axis", [0, 1, None])
|
| 397 |
+
@pytest.mark.parametrize("fill_axis", [0, 1])
|
| 398 |
+
@pytest.mark.parametrize("how", ["inner", "outer", "left", "right"])
|
| 399 |
+
@pytest.mark.parametrize(
|
| 400 |
+
"left_slice",
|
| 401 |
+
[
|
| 402 |
+
[slice(4), slice(10)],
|
| 403 |
+
[slice(0), slice(0)],
|
| 404 |
+
],
|
| 405 |
+
)
|
| 406 |
+
@pytest.mark.parametrize(
|
| 407 |
+
"right_slice",
|
| 408 |
+
[
|
| 409 |
+
[slice(2, None), slice(6, None)],
|
| 410 |
+
[slice(0), slice(0)],
|
| 411 |
+
],
|
| 412 |
+
)
|
| 413 |
+
@pytest.mark.parametrize("limit", [1, None])
|
| 414 |
+
def test_align_fill_method(
|
| 415 |
+
self, how, method, axis, fill_axis, float_frame, left_slice, right_slice, limit
|
| 416 |
+
):
|
| 417 |
+
frame = float_frame
|
| 418 |
+
left = frame.iloc[left_slice[0], left_slice[1]]
|
| 419 |
+
right = frame.iloc[right_slice[0], right_slice[1]]
|
| 420 |
+
|
| 421 |
+
msg = (
|
| 422 |
+
"The 'method', 'limit', and 'fill_axis' keywords in DataFrame.align "
|
| 423 |
+
"are deprecated"
|
| 424 |
+
)
|
| 425 |
+
|
| 426 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 427 |
+
aa, ab = left.align(
|
| 428 |
+
right,
|
| 429 |
+
axis=axis,
|
| 430 |
+
join=how,
|
| 431 |
+
method=method,
|
| 432 |
+
limit=limit,
|
| 433 |
+
fill_axis=fill_axis,
|
| 434 |
+
)
|
| 435 |
+
|
| 436 |
+
join_index, join_columns = None, None
|
| 437 |
+
|
| 438 |
+
ea, eb = left, right
|
| 439 |
+
if axis is None or axis == 0:
|
| 440 |
+
join_index = left.index.join(right.index, how=how)
|
| 441 |
+
ea = ea.reindex(index=join_index)
|
| 442 |
+
eb = eb.reindex(index=join_index)
|
| 443 |
+
|
| 444 |
+
if axis is None or axis == 1:
|
| 445 |
+
join_columns = left.columns.join(right.columns, how=how)
|
| 446 |
+
ea = ea.reindex(columns=join_columns)
|
| 447 |
+
eb = eb.reindex(columns=join_columns)
|
| 448 |
+
|
| 449 |
+
msg = "DataFrame.fillna with 'method' is deprecated"
|
| 450 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 451 |
+
ea = ea.fillna(axis=fill_axis, method=method, limit=limit)
|
| 452 |
+
eb = eb.fillna(axis=fill_axis, method=method, limit=limit)
|
| 453 |
+
|
| 454 |
+
tm.assert_frame_equal(aa, ea)
|
| 455 |
+
tm.assert_frame_equal(ab, eb)
|
| 456 |
+
|
| 457 |
+
def test_align_series_check_copy(self):
|
| 458 |
+
# GH#
|
| 459 |
+
df = DataFrame({0: [1, 2]})
|
| 460 |
+
ser = Series([1], name=0)
|
| 461 |
+
expected = ser.copy()
|
| 462 |
+
result, other = df.align(ser, axis=1)
|
| 463 |
+
ser.iloc[0] = 100
|
| 464 |
+
tm.assert_series_equal(other, expected)
|
| 465 |
+
|
| 466 |
+
def test_align_identical_different_object(self):
|
| 467 |
+
# GH#51032
|
| 468 |
+
df = DataFrame({"a": [1, 2]})
|
| 469 |
+
ser = Series([3, 4])
|
| 470 |
+
result, result2 = df.align(ser, axis=0)
|
| 471 |
+
tm.assert_frame_equal(result, df)
|
| 472 |
+
tm.assert_series_equal(result2, ser)
|
| 473 |
+
assert df is not result
|
| 474 |
+
assert ser is not result2
|
| 475 |
+
|
| 476 |
+
def test_align_identical_different_object_columns(self):
|
| 477 |
+
# GH#51032
|
| 478 |
+
df = DataFrame({"a": [1, 2]})
|
| 479 |
+
ser = Series([1], index=["a"])
|
| 480 |
+
result, result2 = df.align(ser, axis=1)
|
| 481 |
+
tm.assert_frame_equal(result, df)
|
| 482 |
+
tm.assert_series_equal(result2, ser)
|
| 483 |
+
assert df is not result
|
| 484 |
+
assert ser is not result2
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_at_time.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import time
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pytest
|
| 5 |
+
import pytz
|
| 6 |
+
|
| 7 |
+
from pandas._libs.tslibs import timezones
|
| 8 |
+
|
| 9 |
+
from pandas import (
|
| 10 |
+
DataFrame,
|
| 11 |
+
date_range,
|
| 12 |
+
)
|
| 13 |
+
import pandas._testing as tm
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class TestAtTime:
|
| 17 |
+
@pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
|
| 18 |
+
def test_localized_at_time(self, tzstr, frame_or_series):
|
| 19 |
+
tz = timezones.maybe_get_tz(tzstr)
|
| 20 |
+
|
| 21 |
+
rng = date_range("4/16/2012", "5/1/2012", freq="h")
|
| 22 |
+
ts = frame_or_series(
|
| 23 |
+
np.random.default_rng(2).standard_normal(len(rng)), index=rng
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
ts_local = ts.tz_localize(tzstr)
|
| 27 |
+
|
| 28 |
+
result = ts_local.at_time(time(10, 0))
|
| 29 |
+
expected = ts.at_time(time(10, 0)).tz_localize(tzstr)
|
| 30 |
+
tm.assert_equal(result, expected)
|
| 31 |
+
assert timezones.tz_compare(result.index.tz, tz)
|
| 32 |
+
|
| 33 |
+
def test_at_time(self, frame_or_series):
|
| 34 |
+
rng = date_range("1/1/2000", "1/5/2000", freq="5min")
|
| 35 |
+
ts = DataFrame(
|
| 36 |
+
np.random.default_rng(2).standard_normal((len(rng), 2)), index=rng
|
| 37 |
+
)
|
| 38 |
+
ts = tm.get_obj(ts, frame_or_series)
|
| 39 |
+
rs = ts.at_time(rng[1])
|
| 40 |
+
assert (rs.index.hour == rng[1].hour).all()
|
| 41 |
+
assert (rs.index.minute == rng[1].minute).all()
|
| 42 |
+
assert (rs.index.second == rng[1].second).all()
|
| 43 |
+
|
| 44 |
+
result = ts.at_time("9:30")
|
| 45 |
+
expected = ts.at_time(time(9, 30))
|
| 46 |
+
tm.assert_equal(result, expected)
|
| 47 |
+
|
| 48 |
+
def test_at_time_midnight(self, frame_or_series):
|
| 49 |
+
# midnight, everything
|
| 50 |
+
rng = date_range("1/1/2000", "1/31/2000")
|
| 51 |
+
ts = DataFrame(
|
| 52 |
+
np.random.default_rng(2).standard_normal((len(rng), 3)), index=rng
|
| 53 |
+
)
|
| 54 |
+
ts = tm.get_obj(ts, frame_or_series)
|
| 55 |
+
|
| 56 |
+
result = ts.at_time(time(0, 0))
|
| 57 |
+
tm.assert_equal(result, ts)
|
| 58 |
+
|
| 59 |
+
def test_at_time_nonexistent(self, frame_or_series):
|
| 60 |
+
# time doesn't exist
|
| 61 |
+
rng = date_range("1/1/2012", freq="23Min", periods=384)
|
| 62 |
+
ts = DataFrame(np.random.default_rng(2).standard_normal(len(rng)), rng)
|
| 63 |
+
ts = tm.get_obj(ts, frame_or_series)
|
| 64 |
+
rs = ts.at_time("16:00")
|
| 65 |
+
assert len(rs) == 0
|
| 66 |
+
|
| 67 |
+
@pytest.mark.parametrize(
|
| 68 |
+
"hour", ["1:00", "1:00AM", time(1), time(1, tzinfo=pytz.UTC)]
|
| 69 |
+
)
|
| 70 |
+
def test_at_time_errors(self, hour):
|
| 71 |
+
# GH#24043
|
| 72 |
+
dti = date_range("2018", periods=3, freq="h")
|
| 73 |
+
df = DataFrame(list(range(len(dti))), index=dti)
|
| 74 |
+
if getattr(hour, "tzinfo", None) is None:
|
| 75 |
+
result = df.at_time(hour)
|
| 76 |
+
expected = df.iloc[1:2]
|
| 77 |
+
tm.assert_frame_equal(result, expected)
|
| 78 |
+
else:
|
| 79 |
+
with pytest.raises(ValueError, match="Index must be timezone"):
|
| 80 |
+
df.at_time(hour)
|
| 81 |
+
|
| 82 |
+
def test_at_time_tz(self):
|
| 83 |
+
# GH#24043
|
| 84 |
+
dti = date_range("2018", periods=3, freq="h", tz="US/Pacific")
|
| 85 |
+
df = DataFrame(list(range(len(dti))), index=dti)
|
| 86 |
+
result = df.at_time(time(4, tzinfo=pytz.timezone("US/Eastern")))
|
| 87 |
+
expected = df.iloc[1:2]
|
| 88 |
+
tm.assert_frame_equal(result, expected)
|
| 89 |
+
|
| 90 |
+
def test_at_time_raises(self, frame_or_series):
|
| 91 |
+
# GH#20725
|
| 92 |
+
obj = DataFrame([[1, 2, 3], [4, 5, 6]])
|
| 93 |
+
obj = tm.get_obj(obj, frame_or_series)
|
| 94 |
+
msg = "Index must be DatetimeIndex"
|
| 95 |
+
with pytest.raises(TypeError, match=msg): # index is not a DatetimeIndex
|
| 96 |
+
obj.at_time("00:00")
|
| 97 |
+
|
| 98 |
+
@pytest.mark.parametrize("axis", ["index", "columns", 0, 1])
|
| 99 |
+
def test_at_time_axis(self, axis):
|
| 100 |
+
# issue 8839
|
| 101 |
+
rng = date_range("1/1/2000", "1/5/2000", freq="5min")
|
| 102 |
+
ts = DataFrame(np.random.default_rng(2).standard_normal((len(rng), len(rng))))
|
| 103 |
+
ts.index, ts.columns = rng, rng
|
| 104 |
+
|
| 105 |
+
indices = rng[(rng.hour == 9) & (rng.minute == 30) & (rng.second == 0)]
|
| 106 |
+
|
| 107 |
+
if axis in ["index", 0]:
|
| 108 |
+
expected = ts.loc[indices, :]
|
| 109 |
+
elif axis in ["columns", 1]:
|
| 110 |
+
expected = ts.loc[:, indices]
|
| 111 |
+
|
| 112 |
+
result = ts.at_time("9:30", axis=axis)
|
| 113 |
+
|
| 114 |
+
# Without clearing freq, result has freq 1440T and expected 5T
|
| 115 |
+
result.index = result.index._with_freq(None)
|
| 116 |
+
expected.index = expected.index._with_freq(None)
|
| 117 |
+
tm.assert_frame_equal(result, expected)
|
| 118 |
+
|
| 119 |
+
def test_at_time_datetimeindex(self):
|
| 120 |
+
index = date_range("2012-01-01", "2012-01-05", freq="30min")
|
| 121 |
+
df = DataFrame(
|
| 122 |
+
np.random.default_rng(2).standard_normal((len(index), 5)), index=index
|
| 123 |
+
)
|
| 124 |
+
akey = time(12, 0, 0)
|
| 125 |
+
ainds = [24, 72, 120, 168]
|
| 126 |
+
|
| 127 |
+
result = df.at_time(akey)
|
| 128 |
+
expected = df.loc[akey]
|
| 129 |
+
expected2 = df.iloc[ainds]
|
| 130 |
+
tm.assert_frame_equal(result, expected)
|
| 131 |
+
tm.assert_frame_equal(result, expected2)
|
| 132 |
+
assert len(result) == 4
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_between_time.py
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import (
|
| 2 |
+
datetime,
|
| 3 |
+
time,
|
| 4 |
+
)
|
| 5 |
+
|
| 6 |
+
import numpy as np
|
| 7 |
+
import pytest
|
| 8 |
+
|
| 9 |
+
from pandas._libs.tslibs import timezones
|
| 10 |
+
import pandas.util._test_decorators as td
|
| 11 |
+
|
| 12 |
+
from pandas import (
|
| 13 |
+
DataFrame,
|
| 14 |
+
Series,
|
| 15 |
+
date_range,
|
| 16 |
+
)
|
| 17 |
+
import pandas._testing as tm
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class TestBetweenTime:
|
| 21 |
+
@td.skip_if_not_us_locale
|
| 22 |
+
def test_between_time_formats(self, frame_or_series):
|
| 23 |
+
# GH#11818
|
| 24 |
+
rng = date_range("1/1/2000", "1/5/2000", freq="5min")
|
| 25 |
+
ts = DataFrame(
|
| 26 |
+
np.random.default_rng(2).standard_normal((len(rng), 2)), index=rng
|
| 27 |
+
)
|
| 28 |
+
ts = tm.get_obj(ts, frame_or_series)
|
| 29 |
+
|
| 30 |
+
strings = [
|
| 31 |
+
("2:00", "2:30"),
|
| 32 |
+
("0200", "0230"),
|
| 33 |
+
("2:00am", "2:30am"),
|
| 34 |
+
("0200am", "0230am"),
|
| 35 |
+
("2:00:00", "2:30:00"),
|
| 36 |
+
("020000", "023000"),
|
| 37 |
+
("2:00:00am", "2:30:00am"),
|
| 38 |
+
("020000am", "023000am"),
|
| 39 |
+
]
|
| 40 |
+
expected_length = 28
|
| 41 |
+
|
| 42 |
+
for time_string in strings:
|
| 43 |
+
assert len(ts.between_time(*time_string)) == expected_length
|
| 44 |
+
|
| 45 |
+
@pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
|
| 46 |
+
def test_localized_between_time(self, tzstr, frame_or_series):
|
| 47 |
+
tz = timezones.maybe_get_tz(tzstr)
|
| 48 |
+
|
| 49 |
+
rng = date_range("4/16/2012", "5/1/2012", freq="h")
|
| 50 |
+
ts = Series(np.random.default_rng(2).standard_normal(len(rng)), index=rng)
|
| 51 |
+
if frame_or_series is DataFrame:
|
| 52 |
+
ts = ts.to_frame()
|
| 53 |
+
|
| 54 |
+
ts_local = ts.tz_localize(tzstr)
|
| 55 |
+
|
| 56 |
+
t1, t2 = time(10, 0), time(11, 0)
|
| 57 |
+
result = ts_local.between_time(t1, t2)
|
| 58 |
+
expected = ts.between_time(t1, t2).tz_localize(tzstr)
|
| 59 |
+
tm.assert_equal(result, expected)
|
| 60 |
+
assert timezones.tz_compare(result.index.tz, tz)
|
| 61 |
+
|
| 62 |
+
def test_between_time_types(self, frame_or_series):
|
| 63 |
+
# GH11818
|
| 64 |
+
rng = date_range("1/1/2000", "1/5/2000", freq="5min")
|
| 65 |
+
obj = DataFrame({"A": 0}, index=rng)
|
| 66 |
+
obj = tm.get_obj(obj, frame_or_series)
|
| 67 |
+
|
| 68 |
+
msg = r"Cannot convert arg \[datetime\.datetime\(2010, 1, 2, 1, 0\)\] to a time"
|
| 69 |
+
with pytest.raises(ValueError, match=msg):
|
| 70 |
+
obj.between_time(datetime(2010, 1, 2, 1), datetime(2010, 1, 2, 5))
|
| 71 |
+
|
| 72 |
+
def test_between_time(self, inclusive_endpoints_fixture, frame_or_series):
|
| 73 |
+
rng = date_range("1/1/2000", "1/5/2000", freq="5min")
|
| 74 |
+
ts = DataFrame(
|
| 75 |
+
np.random.default_rng(2).standard_normal((len(rng), 2)), index=rng
|
| 76 |
+
)
|
| 77 |
+
ts = tm.get_obj(ts, frame_or_series)
|
| 78 |
+
|
| 79 |
+
stime = time(0, 0)
|
| 80 |
+
etime = time(1, 0)
|
| 81 |
+
inclusive = inclusive_endpoints_fixture
|
| 82 |
+
|
| 83 |
+
filtered = ts.between_time(stime, etime, inclusive=inclusive)
|
| 84 |
+
exp_len = 13 * 4 + 1
|
| 85 |
+
|
| 86 |
+
if inclusive in ["right", "neither"]:
|
| 87 |
+
exp_len -= 5
|
| 88 |
+
if inclusive in ["left", "neither"]:
|
| 89 |
+
exp_len -= 4
|
| 90 |
+
|
| 91 |
+
assert len(filtered) == exp_len
|
| 92 |
+
for rs in filtered.index:
|
| 93 |
+
t = rs.time()
|
| 94 |
+
if inclusive in ["left", "both"]:
|
| 95 |
+
assert t >= stime
|
| 96 |
+
else:
|
| 97 |
+
assert t > stime
|
| 98 |
+
|
| 99 |
+
if inclusive in ["right", "both"]:
|
| 100 |
+
assert t <= etime
|
| 101 |
+
else:
|
| 102 |
+
assert t < etime
|
| 103 |
+
|
| 104 |
+
result = ts.between_time("00:00", "01:00")
|
| 105 |
+
expected = ts.between_time(stime, etime)
|
| 106 |
+
tm.assert_equal(result, expected)
|
| 107 |
+
|
| 108 |
+
# across midnight
|
| 109 |
+
rng = date_range("1/1/2000", "1/5/2000", freq="5min")
|
| 110 |
+
ts = DataFrame(
|
| 111 |
+
np.random.default_rng(2).standard_normal((len(rng), 2)), index=rng
|
| 112 |
+
)
|
| 113 |
+
ts = tm.get_obj(ts, frame_or_series)
|
| 114 |
+
stime = time(22, 0)
|
| 115 |
+
etime = time(9, 0)
|
| 116 |
+
|
| 117 |
+
filtered = ts.between_time(stime, etime, inclusive=inclusive)
|
| 118 |
+
exp_len = (12 * 11 + 1) * 4 + 1
|
| 119 |
+
if inclusive in ["right", "neither"]:
|
| 120 |
+
exp_len -= 4
|
| 121 |
+
if inclusive in ["left", "neither"]:
|
| 122 |
+
exp_len -= 4
|
| 123 |
+
|
| 124 |
+
assert len(filtered) == exp_len
|
| 125 |
+
for rs in filtered.index:
|
| 126 |
+
t = rs.time()
|
| 127 |
+
if inclusive in ["left", "both"]:
|
| 128 |
+
assert (t >= stime) or (t <= etime)
|
| 129 |
+
else:
|
| 130 |
+
assert (t > stime) or (t <= etime)
|
| 131 |
+
|
| 132 |
+
if inclusive in ["right", "both"]:
|
| 133 |
+
assert (t <= etime) or (t >= stime)
|
| 134 |
+
else:
|
| 135 |
+
assert (t < etime) or (t >= stime)
|
| 136 |
+
|
| 137 |
+
def test_between_time_raises(self, frame_or_series):
|
| 138 |
+
# GH#20725
|
| 139 |
+
obj = DataFrame([[1, 2, 3], [4, 5, 6]])
|
| 140 |
+
obj = tm.get_obj(obj, frame_or_series)
|
| 141 |
+
|
| 142 |
+
msg = "Index must be DatetimeIndex"
|
| 143 |
+
with pytest.raises(TypeError, match=msg): # index is not a DatetimeIndex
|
| 144 |
+
obj.between_time(start_time="00:00", end_time="12:00")
|
| 145 |
+
|
| 146 |
+
def test_between_time_axis(self, frame_or_series):
|
| 147 |
+
# GH#8839
|
| 148 |
+
rng = date_range("1/1/2000", periods=100, freq="10min")
|
| 149 |
+
ts = Series(np.random.default_rng(2).standard_normal(len(rng)), index=rng)
|
| 150 |
+
if frame_or_series is DataFrame:
|
| 151 |
+
ts = ts.to_frame()
|
| 152 |
+
|
| 153 |
+
stime, etime = ("08:00:00", "09:00:00")
|
| 154 |
+
expected_length = 7
|
| 155 |
+
|
| 156 |
+
assert len(ts.between_time(stime, etime)) == expected_length
|
| 157 |
+
assert len(ts.between_time(stime, etime, axis=0)) == expected_length
|
| 158 |
+
msg = f"No axis named {ts.ndim} for object type {type(ts).__name__}"
|
| 159 |
+
with pytest.raises(ValueError, match=msg):
|
| 160 |
+
ts.between_time(stime, etime, axis=ts.ndim)
|
| 161 |
+
|
| 162 |
+
def test_between_time_axis_aliases(self, axis):
|
| 163 |
+
# GH#8839
|
| 164 |
+
rng = date_range("1/1/2000", periods=100, freq="10min")
|
| 165 |
+
ts = DataFrame(np.random.default_rng(2).standard_normal((len(rng), len(rng))))
|
| 166 |
+
stime, etime = ("08:00:00", "09:00:00")
|
| 167 |
+
exp_len = 7
|
| 168 |
+
|
| 169 |
+
if axis in ["index", 0]:
|
| 170 |
+
ts.index = rng
|
| 171 |
+
assert len(ts.between_time(stime, etime)) == exp_len
|
| 172 |
+
assert len(ts.between_time(stime, etime, axis=0)) == exp_len
|
| 173 |
+
|
| 174 |
+
if axis in ["columns", 1]:
|
| 175 |
+
ts.columns = rng
|
| 176 |
+
selected = ts.between_time(stime, etime, axis=1).columns
|
| 177 |
+
assert len(selected) == exp_len
|
| 178 |
+
|
| 179 |
+
def test_between_time_axis_raises(self, axis):
|
| 180 |
+
# issue 8839
|
| 181 |
+
rng = date_range("1/1/2000", periods=100, freq="10min")
|
| 182 |
+
mask = np.arange(0, len(rng))
|
| 183 |
+
rand_data = np.random.default_rng(2).standard_normal((len(rng), len(rng)))
|
| 184 |
+
ts = DataFrame(rand_data, index=rng, columns=rng)
|
| 185 |
+
stime, etime = ("08:00:00", "09:00:00")
|
| 186 |
+
|
| 187 |
+
msg = "Index must be DatetimeIndex"
|
| 188 |
+
if axis in ["columns", 1]:
|
| 189 |
+
ts.index = mask
|
| 190 |
+
with pytest.raises(TypeError, match=msg):
|
| 191 |
+
ts.between_time(stime, etime)
|
| 192 |
+
with pytest.raises(TypeError, match=msg):
|
| 193 |
+
ts.between_time(stime, etime, axis=0)
|
| 194 |
+
|
| 195 |
+
if axis in ["index", 0]:
|
| 196 |
+
ts.columns = mask
|
| 197 |
+
with pytest.raises(TypeError, match=msg):
|
| 198 |
+
ts.between_time(stime, etime, axis=1)
|
| 199 |
+
|
| 200 |
+
def test_between_time_datetimeindex(self):
|
| 201 |
+
index = date_range("2012-01-01", "2012-01-05", freq="30min")
|
| 202 |
+
df = DataFrame(
|
| 203 |
+
np.random.default_rng(2).standard_normal((len(index), 5)), index=index
|
| 204 |
+
)
|
| 205 |
+
bkey = slice(time(13, 0, 0), time(14, 0, 0))
|
| 206 |
+
binds = [26, 27, 28, 74, 75, 76, 122, 123, 124, 170, 171, 172]
|
| 207 |
+
|
| 208 |
+
result = df.between_time(bkey.start, bkey.stop)
|
| 209 |
+
expected = df.loc[bkey]
|
| 210 |
+
expected2 = df.iloc[binds]
|
| 211 |
+
tm.assert_frame_equal(result, expected)
|
| 212 |
+
tm.assert_frame_equal(result, expected2)
|
| 213 |
+
assert len(result) == 12
|
| 214 |
+
|
| 215 |
+
def test_between_time_incorrect_arg_inclusive(self):
|
| 216 |
+
# GH40245
|
| 217 |
+
rng = date_range("1/1/2000", "1/5/2000", freq="5min")
|
| 218 |
+
ts = DataFrame(
|
| 219 |
+
np.random.default_rng(2).standard_normal((len(rng), 2)), index=rng
|
| 220 |
+
)
|
| 221 |
+
|
| 222 |
+
stime = time(0, 0)
|
| 223 |
+
etime = time(1, 0)
|
| 224 |
+
inclusive = "bad_string"
|
| 225 |
+
msg = "Inclusive has to be either 'both', 'neither', 'left' or 'right'"
|
| 226 |
+
with pytest.raises(ValueError, match=msg):
|
| 227 |
+
ts.between_time(stime, etime, inclusive=inclusive)
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_combine.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pytest
|
| 3 |
+
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import pandas._testing as tm
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class TestCombine:
|
| 9 |
+
@pytest.mark.parametrize(
|
| 10 |
+
"data",
|
| 11 |
+
[
|
| 12 |
+
pd.date_range("2000", periods=4),
|
| 13 |
+
pd.date_range("2000", periods=4, tz="US/Central"),
|
| 14 |
+
pd.period_range("2000", periods=4),
|
| 15 |
+
pd.timedelta_range(0, periods=4),
|
| 16 |
+
],
|
| 17 |
+
)
|
| 18 |
+
def test_combine_datetlike_udf(self, data):
|
| 19 |
+
# GH#23079
|
| 20 |
+
df = pd.DataFrame({"A": data})
|
| 21 |
+
other = df.copy()
|
| 22 |
+
df.iloc[1, 0] = None
|
| 23 |
+
|
| 24 |
+
def combiner(a, b):
|
| 25 |
+
return b
|
| 26 |
+
|
| 27 |
+
result = df.combine(other, combiner)
|
| 28 |
+
tm.assert_frame_equal(result, other)
|
| 29 |
+
|
| 30 |
+
def test_combine_generic(self, float_frame):
|
| 31 |
+
df1 = float_frame
|
| 32 |
+
df2 = float_frame.loc[float_frame.index[:-5], ["A", "B", "C"]]
|
| 33 |
+
|
| 34 |
+
combined = df1.combine(df2, np.add)
|
| 35 |
+
combined2 = df2.combine(df1, np.add)
|
| 36 |
+
assert combined["D"].isna().all()
|
| 37 |
+
assert combined2["D"].isna().all()
|
| 38 |
+
|
| 39 |
+
chunk = combined.loc[combined.index[:-5], ["A", "B", "C"]]
|
| 40 |
+
chunk2 = combined2.loc[combined2.index[:-5], ["A", "B", "C"]]
|
| 41 |
+
|
| 42 |
+
exp = (
|
| 43 |
+
float_frame.loc[float_frame.index[:-5], ["A", "B", "C"]].reindex_like(chunk)
|
| 44 |
+
* 2
|
| 45 |
+
)
|
| 46 |
+
tm.assert_frame_equal(chunk, exp)
|
| 47 |
+
tm.assert_frame_equal(chunk2, exp)
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_combine_first.py
ADDED
|
@@ -0,0 +1,556 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import datetime
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pytest
|
| 5 |
+
|
| 6 |
+
from pandas.core.dtypes.cast import find_common_type
|
| 7 |
+
from pandas.core.dtypes.common import is_dtype_equal
|
| 8 |
+
|
| 9 |
+
import pandas as pd
|
| 10 |
+
from pandas import (
|
| 11 |
+
DataFrame,
|
| 12 |
+
Index,
|
| 13 |
+
MultiIndex,
|
| 14 |
+
Series,
|
| 15 |
+
)
|
| 16 |
+
import pandas._testing as tm
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class TestDataFrameCombineFirst:
|
| 20 |
+
def test_combine_first_mixed(self):
|
| 21 |
+
a = Series(["a", "b"], index=range(2))
|
| 22 |
+
b = Series(range(2), index=range(2))
|
| 23 |
+
f = DataFrame({"A": a, "B": b})
|
| 24 |
+
|
| 25 |
+
a = Series(["a", "b"], index=range(5, 7))
|
| 26 |
+
b = Series(range(2), index=range(5, 7))
|
| 27 |
+
g = DataFrame({"A": a, "B": b})
|
| 28 |
+
|
| 29 |
+
exp = DataFrame({"A": list("abab"), "B": [0, 1, 0, 1]}, index=[0, 1, 5, 6])
|
| 30 |
+
combined = f.combine_first(g)
|
| 31 |
+
tm.assert_frame_equal(combined, exp)
|
| 32 |
+
|
| 33 |
+
def test_combine_first(self, float_frame, using_infer_string):
|
| 34 |
+
# disjoint
|
| 35 |
+
head, tail = float_frame[:5], float_frame[5:]
|
| 36 |
+
|
| 37 |
+
combined = head.combine_first(tail)
|
| 38 |
+
reordered_frame = float_frame.reindex(combined.index)
|
| 39 |
+
tm.assert_frame_equal(combined, reordered_frame)
|
| 40 |
+
tm.assert_index_equal(combined.columns, float_frame.columns)
|
| 41 |
+
tm.assert_series_equal(combined["A"], reordered_frame["A"])
|
| 42 |
+
|
| 43 |
+
# same index
|
| 44 |
+
fcopy = float_frame.copy()
|
| 45 |
+
fcopy["A"] = 1
|
| 46 |
+
del fcopy["C"]
|
| 47 |
+
|
| 48 |
+
fcopy2 = float_frame.copy()
|
| 49 |
+
fcopy2["B"] = 0
|
| 50 |
+
del fcopy2["D"]
|
| 51 |
+
|
| 52 |
+
combined = fcopy.combine_first(fcopy2)
|
| 53 |
+
|
| 54 |
+
assert (combined["A"] == 1).all()
|
| 55 |
+
tm.assert_series_equal(combined["B"], fcopy["B"])
|
| 56 |
+
tm.assert_series_equal(combined["C"], fcopy2["C"])
|
| 57 |
+
tm.assert_series_equal(combined["D"], fcopy["D"])
|
| 58 |
+
|
| 59 |
+
# overlap
|
| 60 |
+
head, tail = reordered_frame[:10].copy(), reordered_frame
|
| 61 |
+
head["A"] = 1
|
| 62 |
+
|
| 63 |
+
combined = head.combine_first(tail)
|
| 64 |
+
assert (combined["A"][:10] == 1).all()
|
| 65 |
+
|
| 66 |
+
# reverse overlap
|
| 67 |
+
tail.iloc[:10, tail.columns.get_loc("A")] = 0
|
| 68 |
+
combined = tail.combine_first(head)
|
| 69 |
+
assert (combined["A"][:10] == 0).all()
|
| 70 |
+
|
| 71 |
+
# no overlap
|
| 72 |
+
f = float_frame[:10]
|
| 73 |
+
g = float_frame[10:]
|
| 74 |
+
combined = f.combine_first(g)
|
| 75 |
+
tm.assert_series_equal(combined["A"].reindex(f.index), f["A"])
|
| 76 |
+
tm.assert_series_equal(combined["A"].reindex(g.index), g["A"])
|
| 77 |
+
|
| 78 |
+
# corner cases
|
| 79 |
+
warning = FutureWarning if using_infer_string else None
|
| 80 |
+
with tm.assert_produces_warning(warning, match="empty entries"):
|
| 81 |
+
comb = float_frame.combine_first(DataFrame())
|
| 82 |
+
tm.assert_frame_equal(comb, float_frame)
|
| 83 |
+
|
| 84 |
+
comb = DataFrame().combine_first(float_frame)
|
| 85 |
+
tm.assert_frame_equal(comb, float_frame.sort_index())
|
| 86 |
+
|
| 87 |
+
comb = float_frame.combine_first(DataFrame(index=["faz", "boo"]))
|
| 88 |
+
assert "faz" in comb.index
|
| 89 |
+
|
| 90 |
+
# #2525
|
| 91 |
+
df = DataFrame({"a": [1]}, index=[datetime(2012, 1, 1)])
|
| 92 |
+
df2 = DataFrame(columns=["b"])
|
| 93 |
+
result = df.combine_first(df2)
|
| 94 |
+
assert "b" in result
|
| 95 |
+
|
| 96 |
+
def test_combine_first_mixed_bug(self):
|
| 97 |
+
idx = Index(["a", "b", "c", "e"])
|
| 98 |
+
ser1 = Series([5.0, -9.0, 4.0, 100.0], index=idx)
|
| 99 |
+
ser2 = Series(["a", "b", "c", "e"], index=idx)
|
| 100 |
+
ser3 = Series([12, 4, 5, 97], index=idx)
|
| 101 |
+
|
| 102 |
+
frame1 = DataFrame({"col0": ser1, "col2": ser2, "col3": ser3})
|
| 103 |
+
|
| 104 |
+
idx = Index(["a", "b", "c", "f"])
|
| 105 |
+
ser1 = Series([5.0, -9.0, 4.0, 100.0], index=idx)
|
| 106 |
+
ser2 = Series(["a", "b", "c", "f"], index=idx)
|
| 107 |
+
ser3 = Series([12, 4, 5, 97], index=idx)
|
| 108 |
+
|
| 109 |
+
frame2 = DataFrame({"col1": ser1, "col2": ser2, "col5": ser3})
|
| 110 |
+
|
| 111 |
+
combined = frame1.combine_first(frame2)
|
| 112 |
+
assert len(combined.columns) == 5
|
| 113 |
+
|
| 114 |
+
def test_combine_first_same_as_in_update(self):
|
| 115 |
+
# gh 3016 (same as in update)
|
| 116 |
+
df = DataFrame(
|
| 117 |
+
[[1.0, 2.0, False, True], [4.0, 5.0, True, False]],
|
| 118 |
+
columns=["A", "B", "bool1", "bool2"],
|
| 119 |
+
)
|
| 120 |
+
|
| 121 |
+
other = DataFrame([[45, 45]], index=[0], columns=["A", "B"])
|
| 122 |
+
result = df.combine_first(other)
|
| 123 |
+
tm.assert_frame_equal(result, df)
|
| 124 |
+
|
| 125 |
+
df.loc[0, "A"] = np.nan
|
| 126 |
+
result = df.combine_first(other)
|
| 127 |
+
df.loc[0, "A"] = 45
|
| 128 |
+
tm.assert_frame_equal(result, df)
|
| 129 |
+
|
| 130 |
+
def test_combine_first_doc_example(self):
|
| 131 |
+
# doc example
|
| 132 |
+
df1 = DataFrame(
|
| 133 |
+
{"A": [1.0, np.nan, 3.0, 5.0, np.nan], "B": [np.nan, 2.0, 3.0, np.nan, 6.0]}
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
+
df2 = DataFrame(
|
| 137 |
+
{
|
| 138 |
+
"A": [5.0, 2.0, 4.0, np.nan, 3.0, 7.0],
|
| 139 |
+
"B": [np.nan, np.nan, 3.0, 4.0, 6.0, 8.0],
|
| 140 |
+
}
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
result = df1.combine_first(df2)
|
| 144 |
+
expected = DataFrame({"A": [1, 2, 3, 5, 3, 7.0], "B": [np.nan, 2, 3, 4, 6, 8]})
|
| 145 |
+
tm.assert_frame_equal(result, expected)
|
| 146 |
+
|
| 147 |
+
def test_combine_first_return_obj_type_with_bools(self):
|
| 148 |
+
# GH3552
|
| 149 |
+
|
| 150 |
+
df1 = DataFrame(
|
| 151 |
+
[[np.nan, 3.0, True], [-4.6, np.nan, True], [np.nan, 7.0, False]]
|
| 152 |
+
)
|
| 153 |
+
df2 = DataFrame([[-42.6, np.nan, True], [-5.0, 1.6, False]], index=[1, 2])
|
| 154 |
+
|
| 155 |
+
expected = Series([True, True, False], name=2, dtype=bool)
|
| 156 |
+
|
| 157 |
+
result_12 = df1.combine_first(df2)[2]
|
| 158 |
+
tm.assert_series_equal(result_12, expected)
|
| 159 |
+
|
| 160 |
+
result_21 = df2.combine_first(df1)[2]
|
| 161 |
+
tm.assert_series_equal(result_21, expected)
|
| 162 |
+
|
| 163 |
+
@pytest.mark.parametrize(
|
| 164 |
+
"data1, data2, data_expected",
|
| 165 |
+
(
|
| 166 |
+
(
|
| 167 |
+
[datetime(2000, 1, 1), datetime(2000, 1, 2), datetime(2000, 1, 3)],
|
| 168 |
+
[pd.NaT, pd.NaT, pd.NaT],
|
| 169 |
+
[datetime(2000, 1, 1), datetime(2000, 1, 2), datetime(2000, 1, 3)],
|
| 170 |
+
),
|
| 171 |
+
(
|
| 172 |
+
[pd.NaT, pd.NaT, pd.NaT],
|
| 173 |
+
[datetime(2000, 1, 1), datetime(2000, 1, 2), datetime(2000, 1, 3)],
|
| 174 |
+
[datetime(2000, 1, 1), datetime(2000, 1, 2), datetime(2000, 1, 3)],
|
| 175 |
+
),
|
| 176 |
+
(
|
| 177 |
+
[datetime(2000, 1, 2), pd.NaT, pd.NaT],
|
| 178 |
+
[datetime(2000, 1, 1), datetime(2000, 1, 2), datetime(2000, 1, 3)],
|
| 179 |
+
[datetime(2000, 1, 2), datetime(2000, 1, 2), datetime(2000, 1, 3)],
|
| 180 |
+
),
|
| 181 |
+
(
|
| 182 |
+
[datetime(2000, 1, 1), datetime(2000, 1, 2), datetime(2000, 1, 3)],
|
| 183 |
+
[datetime(2000, 1, 2), pd.NaT, pd.NaT],
|
| 184 |
+
[datetime(2000, 1, 1), datetime(2000, 1, 2), datetime(2000, 1, 3)],
|
| 185 |
+
),
|
| 186 |
+
),
|
| 187 |
+
)
|
| 188 |
+
def test_combine_first_convert_datatime_correctly(
|
| 189 |
+
self, data1, data2, data_expected
|
| 190 |
+
):
|
| 191 |
+
# GH 3593
|
| 192 |
+
|
| 193 |
+
df1, df2 = DataFrame({"a": data1}), DataFrame({"a": data2})
|
| 194 |
+
result = df1.combine_first(df2)
|
| 195 |
+
expected = DataFrame({"a": data_expected})
|
| 196 |
+
tm.assert_frame_equal(result, expected)
|
| 197 |
+
|
| 198 |
+
def test_combine_first_align_nan(self):
|
| 199 |
+
# GH 7509 (not fixed)
|
| 200 |
+
dfa = DataFrame([[pd.Timestamp("2011-01-01"), 2]], columns=["a", "b"])
|
| 201 |
+
dfb = DataFrame([[4], [5]], columns=["b"])
|
| 202 |
+
assert dfa["a"].dtype == "datetime64[ns]"
|
| 203 |
+
assert dfa["b"].dtype == "int64"
|
| 204 |
+
|
| 205 |
+
res = dfa.combine_first(dfb)
|
| 206 |
+
exp = DataFrame(
|
| 207 |
+
{"a": [pd.Timestamp("2011-01-01"), pd.NaT], "b": [2, 5]},
|
| 208 |
+
columns=["a", "b"],
|
| 209 |
+
)
|
| 210 |
+
tm.assert_frame_equal(res, exp)
|
| 211 |
+
assert res["a"].dtype == "datetime64[ns]"
|
| 212 |
+
# TODO: this must be int64
|
| 213 |
+
assert res["b"].dtype == "int64"
|
| 214 |
+
|
| 215 |
+
res = dfa.iloc[:0].combine_first(dfb)
|
| 216 |
+
exp = DataFrame({"a": [np.nan, np.nan], "b": [4, 5]}, columns=["a", "b"])
|
| 217 |
+
tm.assert_frame_equal(res, exp)
|
| 218 |
+
# TODO: this must be datetime64
|
| 219 |
+
assert res["a"].dtype == "float64"
|
| 220 |
+
# TODO: this must be int64
|
| 221 |
+
assert res["b"].dtype == "int64"
|
| 222 |
+
|
| 223 |
+
def test_combine_first_timezone(self, unit):
|
| 224 |
+
# see gh-7630
|
| 225 |
+
data1 = pd.to_datetime("20100101 01:01").tz_localize("UTC").as_unit(unit)
|
| 226 |
+
df1 = DataFrame(
|
| 227 |
+
columns=["UTCdatetime", "abc"],
|
| 228 |
+
data=data1,
|
| 229 |
+
index=pd.date_range("20140627", periods=1),
|
| 230 |
+
)
|
| 231 |
+
data2 = pd.to_datetime("20121212 12:12").tz_localize("UTC").as_unit(unit)
|
| 232 |
+
df2 = DataFrame(
|
| 233 |
+
columns=["UTCdatetime", "xyz"],
|
| 234 |
+
data=data2,
|
| 235 |
+
index=pd.date_range("20140628", periods=1),
|
| 236 |
+
)
|
| 237 |
+
res = df2[["UTCdatetime"]].combine_first(df1)
|
| 238 |
+
exp = DataFrame(
|
| 239 |
+
{
|
| 240 |
+
"UTCdatetime": [
|
| 241 |
+
pd.Timestamp("2010-01-01 01:01", tz="UTC"),
|
| 242 |
+
pd.Timestamp("2012-12-12 12:12", tz="UTC"),
|
| 243 |
+
],
|
| 244 |
+
"abc": [pd.Timestamp("2010-01-01 01:01:00", tz="UTC"), pd.NaT],
|
| 245 |
+
},
|
| 246 |
+
columns=["UTCdatetime", "abc"],
|
| 247 |
+
index=pd.date_range("20140627", periods=2, freq="D"),
|
| 248 |
+
dtype=f"datetime64[{unit}, UTC]",
|
| 249 |
+
)
|
| 250 |
+
assert res["UTCdatetime"].dtype == f"datetime64[{unit}, UTC]"
|
| 251 |
+
assert res["abc"].dtype == f"datetime64[{unit}, UTC]"
|
| 252 |
+
|
| 253 |
+
tm.assert_frame_equal(res, exp)
|
| 254 |
+
|
| 255 |
+
def test_combine_first_timezone2(self, unit):
|
| 256 |
+
# see gh-10567
|
| 257 |
+
dts1 = pd.date_range("2015-01-01", "2015-01-05", tz="UTC", unit=unit)
|
| 258 |
+
df1 = DataFrame({"DATE": dts1})
|
| 259 |
+
dts2 = pd.date_range("2015-01-03", "2015-01-05", tz="UTC", unit=unit)
|
| 260 |
+
df2 = DataFrame({"DATE": dts2})
|
| 261 |
+
|
| 262 |
+
res = df1.combine_first(df2)
|
| 263 |
+
tm.assert_frame_equal(res, df1)
|
| 264 |
+
assert res["DATE"].dtype == f"datetime64[{unit}, UTC]"
|
| 265 |
+
|
| 266 |
+
def test_combine_first_timezone3(self, unit):
|
| 267 |
+
dts1 = pd.DatetimeIndex(
|
| 268 |
+
["2011-01-01", "NaT", "2011-01-03", "2011-01-04"], tz="US/Eastern"
|
| 269 |
+
).as_unit(unit)
|
| 270 |
+
df1 = DataFrame({"DATE": dts1}, index=[1, 3, 5, 7])
|
| 271 |
+
dts2 = pd.DatetimeIndex(
|
| 272 |
+
["2012-01-01", "2012-01-02", "2012-01-03"], tz="US/Eastern"
|
| 273 |
+
).as_unit(unit)
|
| 274 |
+
df2 = DataFrame({"DATE": dts2}, index=[2, 4, 5])
|
| 275 |
+
|
| 276 |
+
res = df1.combine_first(df2)
|
| 277 |
+
exp_dts = pd.DatetimeIndex(
|
| 278 |
+
[
|
| 279 |
+
"2011-01-01",
|
| 280 |
+
"2012-01-01",
|
| 281 |
+
"NaT",
|
| 282 |
+
"2012-01-02",
|
| 283 |
+
"2011-01-03",
|
| 284 |
+
"2011-01-04",
|
| 285 |
+
],
|
| 286 |
+
tz="US/Eastern",
|
| 287 |
+
).as_unit(unit)
|
| 288 |
+
exp = DataFrame({"DATE": exp_dts}, index=[1, 2, 3, 4, 5, 7])
|
| 289 |
+
tm.assert_frame_equal(res, exp)
|
| 290 |
+
|
| 291 |
+
# FIXME: parametrizing over unit breaks on non-nano
|
| 292 |
+
def test_combine_first_timezone4(self):
|
| 293 |
+
# different tz
|
| 294 |
+
dts1 = pd.date_range("2015-01-01", "2015-01-05", tz="US/Eastern")
|
| 295 |
+
df1 = DataFrame({"DATE": dts1})
|
| 296 |
+
dts2 = pd.date_range("2015-01-03", "2015-01-05")
|
| 297 |
+
df2 = DataFrame({"DATE": dts2})
|
| 298 |
+
|
| 299 |
+
# if df1 doesn't have NaN, keep its dtype
|
| 300 |
+
res = df1.combine_first(df2)
|
| 301 |
+
tm.assert_frame_equal(res, df1)
|
| 302 |
+
assert res["DATE"].dtype == "datetime64[ns, US/Eastern]"
|
| 303 |
+
|
| 304 |
+
def test_combine_first_timezone5(self, unit):
|
| 305 |
+
dts1 = pd.date_range("2015-01-01", "2015-01-02", tz="US/Eastern", unit=unit)
|
| 306 |
+
df1 = DataFrame({"DATE": dts1})
|
| 307 |
+
dts2 = pd.date_range("2015-01-01", "2015-01-03", unit=unit)
|
| 308 |
+
df2 = DataFrame({"DATE": dts2})
|
| 309 |
+
|
| 310 |
+
res = df1.combine_first(df2)
|
| 311 |
+
exp_dts = [
|
| 312 |
+
pd.Timestamp("2015-01-01", tz="US/Eastern"),
|
| 313 |
+
pd.Timestamp("2015-01-02", tz="US/Eastern"),
|
| 314 |
+
pd.Timestamp("2015-01-03"),
|
| 315 |
+
]
|
| 316 |
+
exp = DataFrame({"DATE": exp_dts})
|
| 317 |
+
tm.assert_frame_equal(res, exp)
|
| 318 |
+
assert res["DATE"].dtype == "object"
|
| 319 |
+
|
| 320 |
+
def test_combine_first_timedelta(self):
|
| 321 |
+
data1 = pd.TimedeltaIndex(["1 day", "NaT", "3 day", "4day"])
|
| 322 |
+
df1 = DataFrame({"TD": data1}, index=[1, 3, 5, 7])
|
| 323 |
+
data2 = pd.TimedeltaIndex(["10 day", "11 day", "12 day"])
|
| 324 |
+
df2 = DataFrame({"TD": data2}, index=[2, 4, 5])
|
| 325 |
+
|
| 326 |
+
res = df1.combine_first(df2)
|
| 327 |
+
exp_dts = pd.TimedeltaIndex(
|
| 328 |
+
["1 day", "10 day", "NaT", "11 day", "3 day", "4 day"]
|
| 329 |
+
)
|
| 330 |
+
exp = DataFrame({"TD": exp_dts}, index=[1, 2, 3, 4, 5, 7])
|
| 331 |
+
tm.assert_frame_equal(res, exp)
|
| 332 |
+
assert res["TD"].dtype == "timedelta64[ns]"
|
| 333 |
+
|
| 334 |
+
def test_combine_first_period(self):
|
| 335 |
+
data1 = pd.PeriodIndex(["2011-01", "NaT", "2011-03", "2011-04"], freq="M")
|
| 336 |
+
df1 = DataFrame({"P": data1}, index=[1, 3, 5, 7])
|
| 337 |
+
data2 = pd.PeriodIndex(["2012-01-01", "2012-02", "2012-03"], freq="M")
|
| 338 |
+
df2 = DataFrame({"P": data2}, index=[2, 4, 5])
|
| 339 |
+
|
| 340 |
+
res = df1.combine_first(df2)
|
| 341 |
+
exp_dts = pd.PeriodIndex(
|
| 342 |
+
["2011-01", "2012-01", "NaT", "2012-02", "2011-03", "2011-04"], freq="M"
|
| 343 |
+
)
|
| 344 |
+
exp = DataFrame({"P": exp_dts}, index=[1, 2, 3, 4, 5, 7])
|
| 345 |
+
tm.assert_frame_equal(res, exp)
|
| 346 |
+
assert res["P"].dtype == data1.dtype
|
| 347 |
+
|
| 348 |
+
# different freq
|
| 349 |
+
dts2 = pd.PeriodIndex(["2012-01-01", "2012-01-02", "2012-01-03"], freq="D")
|
| 350 |
+
df2 = DataFrame({"P": dts2}, index=[2, 4, 5])
|
| 351 |
+
|
| 352 |
+
res = df1.combine_first(df2)
|
| 353 |
+
exp_dts = [
|
| 354 |
+
pd.Period("2011-01", freq="M"),
|
| 355 |
+
pd.Period("2012-01-01", freq="D"),
|
| 356 |
+
pd.NaT,
|
| 357 |
+
pd.Period("2012-01-02", freq="D"),
|
| 358 |
+
pd.Period("2011-03", freq="M"),
|
| 359 |
+
pd.Period("2011-04", freq="M"),
|
| 360 |
+
]
|
| 361 |
+
exp = DataFrame({"P": exp_dts}, index=[1, 2, 3, 4, 5, 7])
|
| 362 |
+
tm.assert_frame_equal(res, exp)
|
| 363 |
+
assert res["P"].dtype == "object"
|
| 364 |
+
|
| 365 |
+
def test_combine_first_int(self):
|
| 366 |
+
# GH14687 - integer series that do no align exactly
|
| 367 |
+
|
| 368 |
+
df1 = DataFrame({"a": [0, 1, 3, 5]}, dtype="int64")
|
| 369 |
+
df2 = DataFrame({"a": [1, 4]}, dtype="int64")
|
| 370 |
+
|
| 371 |
+
result_12 = df1.combine_first(df2)
|
| 372 |
+
expected_12 = DataFrame({"a": [0, 1, 3, 5]})
|
| 373 |
+
tm.assert_frame_equal(result_12, expected_12)
|
| 374 |
+
|
| 375 |
+
result_21 = df2.combine_first(df1)
|
| 376 |
+
expected_21 = DataFrame({"a": [1, 4, 3, 5]})
|
| 377 |
+
tm.assert_frame_equal(result_21, expected_21)
|
| 378 |
+
|
| 379 |
+
@pytest.mark.parametrize("val", [1, 1.0])
|
| 380 |
+
def test_combine_first_with_asymmetric_other(self, val):
|
| 381 |
+
# see gh-20699
|
| 382 |
+
df1 = DataFrame({"isNum": [val]})
|
| 383 |
+
df2 = DataFrame({"isBool": [True]})
|
| 384 |
+
|
| 385 |
+
res = df1.combine_first(df2)
|
| 386 |
+
exp = DataFrame({"isBool": [True], "isNum": [val]})
|
| 387 |
+
|
| 388 |
+
tm.assert_frame_equal(res, exp)
|
| 389 |
+
|
| 390 |
+
def test_combine_first_string_dtype_only_na(self, nullable_string_dtype):
|
| 391 |
+
# GH: 37519
|
| 392 |
+
df = DataFrame(
|
| 393 |
+
{"a": ["962", "85"], "b": [pd.NA] * 2}, dtype=nullable_string_dtype
|
| 394 |
+
)
|
| 395 |
+
df2 = DataFrame({"a": ["85"], "b": [pd.NA]}, dtype=nullable_string_dtype)
|
| 396 |
+
df.set_index(["a", "b"], inplace=True)
|
| 397 |
+
df2.set_index(["a", "b"], inplace=True)
|
| 398 |
+
result = df.combine_first(df2)
|
| 399 |
+
expected = DataFrame(
|
| 400 |
+
{"a": ["962", "85"], "b": [pd.NA] * 2}, dtype=nullable_string_dtype
|
| 401 |
+
).set_index(["a", "b"])
|
| 402 |
+
tm.assert_frame_equal(result, expected)
|
| 403 |
+
|
| 404 |
+
|
| 405 |
+
@pytest.mark.parametrize(
|
| 406 |
+
"scalar1, scalar2",
|
| 407 |
+
[
|
| 408 |
+
(datetime(2020, 1, 1), datetime(2020, 1, 2)),
|
| 409 |
+
(pd.Period("2020-01-01", "D"), pd.Period("2020-01-02", "D")),
|
| 410 |
+
(pd.Timedelta("89 days"), pd.Timedelta("60 min")),
|
| 411 |
+
(pd.Interval(left=0, right=1), pd.Interval(left=2, right=3, closed="left")),
|
| 412 |
+
],
|
| 413 |
+
)
|
| 414 |
+
def test_combine_first_timestamp_bug(scalar1, scalar2, nulls_fixture):
|
| 415 |
+
# GH28481
|
| 416 |
+
na_value = nulls_fixture
|
| 417 |
+
|
| 418 |
+
frame = DataFrame([[na_value, na_value]], columns=["a", "b"])
|
| 419 |
+
other = DataFrame([[scalar1, scalar2]], columns=["b", "c"])
|
| 420 |
+
|
| 421 |
+
common_dtype = find_common_type([frame.dtypes["b"], other.dtypes["b"]])
|
| 422 |
+
|
| 423 |
+
if is_dtype_equal(common_dtype, "object") or frame.dtypes["b"] == other.dtypes["b"]:
|
| 424 |
+
val = scalar1
|
| 425 |
+
else:
|
| 426 |
+
val = na_value
|
| 427 |
+
|
| 428 |
+
result = frame.combine_first(other)
|
| 429 |
+
|
| 430 |
+
expected = DataFrame([[na_value, val, scalar2]], columns=["a", "b", "c"])
|
| 431 |
+
|
| 432 |
+
expected["b"] = expected["b"].astype(common_dtype)
|
| 433 |
+
|
| 434 |
+
tm.assert_frame_equal(result, expected)
|
| 435 |
+
|
| 436 |
+
|
| 437 |
+
def test_combine_first_timestamp_bug_NaT():
|
| 438 |
+
# GH28481
|
| 439 |
+
frame = DataFrame([[pd.NaT, pd.NaT]], columns=["a", "b"])
|
| 440 |
+
other = DataFrame(
|
| 441 |
+
[[datetime(2020, 1, 1), datetime(2020, 1, 2)]], columns=["b", "c"]
|
| 442 |
+
)
|
| 443 |
+
|
| 444 |
+
result = frame.combine_first(other)
|
| 445 |
+
expected = DataFrame(
|
| 446 |
+
[[pd.NaT, datetime(2020, 1, 1), datetime(2020, 1, 2)]], columns=["a", "b", "c"]
|
| 447 |
+
)
|
| 448 |
+
|
| 449 |
+
tm.assert_frame_equal(result, expected)
|
| 450 |
+
|
| 451 |
+
|
| 452 |
+
def test_combine_first_with_nan_multiindex():
|
| 453 |
+
# gh-36562
|
| 454 |
+
|
| 455 |
+
mi1 = MultiIndex.from_arrays(
|
| 456 |
+
[["b", "b", "c", "a", "b", np.nan], [1, 2, 3, 4, 5, 6]], names=["a", "b"]
|
| 457 |
+
)
|
| 458 |
+
df = DataFrame({"c": [1, 1, 1, 1, 1, 1]}, index=mi1)
|
| 459 |
+
mi2 = MultiIndex.from_arrays(
|
| 460 |
+
[["a", "b", "c", "a", "b", "d"], [1, 1, 1, 1, 1, 1]], names=["a", "b"]
|
| 461 |
+
)
|
| 462 |
+
s = Series([1, 2, 3, 4, 5, 6], index=mi2)
|
| 463 |
+
res = df.combine_first(DataFrame({"d": s}))
|
| 464 |
+
mi_expected = MultiIndex.from_arrays(
|
| 465 |
+
[
|
| 466 |
+
["a", "a", "a", "b", "b", "b", "b", "c", "c", "d", np.nan],
|
| 467 |
+
[1, 1, 4, 1, 1, 2, 5, 1, 3, 1, 6],
|
| 468 |
+
],
|
| 469 |
+
names=["a", "b"],
|
| 470 |
+
)
|
| 471 |
+
expected = DataFrame(
|
| 472 |
+
{
|
| 473 |
+
"c": [np.nan, np.nan, 1, 1, 1, 1, 1, np.nan, 1, np.nan, 1],
|
| 474 |
+
"d": [1.0, 4.0, np.nan, 2.0, 5.0, np.nan, np.nan, 3.0, np.nan, 6.0, np.nan],
|
| 475 |
+
},
|
| 476 |
+
index=mi_expected,
|
| 477 |
+
)
|
| 478 |
+
tm.assert_frame_equal(res, expected)
|
| 479 |
+
|
| 480 |
+
|
| 481 |
+
def test_combine_preserve_dtypes():
|
| 482 |
+
# GH7509
|
| 483 |
+
a_column = Series(["a", "b"], index=range(2))
|
| 484 |
+
b_column = Series(range(2), index=range(2))
|
| 485 |
+
df1 = DataFrame({"A": a_column, "B": b_column})
|
| 486 |
+
|
| 487 |
+
c_column = Series(["a", "b"], index=range(5, 7))
|
| 488 |
+
b_column = Series(range(-1, 1), index=range(5, 7))
|
| 489 |
+
df2 = DataFrame({"B": b_column, "C": c_column})
|
| 490 |
+
|
| 491 |
+
expected = DataFrame(
|
| 492 |
+
{
|
| 493 |
+
"A": ["a", "b", np.nan, np.nan],
|
| 494 |
+
"B": [0, 1, -1, 0],
|
| 495 |
+
"C": [np.nan, np.nan, "a", "b"],
|
| 496 |
+
},
|
| 497 |
+
index=[0, 1, 5, 6],
|
| 498 |
+
)
|
| 499 |
+
combined = df1.combine_first(df2)
|
| 500 |
+
tm.assert_frame_equal(combined, expected)
|
| 501 |
+
|
| 502 |
+
|
| 503 |
+
def test_combine_first_duplicates_rows_for_nan_index_values():
|
| 504 |
+
# GH39881
|
| 505 |
+
df1 = DataFrame(
|
| 506 |
+
{"x": [9, 10, 11]},
|
| 507 |
+
index=MultiIndex.from_arrays([[1, 2, 3], [np.nan, 5, 6]], names=["a", "b"]),
|
| 508 |
+
)
|
| 509 |
+
|
| 510 |
+
df2 = DataFrame(
|
| 511 |
+
{"y": [12, 13, 14]},
|
| 512 |
+
index=MultiIndex.from_arrays([[1, 2, 4], [np.nan, 5, 7]], names=["a", "b"]),
|
| 513 |
+
)
|
| 514 |
+
|
| 515 |
+
expected = DataFrame(
|
| 516 |
+
{
|
| 517 |
+
"x": [9.0, 10.0, 11.0, np.nan],
|
| 518 |
+
"y": [12.0, 13.0, np.nan, 14.0],
|
| 519 |
+
},
|
| 520 |
+
index=MultiIndex.from_arrays(
|
| 521 |
+
[[1, 2, 3, 4], [np.nan, 5, 6, 7]], names=["a", "b"]
|
| 522 |
+
),
|
| 523 |
+
)
|
| 524 |
+
combined = df1.combine_first(df2)
|
| 525 |
+
tm.assert_frame_equal(combined, expected)
|
| 526 |
+
|
| 527 |
+
|
| 528 |
+
def test_combine_first_int64_not_cast_to_float64():
|
| 529 |
+
# GH 28613
|
| 530 |
+
df_1 = DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
|
| 531 |
+
df_2 = DataFrame({"A": [1, 20, 30], "B": [40, 50, 60], "C": [12, 34, 65]})
|
| 532 |
+
result = df_1.combine_first(df_2)
|
| 533 |
+
expected = DataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [12, 34, 65]})
|
| 534 |
+
tm.assert_frame_equal(result, expected)
|
| 535 |
+
|
| 536 |
+
|
| 537 |
+
def test_midx_losing_dtype():
|
| 538 |
+
# GH#49830
|
| 539 |
+
midx = MultiIndex.from_arrays([[0, 0], [np.nan, np.nan]])
|
| 540 |
+
midx2 = MultiIndex.from_arrays([[1, 1], [np.nan, np.nan]])
|
| 541 |
+
df1 = DataFrame({"a": [None, 4]}, index=midx)
|
| 542 |
+
df2 = DataFrame({"a": [3, 3]}, index=midx2)
|
| 543 |
+
result = df1.combine_first(df2)
|
| 544 |
+
expected_midx = MultiIndex.from_arrays(
|
| 545 |
+
[[0, 0, 1, 1], [np.nan, np.nan, np.nan, np.nan]]
|
| 546 |
+
)
|
| 547 |
+
expected = DataFrame({"a": [np.nan, 4, 3, 3]}, index=expected_midx)
|
| 548 |
+
tm.assert_frame_equal(result, expected)
|
| 549 |
+
|
| 550 |
+
|
| 551 |
+
def test_combine_first_empty_columns():
|
| 552 |
+
left = DataFrame(columns=["a", "b"])
|
| 553 |
+
right = DataFrame(columns=["a", "c"])
|
| 554 |
+
result = left.combine_first(right)
|
| 555 |
+
expected = DataFrame(columns=["a", "b", "c"])
|
| 556 |
+
tm.assert_frame_equal(result, expected)
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_droplevel.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
|
| 3 |
+
from pandas import (
|
| 4 |
+
DataFrame,
|
| 5 |
+
Index,
|
| 6 |
+
MultiIndex,
|
| 7 |
+
)
|
| 8 |
+
import pandas._testing as tm
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class TestDropLevel:
|
| 12 |
+
def test_droplevel(self, frame_or_series):
|
| 13 |
+
# GH#20342
|
| 14 |
+
cols = MultiIndex.from_tuples(
|
| 15 |
+
[("c", "e"), ("d", "f")], names=["level_1", "level_2"]
|
| 16 |
+
)
|
| 17 |
+
mi = MultiIndex.from_tuples([(1, 2), (5, 6), (9, 10)], names=["a", "b"])
|
| 18 |
+
df = DataFrame([[3, 4], [7, 8], [11, 12]], index=mi, columns=cols)
|
| 19 |
+
if frame_or_series is not DataFrame:
|
| 20 |
+
df = df.iloc[:, 0]
|
| 21 |
+
|
| 22 |
+
# test that dropping of a level in index works
|
| 23 |
+
expected = df.reset_index("a", drop=True)
|
| 24 |
+
result = df.droplevel("a", axis="index")
|
| 25 |
+
tm.assert_equal(result, expected)
|
| 26 |
+
|
| 27 |
+
if frame_or_series is DataFrame:
|
| 28 |
+
# test that dropping of a level in columns works
|
| 29 |
+
expected = df.copy()
|
| 30 |
+
expected.columns = Index(["c", "d"], name="level_1")
|
| 31 |
+
result = df.droplevel("level_2", axis="columns")
|
| 32 |
+
tm.assert_equal(result, expected)
|
| 33 |
+
else:
|
| 34 |
+
# test that droplevel raises ValueError on axis != 0
|
| 35 |
+
with pytest.raises(ValueError, match="No axis named columns"):
|
| 36 |
+
df.droplevel(1, axis="columns")
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_fillna.py
ADDED
|
@@ -0,0 +1,916 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pytest
|
| 3 |
+
|
| 4 |
+
from pandas.compat import WARNING_CHECK_DISABLED
|
| 5 |
+
import pandas.util._test_decorators as td
|
| 6 |
+
|
| 7 |
+
from pandas import (
|
| 8 |
+
Categorical,
|
| 9 |
+
DataFrame,
|
| 10 |
+
DatetimeIndex,
|
| 11 |
+
NaT,
|
| 12 |
+
PeriodIndex,
|
| 13 |
+
Series,
|
| 14 |
+
TimedeltaIndex,
|
| 15 |
+
Timestamp,
|
| 16 |
+
date_range,
|
| 17 |
+
to_datetime,
|
| 18 |
+
)
|
| 19 |
+
import pandas._testing as tm
|
| 20 |
+
from pandas.tests.frame.common import _check_mixed_float
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class TestFillNA:
|
| 24 |
+
def test_fillna_dict_inplace_nonunique_columns(
|
| 25 |
+
self, using_copy_on_write, warn_copy_on_write
|
| 26 |
+
):
|
| 27 |
+
df = DataFrame(
|
| 28 |
+
{"A": [np.nan] * 3, "B": [NaT, Timestamp(1), NaT], "C": [np.nan, "foo", 2]}
|
| 29 |
+
)
|
| 30 |
+
df.columns = ["A", "A", "A"]
|
| 31 |
+
orig = df[:]
|
| 32 |
+
|
| 33 |
+
# TODO(CoW-warn) better warning message
|
| 34 |
+
with tm.assert_cow_warning(warn_copy_on_write):
|
| 35 |
+
df.fillna({"A": 2}, inplace=True)
|
| 36 |
+
# The first and third columns can be set inplace, while the second cannot.
|
| 37 |
+
|
| 38 |
+
expected = DataFrame(
|
| 39 |
+
{"A": [2.0] * 3, "B": [2, Timestamp(1), 2], "C": [2, "foo", 2]}
|
| 40 |
+
)
|
| 41 |
+
expected.columns = ["A", "A", "A"]
|
| 42 |
+
tm.assert_frame_equal(df, expected)
|
| 43 |
+
|
| 44 |
+
# TODO: what's the expected/desired behavior with CoW?
|
| 45 |
+
if not using_copy_on_write:
|
| 46 |
+
assert tm.shares_memory(df.iloc[:, 0], orig.iloc[:, 0])
|
| 47 |
+
assert not tm.shares_memory(df.iloc[:, 1], orig.iloc[:, 1])
|
| 48 |
+
if not using_copy_on_write:
|
| 49 |
+
assert tm.shares_memory(df.iloc[:, 2], orig.iloc[:, 2])
|
| 50 |
+
|
| 51 |
+
@td.skip_array_manager_not_yet_implemented
|
| 52 |
+
def test_fillna_on_column_view(self, using_copy_on_write):
|
| 53 |
+
# GH#46149 avoid unnecessary copies
|
| 54 |
+
arr = np.full((40, 50), np.nan)
|
| 55 |
+
df = DataFrame(arr, copy=False)
|
| 56 |
+
|
| 57 |
+
if using_copy_on_write:
|
| 58 |
+
with tm.raises_chained_assignment_error():
|
| 59 |
+
df[0].fillna(-1, inplace=True)
|
| 60 |
+
assert np.isnan(arr[:, 0]).all()
|
| 61 |
+
else:
|
| 62 |
+
with tm.assert_produces_warning(
|
| 63 |
+
FutureWarning if not WARNING_CHECK_DISABLED else None,
|
| 64 |
+
match="inplace method",
|
| 65 |
+
):
|
| 66 |
+
df[0].fillna(-1, inplace=True)
|
| 67 |
+
assert (arr[:, 0] == -1).all()
|
| 68 |
+
|
| 69 |
+
# i.e. we didn't create a new 49-column block
|
| 70 |
+
assert len(df._mgr.arrays) == 1
|
| 71 |
+
assert np.shares_memory(df.values, arr)
|
| 72 |
+
|
| 73 |
+
def test_fillna_datetime(self, datetime_frame):
|
| 74 |
+
tf = datetime_frame
|
| 75 |
+
tf.loc[tf.index[:5], "A"] = np.nan
|
| 76 |
+
tf.loc[tf.index[-5:], "A"] = np.nan
|
| 77 |
+
|
| 78 |
+
zero_filled = datetime_frame.fillna(0)
|
| 79 |
+
assert (zero_filled.loc[zero_filled.index[:5], "A"] == 0).all()
|
| 80 |
+
|
| 81 |
+
msg = "DataFrame.fillna with 'method' is deprecated"
|
| 82 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 83 |
+
padded = datetime_frame.fillna(method="pad")
|
| 84 |
+
assert np.isnan(padded.loc[padded.index[:5], "A"]).all()
|
| 85 |
+
assert (
|
| 86 |
+
padded.loc[padded.index[-5:], "A"] == padded.loc[padded.index[-5], "A"]
|
| 87 |
+
).all()
|
| 88 |
+
|
| 89 |
+
msg = "Must specify a fill 'value' or 'method'"
|
| 90 |
+
with pytest.raises(ValueError, match=msg):
|
| 91 |
+
datetime_frame.fillna()
|
| 92 |
+
msg = "Cannot specify both 'value' and 'method'"
|
| 93 |
+
with pytest.raises(ValueError, match=msg):
|
| 94 |
+
datetime_frame.fillna(5, method="ffill")
|
| 95 |
+
|
| 96 |
+
def test_fillna_mixed_type(self, float_string_frame):
|
| 97 |
+
mf = float_string_frame
|
| 98 |
+
mf.loc[mf.index[5:20], "foo"] = np.nan
|
| 99 |
+
mf.loc[mf.index[-10:], "A"] = np.nan
|
| 100 |
+
# TODO: make stronger assertion here, GH 25640
|
| 101 |
+
mf.fillna(value=0)
|
| 102 |
+
msg = "DataFrame.fillna with 'method' is deprecated"
|
| 103 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 104 |
+
mf.fillna(method="pad")
|
| 105 |
+
|
| 106 |
+
def test_fillna_mixed_float(self, mixed_float_frame):
|
| 107 |
+
# mixed numeric (but no float16)
|
| 108 |
+
mf = mixed_float_frame.reindex(columns=["A", "B", "D"])
|
| 109 |
+
mf.loc[mf.index[-10:], "A"] = np.nan
|
| 110 |
+
result = mf.fillna(value=0)
|
| 111 |
+
_check_mixed_float(result, dtype={"C": None})
|
| 112 |
+
|
| 113 |
+
msg = "DataFrame.fillna with 'method' is deprecated"
|
| 114 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 115 |
+
result = mf.fillna(method="pad")
|
| 116 |
+
_check_mixed_float(result, dtype={"C": None})
|
| 117 |
+
|
| 118 |
+
def test_fillna_empty(self, using_copy_on_write):
|
| 119 |
+
if using_copy_on_write:
|
| 120 |
+
pytest.skip("condition is unnecessary complex and is deprecated anyway")
|
| 121 |
+
# empty frame (GH#2778)
|
| 122 |
+
df = DataFrame(columns=["x"])
|
| 123 |
+
for m in ["pad", "backfill"]:
|
| 124 |
+
msg = "Series.fillna with 'method' is deprecated"
|
| 125 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 126 |
+
df.x.fillna(method=m, inplace=True)
|
| 127 |
+
df.x.fillna(method=m)
|
| 128 |
+
|
| 129 |
+
def test_fillna_different_dtype(self):
|
| 130 |
+
# with different dtype (GH#3386)
|
| 131 |
+
df = DataFrame(
|
| 132 |
+
[["a", "a", np.nan, "a"], ["b", "b", np.nan, "b"], ["c", "c", np.nan, "c"]]
|
| 133 |
+
)
|
| 134 |
+
|
| 135 |
+
result = df.fillna({2: "foo"})
|
| 136 |
+
expected = DataFrame(
|
| 137 |
+
[["a", "a", "foo", "a"], ["b", "b", "foo", "b"], ["c", "c", "foo", "c"]]
|
| 138 |
+
)
|
| 139 |
+
# column is originally float (all-NaN) -> filling with string gives object dtype
|
| 140 |
+
expected[2] = expected[2].astype("object")
|
| 141 |
+
tm.assert_frame_equal(result, expected)
|
| 142 |
+
|
| 143 |
+
return_value = df.fillna({2: "foo"}, inplace=True)
|
| 144 |
+
tm.assert_frame_equal(df, expected)
|
| 145 |
+
assert return_value is None
|
| 146 |
+
|
| 147 |
+
def test_fillna_limit_and_value(self):
|
| 148 |
+
# limit and value
|
| 149 |
+
df = DataFrame(np.random.default_rng(2).standard_normal((10, 3)))
|
| 150 |
+
df.iloc[2:7, 0] = np.nan
|
| 151 |
+
df.iloc[3:5, 2] = np.nan
|
| 152 |
+
|
| 153 |
+
expected = df.copy()
|
| 154 |
+
expected.iloc[2, 0] = 999
|
| 155 |
+
expected.iloc[3, 2] = 999
|
| 156 |
+
result = df.fillna(999, limit=1)
|
| 157 |
+
tm.assert_frame_equal(result, expected)
|
| 158 |
+
|
| 159 |
+
def test_fillna_datelike(self):
|
| 160 |
+
# with datelike
|
| 161 |
+
# GH#6344
|
| 162 |
+
df = DataFrame(
|
| 163 |
+
{
|
| 164 |
+
"Date": [NaT, Timestamp("2014-1-1")],
|
| 165 |
+
"Date2": [Timestamp("2013-1-1"), NaT],
|
| 166 |
+
}
|
| 167 |
+
)
|
| 168 |
+
|
| 169 |
+
expected = df.copy()
|
| 170 |
+
expected["Date"] = expected["Date"].fillna(df.loc[df.index[0], "Date2"])
|
| 171 |
+
result = df.fillna(value={"Date": df["Date2"]})
|
| 172 |
+
tm.assert_frame_equal(result, expected)
|
| 173 |
+
|
| 174 |
+
def test_fillna_tzaware(self):
|
| 175 |
+
# with timezone
|
| 176 |
+
# GH#15855
|
| 177 |
+
df = DataFrame({"A": [Timestamp("2012-11-11 00:00:00+01:00"), NaT]})
|
| 178 |
+
exp = DataFrame(
|
| 179 |
+
{
|
| 180 |
+
"A": [
|
| 181 |
+
Timestamp("2012-11-11 00:00:00+01:00"),
|
| 182 |
+
Timestamp("2012-11-11 00:00:00+01:00"),
|
| 183 |
+
]
|
| 184 |
+
}
|
| 185 |
+
)
|
| 186 |
+
msg = "DataFrame.fillna with 'method' is deprecated"
|
| 187 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 188 |
+
res = df.fillna(method="pad")
|
| 189 |
+
tm.assert_frame_equal(res, exp)
|
| 190 |
+
|
| 191 |
+
df = DataFrame({"A": [NaT, Timestamp("2012-11-11 00:00:00+01:00")]})
|
| 192 |
+
exp = DataFrame(
|
| 193 |
+
{
|
| 194 |
+
"A": [
|
| 195 |
+
Timestamp("2012-11-11 00:00:00+01:00"),
|
| 196 |
+
Timestamp("2012-11-11 00:00:00+01:00"),
|
| 197 |
+
]
|
| 198 |
+
}
|
| 199 |
+
)
|
| 200 |
+
msg = "DataFrame.fillna with 'method' is deprecated"
|
| 201 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 202 |
+
res = df.fillna(method="bfill")
|
| 203 |
+
tm.assert_frame_equal(res, exp)
|
| 204 |
+
|
| 205 |
+
def test_fillna_tzaware_different_column(self):
|
| 206 |
+
# with timezone in another column
|
| 207 |
+
# GH#15522
|
| 208 |
+
df = DataFrame(
|
| 209 |
+
{
|
| 210 |
+
"A": date_range("20130101", periods=4, tz="US/Eastern"),
|
| 211 |
+
"B": [1, 2, np.nan, np.nan],
|
| 212 |
+
}
|
| 213 |
+
)
|
| 214 |
+
msg = "DataFrame.fillna with 'method' is deprecated"
|
| 215 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 216 |
+
result = df.fillna(method="pad")
|
| 217 |
+
expected = DataFrame(
|
| 218 |
+
{
|
| 219 |
+
"A": date_range("20130101", periods=4, tz="US/Eastern"),
|
| 220 |
+
"B": [1.0, 2.0, 2.0, 2.0],
|
| 221 |
+
}
|
| 222 |
+
)
|
| 223 |
+
tm.assert_frame_equal(result, expected)
|
| 224 |
+
|
| 225 |
+
def test_na_actions_categorical(self):
|
| 226 |
+
cat = Categorical([1, 2, 3, np.nan], categories=[1, 2, 3])
|
| 227 |
+
vals = ["a", "b", np.nan, "d"]
|
| 228 |
+
df = DataFrame({"cats": cat, "vals": vals})
|
| 229 |
+
cat2 = Categorical([1, 2, 3, 3], categories=[1, 2, 3])
|
| 230 |
+
vals2 = ["a", "b", "b", "d"]
|
| 231 |
+
df_exp_fill = DataFrame({"cats": cat2, "vals": vals2})
|
| 232 |
+
cat3 = Categorical([1, 2, 3], categories=[1, 2, 3])
|
| 233 |
+
vals3 = ["a", "b", np.nan]
|
| 234 |
+
df_exp_drop_cats = DataFrame({"cats": cat3, "vals": vals3})
|
| 235 |
+
cat4 = Categorical([1, 2], categories=[1, 2, 3])
|
| 236 |
+
vals4 = ["a", "b"]
|
| 237 |
+
df_exp_drop_all = DataFrame({"cats": cat4, "vals": vals4})
|
| 238 |
+
|
| 239 |
+
# fillna
|
| 240 |
+
res = df.fillna(value={"cats": 3, "vals": "b"})
|
| 241 |
+
tm.assert_frame_equal(res, df_exp_fill)
|
| 242 |
+
|
| 243 |
+
msg = "Cannot setitem on a Categorical with a new category"
|
| 244 |
+
with pytest.raises(TypeError, match=msg):
|
| 245 |
+
df.fillna(value={"cats": 4, "vals": "c"})
|
| 246 |
+
|
| 247 |
+
msg = "DataFrame.fillna with 'method' is deprecated"
|
| 248 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 249 |
+
res = df.fillna(method="pad")
|
| 250 |
+
tm.assert_frame_equal(res, df_exp_fill)
|
| 251 |
+
|
| 252 |
+
# dropna
|
| 253 |
+
res = df.dropna(subset=["cats"])
|
| 254 |
+
tm.assert_frame_equal(res, df_exp_drop_cats)
|
| 255 |
+
|
| 256 |
+
res = df.dropna()
|
| 257 |
+
tm.assert_frame_equal(res, df_exp_drop_all)
|
| 258 |
+
|
| 259 |
+
# make sure that fillna takes missing values into account
|
| 260 |
+
c = Categorical([np.nan, "b", np.nan], categories=["a", "b"])
|
| 261 |
+
df = DataFrame({"cats": c, "vals": [1, 2, 3]})
|
| 262 |
+
|
| 263 |
+
cat_exp = Categorical(["a", "b", "a"], categories=["a", "b"])
|
| 264 |
+
df_exp = DataFrame({"cats": cat_exp, "vals": [1, 2, 3]})
|
| 265 |
+
|
| 266 |
+
res = df.fillna("a")
|
| 267 |
+
tm.assert_frame_equal(res, df_exp)
|
| 268 |
+
|
| 269 |
+
def test_fillna_categorical_nan(self):
|
| 270 |
+
# GH#14021
|
| 271 |
+
# np.nan should always be a valid filler
|
| 272 |
+
cat = Categorical([np.nan, 2, np.nan])
|
| 273 |
+
val = Categorical([np.nan, np.nan, np.nan])
|
| 274 |
+
df = DataFrame({"cats": cat, "vals": val})
|
| 275 |
+
|
| 276 |
+
# GH#32950 df.median() is poorly behaved because there is no
|
| 277 |
+
# Categorical.median
|
| 278 |
+
median = Series({"cats": 2.0, "vals": np.nan})
|
| 279 |
+
|
| 280 |
+
res = df.fillna(median)
|
| 281 |
+
v_exp = [np.nan, np.nan, np.nan]
|
| 282 |
+
df_exp = DataFrame({"cats": [2, 2, 2], "vals": v_exp}, dtype="category")
|
| 283 |
+
tm.assert_frame_equal(res, df_exp)
|
| 284 |
+
|
| 285 |
+
result = df.cats.fillna(np.nan)
|
| 286 |
+
tm.assert_series_equal(result, df.cats)
|
| 287 |
+
|
| 288 |
+
result = df.vals.fillna(np.nan)
|
| 289 |
+
tm.assert_series_equal(result, df.vals)
|
| 290 |
+
|
| 291 |
+
idx = DatetimeIndex(
|
| 292 |
+
["2011-01-01 09:00", "2016-01-01 23:45", "2011-01-01 09:00", NaT, NaT]
|
| 293 |
+
)
|
| 294 |
+
df = DataFrame({"a": Categorical(idx)})
|
| 295 |
+
tm.assert_frame_equal(df.fillna(value=NaT), df)
|
| 296 |
+
|
| 297 |
+
idx = PeriodIndex(["2011-01", "2011-01", "2011-01", NaT, NaT], freq="M")
|
| 298 |
+
df = DataFrame({"a": Categorical(idx)})
|
| 299 |
+
tm.assert_frame_equal(df.fillna(value=NaT), df)
|
| 300 |
+
|
| 301 |
+
idx = TimedeltaIndex(["1 days", "2 days", "1 days", NaT, NaT])
|
| 302 |
+
df = DataFrame({"a": Categorical(idx)})
|
| 303 |
+
tm.assert_frame_equal(df.fillna(value=NaT), df)
|
| 304 |
+
|
| 305 |
+
def test_fillna_downcast(self):
|
| 306 |
+
# GH#15277
|
| 307 |
+
# infer int64 from float64
|
| 308 |
+
df = DataFrame({"a": [1.0, np.nan]})
|
| 309 |
+
msg = "The 'downcast' keyword in fillna is deprecated"
|
| 310 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 311 |
+
result = df.fillna(0, downcast="infer")
|
| 312 |
+
expected = DataFrame({"a": [1, 0]})
|
| 313 |
+
tm.assert_frame_equal(result, expected)
|
| 314 |
+
|
| 315 |
+
# infer int64 from float64 when fillna value is a dict
|
| 316 |
+
df = DataFrame({"a": [1.0, np.nan]})
|
| 317 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 318 |
+
result = df.fillna({"a": 0}, downcast="infer")
|
| 319 |
+
expected = DataFrame({"a": [1, 0]})
|
| 320 |
+
tm.assert_frame_equal(result, expected)
|
| 321 |
+
|
| 322 |
+
def test_fillna_downcast_false(self, frame_or_series):
|
| 323 |
+
# GH#45603 preserve object dtype with downcast=False
|
| 324 |
+
obj = frame_or_series([1, 2, 3], dtype="object")
|
| 325 |
+
msg = "The 'downcast' keyword in fillna"
|
| 326 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 327 |
+
result = obj.fillna("", downcast=False)
|
| 328 |
+
tm.assert_equal(result, obj)
|
| 329 |
+
|
| 330 |
+
def test_fillna_downcast_noop(self, frame_or_series):
|
| 331 |
+
# GH#45423
|
| 332 |
+
# Two relevant paths:
|
| 333 |
+
# 1) not _can_hold_na (e.g. integer)
|
| 334 |
+
# 2) _can_hold_na + noop + not can_hold_element
|
| 335 |
+
|
| 336 |
+
obj = frame_or_series([1, 2, 3], dtype=np.int64)
|
| 337 |
+
|
| 338 |
+
msg = "The 'downcast' keyword in fillna"
|
| 339 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 340 |
+
# GH#40988
|
| 341 |
+
res = obj.fillna("foo", downcast=np.dtype(np.int32))
|
| 342 |
+
expected = obj.astype(np.int32)
|
| 343 |
+
tm.assert_equal(res, expected)
|
| 344 |
+
|
| 345 |
+
obj2 = obj.astype(np.float64)
|
| 346 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 347 |
+
res2 = obj2.fillna("foo", downcast="infer")
|
| 348 |
+
expected2 = obj # get back int64
|
| 349 |
+
tm.assert_equal(res2, expected2)
|
| 350 |
+
|
| 351 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 352 |
+
# GH#40988
|
| 353 |
+
res3 = obj2.fillna("foo", downcast=np.dtype(np.int32))
|
| 354 |
+
tm.assert_equal(res3, expected)
|
| 355 |
+
|
| 356 |
+
@pytest.mark.parametrize("columns", [["A", "A", "B"], ["A", "A"]])
|
| 357 |
+
def test_fillna_dictlike_value_duplicate_colnames(self, columns):
|
| 358 |
+
# GH#43476
|
| 359 |
+
df = DataFrame(np.nan, index=[0, 1], columns=columns)
|
| 360 |
+
with tm.assert_produces_warning(None):
|
| 361 |
+
result = df.fillna({"A": 0})
|
| 362 |
+
|
| 363 |
+
expected = df.copy()
|
| 364 |
+
expected["A"] = 0.0
|
| 365 |
+
tm.assert_frame_equal(result, expected)
|
| 366 |
+
|
| 367 |
+
def test_fillna_dtype_conversion(self, using_infer_string):
|
| 368 |
+
# make sure that fillna on an empty frame works
|
| 369 |
+
df = DataFrame(index=["A", "B", "C"], columns=[1, 2, 3, 4, 5])
|
| 370 |
+
result = df.dtypes
|
| 371 |
+
expected = Series([np.dtype("object")] * 5, index=[1, 2, 3, 4, 5])
|
| 372 |
+
tm.assert_series_equal(result, expected)
|
| 373 |
+
|
| 374 |
+
msg = "Downcasting object dtype arrays"
|
| 375 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 376 |
+
result = df.fillna(1)
|
| 377 |
+
expected = DataFrame(1, index=["A", "B", "C"], columns=[1, 2, 3, 4, 5])
|
| 378 |
+
tm.assert_frame_equal(result, expected)
|
| 379 |
+
|
| 380 |
+
# empty block
|
| 381 |
+
df = DataFrame(index=range(3), columns=["A", "B"], dtype="float64")
|
| 382 |
+
result = df.fillna("nan")
|
| 383 |
+
expected = DataFrame("nan", index=range(3), columns=["A", "B"], dtype=object)
|
| 384 |
+
tm.assert_frame_equal(result, expected)
|
| 385 |
+
|
| 386 |
+
@pytest.mark.parametrize("val", ["", 1, np.nan, 1.0])
|
| 387 |
+
def test_fillna_dtype_conversion_equiv_replace(self, val):
|
| 388 |
+
df = DataFrame({"A": [1, np.nan], "B": [1.0, 2.0]})
|
| 389 |
+
expected = df.replace(np.nan, val)
|
| 390 |
+
result = df.fillna(val)
|
| 391 |
+
tm.assert_frame_equal(result, expected)
|
| 392 |
+
|
| 393 |
+
def test_fillna_datetime_columns(self):
|
| 394 |
+
# GH#7095
|
| 395 |
+
df = DataFrame(
|
| 396 |
+
{
|
| 397 |
+
"A": [-1, -2, np.nan],
|
| 398 |
+
"B": date_range("20130101", periods=3),
|
| 399 |
+
"C": ["foo", "bar", None],
|
| 400 |
+
"D": ["foo2", "bar2", None],
|
| 401 |
+
},
|
| 402 |
+
index=date_range("20130110", periods=3),
|
| 403 |
+
)
|
| 404 |
+
result = df.fillna("?")
|
| 405 |
+
expected = DataFrame(
|
| 406 |
+
{
|
| 407 |
+
"A": [-1, -2, "?"],
|
| 408 |
+
"B": date_range("20130101", periods=3),
|
| 409 |
+
"C": ["foo", "bar", "?"],
|
| 410 |
+
"D": ["foo2", "bar2", "?"],
|
| 411 |
+
},
|
| 412 |
+
index=date_range("20130110", periods=3),
|
| 413 |
+
)
|
| 414 |
+
tm.assert_frame_equal(result, expected)
|
| 415 |
+
|
| 416 |
+
df = DataFrame(
|
| 417 |
+
{
|
| 418 |
+
"A": [-1, -2, np.nan],
|
| 419 |
+
"B": [Timestamp("2013-01-01"), Timestamp("2013-01-02"), NaT],
|
| 420 |
+
"C": ["foo", "bar", None],
|
| 421 |
+
"D": ["foo2", "bar2", None],
|
| 422 |
+
},
|
| 423 |
+
index=date_range("20130110", periods=3),
|
| 424 |
+
)
|
| 425 |
+
result = df.fillna("?")
|
| 426 |
+
expected = DataFrame(
|
| 427 |
+
{
|
| 428 |
+
"A": [-1, -2, "?"],
|
| 429 |
+
"B": [Timestamp("2013-01-01"), Timestamp("2013-01-02"), "?"],
|
| 430 |
+
"C": ["foo", "bar", "?"],
|
| 431 |
+
"D": ["foo2", "bar2", "?"],
|
| 432 |
+
},
|
| 433 |
+
index=date_range("20130110", periods=3),
|
| 434 |
+
)
|
| 435 |
+
tm.assert_frame_equal(result, expected)
|
| 436 |
+
|
| 437 |
+
def test_ffill(self, datetime_frame):
|
| 438 |
+
datetime_frame.loc[datetime_frame.index[:5], "A"] = np.nan
|
| 439 |
+
datetime_frame.loc[datetime_frame.index[-5:], "A"] = np.nan
|
| 440 |
+
|
| 441 |
+
msg = "DataFrame.fillna with 'method' is deprecated"
|
| 442 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 443 |
+
alt = datetime_frame.fillna(method="ffill")
|
| 444 |
+
tm.assert_frame_equal(datetime_frame.ffill(), alt)
|
| 445 |
+
|
| 446 |
+
def test_bfill(self, datetime_frame):
|
| 447 |
+
datetime_frame.loc[datetime_frame.index[:5], "A"] = np.nan
|
| 448 |
+
datetime_frame.loc[datetime_frame.index[-5:], "A"] = np.nan
|
| 449 |
+
|
| 450 |
+
msg = "DataFrame.fillna with 'method' is deprecated"
|
| 451 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 452 |
+
alt = datetime_frame.fillna(method="bfill")
|
| 453 |
+
|
| 454 |
+
tm.assert_frame_equal(datetime_frame.bfill(), alt)
|
| 455 |
+
|
| 456 |
+
def test_frame_pad_backfill_limit(self):
|
| 457 |
+
index = np.arange(10)
|
| 458 |
+
df = DataFrame(np.random.default_rng(2).standard_normal((10, 4)), index=index)
|
| 459 |
+
|
| 460 |
+
result = df[:2].reindex(index, method="pad", limit=5)
|
| 461 |
+
|
| 462 |
+
msg = "DataFrame.fillna with 'method' is deprecated"
|
| 463 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 464 |
+
expected = df[:2].reindex(index).fillna(method="pad")
|
| 465 |
+
expected.iloc[-3:] = np.nan
|
| 466 |
+
tm.assert_frame_equal(result, expected)
|
| 467 |
+
|
| 468 |
+
result = df[-2:].reindex(index, method="backfill", limit=5)
|
| 469 |
+
|
| 470 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 471 |
+
expected = df[-2:].reindex(index).fillna(method="backfill")
|
| 472 |
+
expected.iloc[:3] = np.nan
|
| 473 |
+
tm.assert_frame_equal(result, expected)
|
| 474 |
+
|
| 475 |
+
def test_frame_fillna_limit(self):
|
| 476 |
+
index = np.arange(10)
|
| 477 |
+
df = DataFrame(np.random.default_rng(2).standard_normal((10, 4)), index=index)
|
| 478 |
+
|
| 479 |
+
result = df[:2].reindex(index)
|
| 480 |
+
msg = "DataFrame.fillna with 'method' is deprecated"
|
| 481 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 482 |
+
result = result.fillna(method="pad", limit=5)
|
| 483 |
+
|
| 484 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 485 |
+
expected = df[:2].reindex(index).fillna(method="pad")
|
| 486 |
+
expected.iloc[-3:] = np.nan
|
| 487 |
+
tm.assert_frame_equal(result, expected)
|
| 488 |
+
|
| 489 |
+
result = df[-2:].reindex(index)
|
| 490 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 491 |
+
result = result.fillna(method="backfill", limit=5)
|
| 492 |
+
|
| 493 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 494 |
+
expected = df[-2:].reindex(index).fillna(method="backfill")
|
| 495 |
+
expected.iloc[:3] = np.nan
|
| 496 |
+
tm.assert_frame_equal(result, expected)
|
| 497 |
+
|
| 498 |
+
def test_fillna_skip_certain_blocks(self):
|
| 499 |
+
# don't try to fill boolean, int blocks
|
| 500 |
+
|
| 501 |
+
df = DataFrame(np.random.default_rng(2).standard_normal((10, 4)).astype(int))
|
| 502 |
+
|
| 503 |
+
# it works!
|
| 504 |
+
df.fillna(np.nan)
|
| 505 |
+
|
| 506 |
+
@pytest.mark.parametrize("type", [int, float])
|
| 507 |
+
def test_fillna_positive_limit(self, type):
|
| 508 |
+
df = DataFrame(np.random.default_rng(2).standard_normal((10, 4))).astype(type)
|
| 509 |
+
|
| 510 |
+
msg = "Limit must be greater than 0"
|
| 511 |
+
with pytest.raises(ValueError, match=msg):
|
| 512 |
+
df.fillna(0, limit=-5)
|
| 513 |
+
|
| 514 |
+
@pytest.mark.parametrize("type", [int, float])
|
| 515 |
+
def test_fillna_integer_limit(self, type):
|
| 516 |
+
df = DataFrame(np.random.default_rng(2).standard_normal((10, 4))).astype(type)
|
| 517 |
+
|
| 518 |
+
msg = "Limit must be an integer"
|
| 519 |
+
with pytest.raises(ValueError, match=msg):
|
| 520 |
+
df.fillna(0, limit=0.5)
|
| 521 |
+
|
| 522 |
+
def test_fillna_inplace(self):
|
| 523 |
+
df = DataFrame(np.random.default_rng(2).standard_normal((10, 4)))
|
| 524 |
+
df.loc[:4, 1] = np.nan
|
| 525 |
+
df.loc[-4:, 3] = np.nan
|
| 526 |
+
|
| 527 |
+
expected = df.fillna(value=0)
|
| 528 |
+
assert expected is not df
|
| 529 |
+
|
| 530 |
+
df.fillna(value=0, inplace=True)
|
| 531 |
+
tm.assert_frame_equal(df, expected)
|
| 532 |
+
|
| 533 |
+
expected = df.fillna(value={0: 0}, inplace=True)
|
| 534 |
+
assert expected is None
|
| 535 |
+
|
| 536 |
+
df.loc[:4, 1] = np.nan
|
| 537 |
+
df.loc[-4:, 3] = np.nan
|
| 538 |
+
msg = "DataFrame.fillna with 'method' is deprecated"
|
| 539 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 540 |
+
expected = df.fillna(method="ffill")
|
| 541 |
+
assert expected is not df
|
| 542 |
+
|
| 543 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 544 |
+
df.fillna(method="ffill", inplace=True)
|
| 545 |
+
tm.assert_frame_equal(df, expected)
|
| 546 |
+
|
| 547 |
+
def test_fillna_dict_series(self):
|
| 548 |
+
df = DataFrame(
|
| 549 |
+
{
|
| 550 |
+
"a": [np.nan, 1, 2, np.nan, np.nan],
|
| 551 |
+
"b": [1, 2, 3, np.nan, np.nan],
|
| 552 |
+
"c": [np.nan, 1, 2, 3, 4],
|
| 553 |
+
}
|
| 554 |
+
)
|
| 555 |
+
|
| 556 |
+
result = df.fillna({"a": 0, "b": 5})
|
| 557 |
+
|
| 558 |
+
expected = df.copy()
|
| 559 |
+
expected["a"] = expected["a"].fillna(0)
|
| 560 |
+
expected["b"] = expected["b"].fillna(5)
|
| 561 |
+
tm.assert_frame_equal(result, expected)
|
| 562 |
+
|
| 563 |
+
# it works
|
| 564 |
+
result = df.fillna({"a": 0, "b": 5, "d": 7})
|
| 565 |
+
|
| 566 |
+
# Series treated same as dict
|
| 567 |
+
result = df.fillna(df.max())
|
| 568 |
+
expected = df.fillna(df.max().to_dict())
|
| 569 |
+
tm.assert_frame_equal(result, expected)
|
| 570 |
+
|
| 571 |
+
# disable this for now
|
| 572 |
+
with pytest.raises(NotImplementedError, match="column by column"):
|
| 573 |
+
df.fillna(df.max(1), axis=1)
|
| 574 |
+
|
| 575 |
+
def test_fillna_dataframe(self):
|
| 576 |
+
# GH#8377
|
| 577 |
+
df = DataFrame(
|
| 578 |
+
{
|
| 579 |
+
"a": [np.nan, 1, 2, np.nan, np.nan],
|
| 580 |
+
"b": [1, 2, 3, np.nan, np.nan],
|
| 581 |
+
"c": [np.nan, 1, 2, 3, 4],
|
| 582 |
+
},
|
| 583 |
+
index=list("VWXYZ"),
|
| 584 |
+
)
|
| 585 |
+
|
| 586 |
+
# df2 may have different index and columns
|
| 587 |
+
df2 = DataFrame(
|
| 588 |
+
{
|
| 589 |
+
"a": [np.nan, 10, 20, 30, 40],
|
| 590 |
+
"b": [50, 60, 70, 80, 90],
|
| 591 |
+
"foo": ["bar"] * 5,
|
| 592 |
+
},
|
| 593 |
+
index=list("VWXuZ"),
|
| 594 |
+
)
|
| 595 |
+
|
| 596 |
+
result = df.fillna(df2)
|
| 597 |
+
|
| 598 |
+
# only those columns and indices which are shared get filled
|
| 599 |
+
expected = DataFrame(
|
| 600 |
+
{
|
| 601 |
+
"a": [np.nan, 1, 2, np.nan, 40],
|
| 602 |
+
"b": [1, 2, 3, np.nan, 90],
|
| 603 |
+
"c": [np.nan, 1, 2, 3, 4],
|
| 604 |
+
},
|
| 605 |
+
index=list("VWXYZ"),
|
| 606 |
+
)
|
| 607 |
+
|
| 608 |
+
tm.assert_frame_equal(result, expected)
|
| 609 |
+
|
| 610 |
+
def test_fillna_columns(self):
|
| 611 |
+
arr = np.random.default_rng(2).standard_normal((10, 10))
|
| 612 |
+
arr[:, ::2] = np.nan
|
| 613 |
+
df = DataFrame(arr)
|
| 614 |
+
|
| 615 |
+
msg = "DataFrame.fillna with 'method' is deprecated"
|
| 616 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 617 |
+
result = df.fillna(method="ffill", axis=1)
|
| 618 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 619 |
+
expected = df.T.fillna(method="pad").T
|
| 620 |
+
tm.assert_frame_equal(result, expected)
|
| 621 |
+
|
| 622 |
+
df.insert(6, "foo", 5)
|
| 623 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 624 |
+
result = df.fillna(method="ffill", axis=1)
|
| 625 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 626 |
+
expected = df.astype(float).fillna(method="ffill", axis=1)
|
| 627 |
+
tm.assert_frame_equal(result, expected)
|
| 628 |
+
|
| 629 |
+
def test_fillna_invalid_method(self, float_frame):
|
| 630 |
+
with pytest.raises(ValueError, match="ffil"):
|
| 631 |
+
float_frame.fillna(method="ffil")
|
| 632 |
+
|
| 633 |
+
def test_fillna_invalid_value(self, float_frame):
|
| 634 |
+
# list
|
| 635 |
+
msg = '"value" parameter must be a scalar or dict, but you passed a "{}"'
|
| 636 |
+
with pytest.raises(TypeError, match=msg.format("list")):
|
| 637 |
+
float_frame.fillna([1, 2])
|
| 638 |
+
# tuple
|
| 639 |
+
with pytest.raises(TypeError, match=msg.format("tuple")):
|
| 640 |
+
float_frame.fillna((1, 2))
|
| 641 |
+
# frame with series
|
| 642 |
+
msg = (
|
| 643 |
+
'"value" parameter must be a scalar, dict or Series, but you '
|
| 644 |
+
'passed a "DataFrame"'
|
| 645 |
+
)
|
| 646 |
+
with pytest.raises(TypeError, match=msg):
|
| 647 |
+
float_frame.iloc[:, 0].fillna(float_frame)
|
| 648 |
+
|
| 649 |
+
def test_fillna_col_reordering(self):
|
| 650 |
+
cols = ["COL." + str(i) for i in range(5, 0, -1)]
|
| 651 |
+
data = np.random.default_rng(2).random((20, 5))
|
| 652 |
+
df = DataFrame(index=range(20), columns=cols, data=data)
|
| 653 |
+
msg = "DataFrame.fillna with 'method' is deprecated"
|
| 654 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 655 |
+
filled = df.fillna(method="ffill")
|
| 656 |
+
assert df.columns.tolist() == filled.columns.tolist()
|
| 657 |
+
|
| 658 |
+
def test_fill_empty(self, float_frame):
|
| 659 |
+
df = float_frame.reindex(columns=[])
|
| 660 |
+
result = df.fillna(value=0)
|
| 661 |
+
tm.assert_frame_equal(result, df)
|
| 662 |
+
|
| 663 |
+
def test_fillna_downcast_dict(self):
|
| 664 |
+
# GH#40809
|
| 665 |
+
df = DataFrame({"col1": [1, np.nan]})
|
| 666 |
+
|
| 667 |
+
msg = "The 'downcast' keyword in fillna"
|
| 668 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 669 |
+
result = df.fillna({"col1": 2}, downcast={"col1": "int64"})
|
| 670 |
+
expected = DataFrame({"col1": [1, 2]})
|
| 671 |
+
tm.assert_frame_equal(result, expected)
|
| 672 |
+
|
| 673 |
+
def test_fillna_with_columns_and_limit(self):
|
| 674 |
+
# GH40989
|
| 675 |
+
df = DataFrame(
|
| 676 |
+
[
|
| 677 |
+
[np.nan, 2, np.nan, 0],
|
| 678 |
+
[3, 4, np.nan, 1],
|
| 679 |
+
[np.nan, np.nan, np.nan, 5],
|
| 680 |
+
[np.nan, 3, np.nan, 4],
|
| 681 |
+
],
|
| 682 |
+
columns=list("ABCD"),
|
| 683 |
+
)
|
| 684 |
+
result = df.fillna(axis=1, value=100, limit=1)
|
| 685 |
+
result2 = df.fillna(axis=1, value=100, limit=2)
|
| 686 |
+
|
| 687 |
+
expected = DataFrame(
|
| 688 |
+
{
|
| 689 |
+
"A": Series([100, 3, 100, 100], dtype="float64"),
|
| 690 |
+
"B": [2, 4, np.nan, 3],
|
| 691 |
+
"C": [np.nan, 100, np.nan, np.nan],
|
| 692 |
+
"D": Series([0, 1, 5, 4], dtype="float64"),
|
| 693 |
+
},
|
| 694 |
+
index=[0, 1, 2, 3],
|
| 695 |
+
)
|
| 696 |
+
expected2 = DataFrame(
|
| 697 |
+
{
|
| 698 |
+
"A": Series([100, 3, 100, 100], dtype="float64"),
|
| 699 |
+
"B": Series([2, 4, 100, 3], dtype="float64"),
|
| 700 |
+
"C": [100, 100, np.nan, 100],
|
| 701 |
+
"D": Series([0, 1, 5, 4], dtype="float64"),
|
| 702 |
+
},
|
| 703 |
+
index=[0, 1, 2, 3],
|
| 704 |
+
)
|
| 705 |
+
|
| 706 |
+
tm.assert_frame_equal(result, expected)
|
| 707 |
+
tm.assert_frame_equal(result2, expected2)
|
| 708 |
+
|
| 709 |
+
def test_fillna_datetime_inplace(self):
|
| 710 |
+
# GH#48863
|
| 711 |
+
df = DataFrame(
|
| 712 |
+
{
|
| 713 |
+
"date1": to_datetime(["2018-05-30", None]),
|
| 714 |
+
"date2": to_datetime(["2018-09-30", None]),
|
| 715 |
+
}
|
| 716 |
+
)
|
| 717 |
+
expected = df.copy()
|
| 718 |
+
df.fillna(np.nan, inplace=True)
|
| 719 |
+
tm.assert_frame_equal(df, expected)
|
| 720 |
+
|
| 721 |
+
def test_fillna_inplace_with_columns_limit_and_value(self):
|
| 722 |
+
# GH40989
|
| 723 |
+
df = DataFrame(
|
| 724 |
+
[
|
| 725 |
+
[np.nan, 2, np.nan, 0],
|
| 726 |
+
[3, 4, np.nan, 1],
|
| 727 |
+
[np.nan, np.nan, np.nan, 5],
|
| 728 |
+
[np.nan, 3, np.nan, 4],
|
| 729 |
+
],
|
| 730 |
+
columns=list("ABCD"),
|
| 731 |
+
)
|
| 732 |
+
|
| 733 |
+
expected = df.fillna(axis=1, value=100, limit=1)
|
| 734 |
+
assert expected is not df
|
| 735 |
+
|
| 736 |
+
df.fillna(axis=1, value=100, limit=1, inplace=True)
|
| 737 |
+
tm.assert_frame_equal(df, expected)
|
| 738 |
+
|
| 739 |
+
@td.skip_array_manager_invalid_test
|
| 740 |
+
@pytest.mark.parametrize("val", [-1, {"x": -1, "y": -1}])
|
| 741 |
+
def test_inplace_dict_update_view(
|
| 742 |
+
self, val, using_copy_on_write, warn_copy_on_write
|
| 743 |
+
):
|
| 744 |
+
# GH#47188
|
| 745 |
+
df = DataFrame({"x": [np.nan, 2], "y": [np.nan, 2]})
|
| 746 |
+
df_orig = df.copy()
|
| 747 |
+
result_view = df[:]
|
| 748 |
+
with tm.assert_cow_warning(warn_copy_on_write):
|
| 749 |
+
df.fillna(val, inplace=True)
|
| 750 |
+
expected = DataFrame({"x": [-1, 2.0], "y": [-1.0, 2]})
|
| 751 |
+
tm.assert_frame_equal(df, expected)
|
| 752 |
+
if using_copy_on_write:
|
| 753 |
+
tm.assert_frame_equal(result_view, df_orig)
|
| 754 |
+
else:
|
| 755 |
+
tm.assert_frame_equal(result_view, expected)
|
| 756 |
+
|
| 757 |
+
def test_single_block_df_with_horizontal_axis(self):
|
| 758 |
+
# GH 47713
|
| 759 |
+
df = DataFrame(
|
| 760 |
+
{
|
| 761 |
+
"col1": [5, 0, np.nan, 10, np.nan],
|
| 762 |
+
"col2": [7, np.nan, np.nan, 5, 3],
|
| 763 |
+
"col3": [12, np.nan, 1, 2, 0],
|
| 764 |
+
"col4": [np.nan, 1, 1, np.nan, 18],
|
| 765 |
+
}
|
| 766 |
+
)
|
| 767 |
+
result = df.fillna(50, limit=1, axis=1)
|
| 768 |
+
expected = DataFrame(
|
| 769 |
+
[
|
| 770 |
+
[5.0, 7.0, 12.0, 50.0],
|
| 771 |
+
[0.0, 50.0, np.nan, 1.0],
|
| 772 |
+
[50.0, np.nan, 1.0, 1.0],
|
| 773 |
+
[10.0, 5.0, 2.0, 50.0],
|
| 774 |
+
[50.0, 3.0, 0.0, 18.0],
|
| 775 |
+
],
|
| 776 |
+
columns=["col1", "col2", "col3", "col4"],
|
| 777 |
+
)
|
| 778 |
+
tm.assert_frame_equal(result, expected)
|
| 779 |
+
|
| 780 |
+
def test_fillna_with_multi_index_frame(self):
|
| 781 |
+
# GH 47649
|
| 782 |
+
pdf = DataFrame(
|
| 783 |
+
{
|
| 784 |
+
("x", "a"): [np.nan, 2.0, 3.0],
|
| 785 |
+
("x", "b"): [1.0, 2.0, np.nan],
|
| 786 |
+
("y", "c"): [1.0, 2.0, np.nan],
|
| 787 |
+
}
|
| 788 |
+
)
|
| 789 |
+
expected = DataFrame(
|
| 790 |
+
{
|
| 791 |
+
("x", "a"): [-1.0, 2.0, 3.0],
|
| 792 |
+
("x", "b"): [1.0, 2.0, -1.0],
|
| 793 |
+
("y", "c"): [1.0, 2.0, np.nan],
|
| 794 |
+
}
|
| 795 |
+
)
|
| 796 |
+
tm.assert_frame_equal(pdf.fillna({"x": -1}), expected)
|
| 797 |
+
tm.assert_frame_equal(pdf.fillna({"x": -1, ("x", "b"): -2}), expected)
|
| 798 |
+
|
| 799 |
+
expected = DataFrame(
|
| 800 |
+
{
|
| 801 |
+
("x", "a"): [-1.0, 2.0, 3.0],
|
| 802 |
+
("x", "b"): [1.0, 2.0, -2.0],
|
| 803 |
+
("y", "c"): [1.0, 2.0, np.nan],
|
| 804 |
+
}
|
| 805 |
+
)
|
| 806 |
+
tm.assert_frame_equal(pdf.fillna({("x", "b"): -2, "x": -1}), expected)
|
| 807 |
+
|
| 808 |
+
|
| 809 |
+
def test_fillna_nonconsolidated_frame():
|
| 810 |
+
# https://github.com/pandas-dev/pandas/issues/36495
|
| 811 |
+
df = DataFrame(
|
| 812 |
+
[
|
| 813 |
+
[1, 1, 1, 1.0],
|
| 814 |
+
[2, 2, 2, 2.0],
|
| 815 |
+
[3, 3, 3, 3.0],
|
| 816 |
+
],
|
| 817 |
+
columns=["i1", "i2", "i3", "f1"],
|
| 818 |
+
)
|
| 819 |
+
df_nonconsol = df.pivot(index="i1", columns="i2")
|
| 820 |
+
result = df_nonconsol.fillna(0)
|
| 821 |
+
assert result.isna().sum().sum() == 0
|
| 822 |
+
|
| 823 |
+
|
| 824 |
+
def test_fillna_nones_inplace():
|
| 825 |
+
# GH 48480
|
| 826 |
+
df = DataFrame(
|
| 827 |
+
[[None, None], [None, None]],
|
| 828 |
+
columns=["A", "B"],
|
| 829 |
+
)
|
| 830 |
+
msg = "Downcasting object dtype arrays"
|
| 831 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 832 |
+
df.fillna(value={"A": 1, "B": 2}, inplace=True)
|
| 833 |
+
|
| 834 |
+
expected = DataFrame([[1, 2], [1, 2]], columns=["A", "B"])
|
| 835 |
+
tm.assert_frame_equal(df, expected)
|
| 836 |
+
|
| 837 |
+
|
| 838 |
+
@pytest.mark.parametrize("func", ["pad", "backfill"])
|
| 839 |
+
def test_pad_backfill_deprecated(func):
|
| 840 |
+
# GH#33396
|
| 841 |
+
df = DataFrame({"a": [1, 2, 3]})
|
| 842 |
+
with tm.assert_produces_warning(FutureWarning):
|
| 843 |
+
getattr(df, func)()
|
| 844 |
+
|
| 845 |
+
|
| 846 |
+
@pytest.mark.parametrize(
|
| 847 |
+
"data, expected_data, method, kwargs",
|
| 848 |
+
(
|
| 849 |
+
(
|
| 850 |
+
[np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan],
|
| 851 |
+
[np.nan, np.nan, 3.0, 3.0, 3.0, 3.0, 7.0, np.nan, np.nan],
|
| 852 |
+
"ffill",
|
| 853 |
+
{"limit_area": "inside"},
|
| 854 |
+
),
|
| 855 |
+
(
|
| 856 |
+
[np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan],
|
| 857 |
+
[np.nan, np.nan, 3.0, 3.0, np.nan, np.nan, 7.0, np.nan, np.nan],
|
| 858 |
+
"ffill",
|
| 859 |
+
{"limit_area": "inside", "limit": 1},
|
| 860 |
+
),
|
| 861 |
+
(
|
| 862 |
+
[np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan],
|
| 863 |
+
[np.nan, np.nan, 3.0, np.nan, np.nan, np.nan, 7.0, 7.0, 7.0],
|
| 864 |
+
"ffill",
|
| 865 |
+
{"limit_area": "outside"},
|
| 866 |
+
),
|
| 867 |
+
(
|
| 868 |
+
[np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan],
|
| 869 |
+
[np.nan, np.nan, 3.0, np.nan, np.nan, np.nan, 7.0, 7.0, np.nan],
|
| 870 |
+
"ffill",
|
| 871 |
+
{"limit_area": "outside", "limit": 1},
|
| 872 |
+
),
|
| 873 |
+
(
|
| 874 |
+
[np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan],
|
| 875 |
+
[np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan],
|
| 876 |
+
"ffill",
|
| 877 |
+
{"limit_area": "outside", "limit": 1},
|
| 878 |
+
),
|
| 879 |
+
(
|
| 880 |
+
range(5),
|
| 881 |
+
range(5),
|
| 882 |
+
"ffill",
|
| 883 |
+
{"limit_area": "outside", "limit": 1},
|
| 884 |
+
),
|
| 885 |
+
(
|
| 886 |
+
[np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan],
|
| 887 |
+
[np.nan, np.nan, 3.0, 7.0, 7.0, 7.0, 7.0, np.nan, np.nan],
|
| 888 |
+
"bfill",
|
| 889 |
+
{"limit_area": "inside"},
|
| 890 |
+
),
|
| 891 |
+
(
|
| 892 |
+
[np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan],
|
| 893 |
+
[np.nan, np.nan, 3.0, np.nan, np.nan, 7.0, 7.0, np.nan, np.nan],
|
| 894 |
+
"bfill",
|
| 895 |
+
{"limit_area": "inside", "limit": 1},
|
| 896 |
+
),
|
| 897 |
+
(
|
| 898 |
+
[np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan],
|
| 899 |
+
[3.0, 3.0, 3.0, np.nan, np.nan, np.nan, 7.0, np.nan, np.nan],
|
| 900 |
+
"bfill",
|
| 901 |
+
{"limit_area": "outside"},
|
| 902 |
+
),
|
| 903 |
+
(
|
| 904 |
+
[np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan],
|
| 905 |
+
[np.nan, 3.0, 3.0, np.nan, np.nan, np.nan, 7.0, np.nan, np.nan],
|
| 906 |
+
"bfill",
|
| 907 |
+
{"limit_area": "outside", "limit": 1},
|
| 908 |
+
),
|
| 909 |
+
),
|
| 910 |
+
)
|
| 911 |
+
def test_ffill_bfill_limit_area(data, expected_data, method, kwargs):
|
| 912 |
+
# GH#56492
|
| 913 |
+
df = DataFrame(data)
|
| 914 |
+
expected = DataFrame(expected_data)
|
| 915 |
+
result = getattr(df, method)(**kwargs)
|
| 916 |
+
tm.assert_frame_equal(result, expected)
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_filter.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pytest
|
| 3 |
+
|
| 4 |
+
import pandas as pd
|
| 5 |
+
from pandas import DataFrame
|
| 6 |
+
import pandas._testing as tm
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class TestDataFrameFilter:
|
| 10 |
+
def test_filter(self, float_frame, float_string_frame):
|
| 11 |
+
# Items
|
| 12 |
+
filtered = float_frame.filter(["A", "B", "E"])
|
| 13 |
+
assert len(filtered.columns) == 2
|
| 14 |
+
assert "E" not in filtered
|
| 15 |
+
|
| 16 |
+
filtered = float_frame.filter(["A", "B", "E"], axis="columns")
|
| 17 |
+
assert len(filtered.columns) == 2
|
| 18 |
+
assert "E" not in filtered
|
| 19 |
+
|
| 20 |
+
# Other axis
|
| 21 |
+
idx = float_frame.index[0:4]
|
| 22 |
+
filtered = float_frame.filter(idx, axis="index")
|
| 23 |
+
expected = float_frame.reindex(index=idx)
|
| 24 |
+
tm.assert_frame_equal(filtered, expected)
|
| 25 |
+
|
| 26 |
+
# like
|
| 27 |
+
fcopy = float_frame.copy()
|
| 28 |
+
fcopy["AA"] = 1
|
| 29 |
+
|
| 30 |
+
filtered = fcopy.filter(like="A")
|
| 31 |
+
assert len(filtered.columns) == 2
|
| 32 |
+
assert "AA" in filtered
|
| 33 |
+
|
| 34 |
+
# like with ints in column names
|
| 35 |
+
df = DataFrame(0.0, index=[0, 1, 2], columns=[0, 1, "_A", "_B"])
|
| 36 |
+
filtered = df.filter(like="_")
|
| 37 |
+
assert len(filtered.columns) == 2
|
| 38 |
+
|
| 39 |
+
# regex with ints in column names
|
| 40 |
+
# from PR #10384
|
| 41 |
+
df = DataFrame(0.0, index=[0, 1, 2], columns=["A1", 1, "B", 2, "C"])
|
| 42 |
+
expected = DataFrame(
|
| 43 |
+
0.0, index=[0, 1, 2], columns=pd.Index([1, 2], dtype=object)
|
| 44 |
+
)
|
| 45 |
+
filtered = df.filter(regex="^[0-9]+$")
|
| 46 |
+
tm.assert_frame_equal(filtered, expected)
|
| 47 |
+
|
| 48 |
+
expected = DataFrame(0.0, index=[0, 1, 2], columns=[0, "0", 1, "1"])
|
| 49 |
+
# shouldn't remove anything
|
| 50 |
+
filtered = expected.filter(regex="^[0-9]+$")
|
| 51 |
+
tm.assert_frame_equal(filtered, expected)
|
| 52 |
+
|
| 53 |
+
# pass in None
|
| 54 |
+
with pytest.raises(TypeError, match="Must pass"):
|
| 55 |
+
float_frame.filter()
|
| 56 |
+
with pytest.raises(TypeError, match="Must pass"):
|
| 57 |
+
float_frame.filter(items=None)
|
| 58 |
+
with pytest.raises(TypeError, match="Must pass"):
|
| 59 |
+
float_frame.filter(axis=1)
|
| 60 |
+
|
| 61 |
+
# test mutually exclusive arguments
|
| 62 |
+
with pytest.raises(TypeError, match="mutually exclusive"):
|
| 63 |
+
float_frame.filter(items=["one", "three"], regex="e$", like="bbi")
|
| 64 |
+
with pytest.raises(TypeError, match="mutually exclusive"):
|
| 65 |
+
float_frame.filter(items=["one", "three"], regex="e$", axis=1)
|
| 66 |
+
with pytest.raises(TypeError, match="mutually exclusive"):
|
| 67 |
+
float_frame.filter(items=["one", "three"], regex="e$")
|
| 68 |
+
with pytest.raises(TypeError, match="mutually exclusive"):
|
| 69 |
+
float_frame.filter(items=["one", "three"], like="bbi", axis=0)
|
| 70 |
+
with pytest.raises(TypeError, match="mutually exclusive"):
|
| 71 |
+
float_frame.filter(items=["one", "three"], like="bbi")
|
| 72 |
+
|
| 73 |
+
# objects
|
| 74 |
+
filtered = float_string_frame.filter(like="foo")
|
| 75 |
+
assert "foo" in filtered
|
| 76 |
+
|
| 77 |
+
# unicode columns, won't ascii-encode
|
| 78 |
+
df = float_frame.rename(columns={"B": "\u2202"})
|
| 79 |
+
filtered = df.filter(like="C")
|
| 80 |
+
assert "C" in filtered
|
| 81 |
+
|
| 82 |
+
def test_filter_regex_search(self, float_frame):
|
| 83 |
+
fcopy = float_frame.copy()
|
| 84 |
+
fcopy["AA"] = 1
|
| 85 |
+
|
| 86 |
+
# regex
|
| 87 |
+
filtered = fcopy.filter(regex="[A]+")
|
| 88 |
+
assert len(filtered.columns) == 2
|
| 89 |
+
assert "AA" in filtered
|
| 90 |
+
|
| 91 |
+
# doesn't have to be at beginning
|
| 92 |
+
df = DataFrame(
|
| 93 |
+
{"aBBa": [1, 2], "BBaBB": [1, 2], "aCCa": [1, 2], "aCCaBB": [1, 2]}
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
result = df.filter(regex="BB")
|
| 97 |
+
exp = df[[x for x in df.columns if "BB" in x]]
|
| 98 |
+
tm.assert_frame_equal(result, exp)
|
| 99 |
+
|
| 100 |
+
@pytest.mark.parametrize(
|
| 101 |
+
"name,expected",
|
| 102 |
+
[
|
| 103 |
+
("a", DataFrame({"a": [1, 2]})),
|
| 104 |
+
("a", DataFrame({"a": [1, 2]})),
|
| 105 |
+
("あ", DataFrame({"あ": [3, 4]})),
|
| 106 |
+
],
|
| 107 |
+
)
|
| 108 |
+
def test_filter_unicode(self, name, expected):
|
| 109 |
+
# GH13101
|
| 110 |
+
df = DataFrame({"a": [1, 2], "あ": [3, 4]})
|
| 111 |
+
|
| 112 |
+
tm.assert_frame_equal(df.filter(like=name), expected)
|
| 113 |
+
tm.assert_frame_equal(df.filter(regex=name), expected)
|
| 114 |
+
|
| 115 |
+
@pytest.mark.parametrize("name", ["a", "a"])
|
| 116 |
+
def test_filter_bytestring(self, name):
|
| 117 |
+
# GH13101
|
| 118 |
+
df = DataFrame({b"a": [1, 2], b"b": [3, 4]})
|
| 119 |
+
expected = DataFrame({b"a": [1, 2]})
|
| 120 |
+
|
| 121 |
+
tm.assert_frame_equal(df.filter(like=name), expected)
|
| 122 |
+
tm.assert_frame_equal(df.filter(regex=name), expected)
|
| 123 |
+
|
| 124 |
+
def test_filter_corner(self):
|
| 125 |
+
empty = DataFrame()
|
| 126 |
+
|
| 127 |
+
result = empty.filter([])
|
| 128 |
+
tm.assert_frame_equal(result, empty)
|
| 129 |
+
|
| 130 |
+
result = empty.filter(like="foo")
|
| 131 |
+
tm.assert_frame_equal(result, empty)
|
| 132 |
+
|
| 133 |
+
def test_filter_regex_non_string(self):
|
| 134 |
+
# GH#5798 trying to filter on non-string columns should drop,
|
| 135 |
+
# not raise
|
| 136 |
+
df = DataFrame(np.random.default_rng(2).random((3, 2)), columns=["STRING", 123])
|
| 137 |
+
result = df.filter(regex="STRING")
|
| 138 |
+
expected = df[["STRING"]]
|
| 139 |
+
tm.assert_frame_equal(result, expected)
|
| 140 |
+
|
| 141 |
+
def test_filter_keep_order(self):
|
| 142 |
+
# GH#54980
|
| 143 |
+
df = DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
|
| 144 |
+
result = df.filter(items=["B", "A"])
|
| 145 |
+
expected = df[["B", "A"]]
|
| 146 |
+
tm.assert_frame_equal(result, expected)
|
| 147 |
+
|
| 148 |
+
def test_filter_different_dtype(self):
|
| 149 |
+
# GH#54980
|
| 150 |
+
df = DataFrame({1: [1, 2, 3], 2: [4, 5, 6]})
|
| 151 |
+
result = df.filter(items=["B", "A"])
|
| 152 |
+
expected = df[[]]
|
| 153 |
+
tm.assert_frame_equal(result, expected)
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_iterrows.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pandas import (
|
| 2 |
+
DataFrame,
|
| 3 |
+
Timedelta,
|
| 4 |
+
)
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def test_no_overflow_of_freq_and_time_in_dataframe():
|
| 8 |
+
# GH 35665
|
| 9 |
+
df = DataFrame(
|
| 10 |
+
{
|
| 11 |
+
"some_string": ["2222Y3"],
|
| 12 |
+
"time": [Timedelta("0 days 00:00:00.990000")],
|
| 13 |
+
}
|
| 14 |
+
)
|
| 15 |
+
for _, row in df.iterrows():
|
| 16 |
+
assert row.dtype == "object"
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_join.py
ADDED
|
@@ -0,0 +1,576 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import datetime
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pytest
|
| 5 |
+
|
| 6 |
+
from pandas.errors import MergeError
|
| 7 |
+
|
| 8 |
+
import pandas as pd
|
| 9 |
+
from pandas import (
|
| 10 |
+
DataFrame,
|
| 11 |
+
Index,
|
| 12 |
+
MultiIndex,
|
| 13 |
+
date_range,
|
| 14 |
+
period_range,
|
| 15 |
+
)
|
| 16 |
+
import pandas._testing as tm
|
| 17 |
+
from pandas.core.reshape.concat import concat
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
@pytest.fixture
|
| 21 |
+
def frame_with_period_index():
|
| 22 |
+
return DataFrame(
|
| 23 |
+
data=np.arange(20).reshape(4, 5),
|
| 24 |
+
columns=list("abcde"),
|
| 25 |
+
index=period_range(start="2000", freq="Y", periods=4),
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@pytest.fixture
|
| 30 |
+
def left():
|
| 31 |
+
return DataFrame({"a": [20, 10, 0]}, index=[2, 1, 0])
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
@pytest.fixture
|
| 35 |
+
def right():
|
| 36 |
+
return DataFrame({"b": [300, 100, 200]}, index=[3, 1, 2])
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
@pytest.fixture
|
| 40 |
+
def left_no_dup():
|
| 41 |
+
return DataFrame(
|
| 42 |
+
{"a": ["a", "b", "c", "d"], "b": ["cat", "dog", "weasel", "horse"]},
|
| 43 |
+
index=range(4),
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
@pytest.fixture
|
| 48 |
+
def right_no_dup():
|
| 49 |
+
return DataFrame(
|
| 50 |
+
{
|
| 51 |
+
"a": ["a", "b", "c", "d", "e"],
|
| 52 |
+
"c": ["meow", "bark", "um... weasel noise?", "nay", "chirp"],
|
| 53 |
+
},
|
| 54 |
+
index=range(5),
|
| 55 |
+
).set_index("a")
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
@pytest.fixture
|
| 59 |
+
def left_w_dups(left_no_dup):
|
| 60 |
+
return concat(
|
| 61 |
+
[left_no_dup, DataFrame({"a": ["a"], "b": ["cow"]}, index=[3])], sort=True
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
@pytest.fixture
|
| 66 |
+
def right_w_dups(right_no_dup):
|
| 67 |
+
return concat(
|
| 68 |
+
[right_no_dup, DataFrame({"a": ["e"], "c": ["moo"]}, index=[3])]
|
| 69 |
+
).set_index("a")
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
@pytest.mark.parametrize(
|
| 73 |
+
"how, sort, expected",
|
| 74 |
+
[
|
| 75 |
+
("inner", False, DataFrame({"a": [20, 10], "b": [200, 100]}, index=[2, 1])),
|
| 76 |
+
("inner", True, DataFrame({"a": [10, 20], "b": [100, 200]}, index=[1, 2])),
|
| 77 |
+
(
|
| 78 |
+
"left",
|
| 79 |
+
False,
|
| 80 |
+
DataFrame({"a": [20, 10, 0], "b": [200, 100, np.nan]}, index=[2, 1, 0]),
|
| 81 |
+
),
|
| 82 |
+
(
|
| 83 |
+
"left",
|
| 84 |
+
True,
|
| 85 |
+
DataFrame({"a": [0, 10, 20], "b": [np.nan, 100, 200]}, index=[0, 1, 2]),
|
| 86 |
+
),
|
| 87 |
+
(
|
| 88 |
+
"right",
|
| 89 |
+
False,
|
| 90 |
+
DataFrame({"a": [np.nan, 10, 20], "b": [300, 100, 200]}, index=[3, 1, 2]),
|
| 91 |
+
),
|
| 92 |
+
(
|
| 93 |
+
"right",
|
| 94 |
+
True,
|
| 95 |
+
DataFrame({"a": [10, 20, np.nan], "b": [100, 200, 300]}, index=[1, 2, 3]),
|
| 96 |
+
),
|
| 97 |
+
(
|
| 98 |
+
"outer",
|
| 99 |
+
False,
|
| 100 |
+
DataFrame(
|
| 101 |
+
{"a": [0, 10, 20, np.nan], "b": [np.nan, 100, 200, 300]},
|
| 102 |
+
index=[0, 1, 2, 3],
|
| 103 |
+
),
|
| 104 |
+
),
|
| 105 |
+
(
|
| 106 |
+
"outer",
|
| 107 |
+
True,
|
| 108 |
+
DataFrame(
|
| 109 |
+
{"a": [0, 10, 20, np.nan], "b": [np.nan, 100, 200, 300]},
|
| 110 |
+
index=[0, 1, 2, 3],
|
| 111 |
+
),
|
| 112 |
+
),
|
| 113 |
+
],
|
| 114 |
+
)
|
| 115 |
+
def test_join(left, right, how, sort, expected):
|
| 116 |
+
result = left.join(right, how=how, sort=sort, validate="1:1")
|
| 117 |
+
tm.assert_frame_equal(result, expected)
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def test_suffix_on_list_join():
|
| 121 |
+
first = DataFrame({"key": [1, 2, 3, 4, 5]})
|
| 122 |
+
second = DataFrame({"key": [1, 8, 3, 2, 5], "v1": [1, 2, 3, 4, 5]})
|
| 123 |
+
third = DataFrame({"keys": [5, 2, 3, 4, 1], "v2": [1, 2, 3, 4, 5]})
|
| 124 |
+
|
| 125 |
+
# check proper errors are raised
|
| 126 |
+
msg = "Suffixes not supported when joining multiple DataFrames"
|
| 127 |
+
with pytest.raises(ValueError, match=msg):
|
| 128 |
+
first.join([second], lsuffix="y")
|
| 129 |
+
with pytest.raises(ValueError, match=msg):
|
| 130 |
+
first.join([second, third], rsuffix="x")
|
| 131 |
+
with pytest.raises(ValueError, match=msg):
|
| 132 |
+
first.join([second, third], lsuffix="y", rsuffix="x")
|
| 133 |
+
with pytest.raises(ValueError, match="Indexes have overlapping values"):
|
| 134 |
+
first.join([second, third])
|
| 135 |
+
|
| 136 |
+
# no errors should be raised
|
| 137 |
+
arr_joined = first.join([third])
|
| 138 |
+
norm_joined = first.join(third)
|
| 139 |
+
tm.assert_frame_equal(arr_joined, norm_joined)
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def test_join_invalid_validate(left_no_dup, right_no_dup):
|
| 143 |
+
# GH 46622
|
| 144 |
+
# Check invalid arguments
|
| 145 |
+
msg = (
|
| 146 |
+
'"invalid" is not a valid argument. '
|
| 147 |
+
"Valid arguments are:\n"
|
| 148 |
+
'- "1:1"\n'
|
| 149 |
+
'- "1:m"\n'
|
| 150 |
+
'- "m:1"\n'
|
| 151 |
+
'- "m:m"\n'
|
| 152 |
+
'- "one_to_one"\n'
|
| 153 |
+
'- "one_to_many"\n'
|
| 154 |
+
'- "many_to_one"\n'
|
| 155 |
+
'- "many_to_many"'
|
| 156 |
+
)
|
| 157 |
+
with pytest.raises(ValueError, match=msg):
|
| 158 |
+
left_no_dup.merge(right_no_dup, on="a", validate="invalid")
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
@pytest.mark.parametrize("dtype", ["object", "string[pyarrow]"])
|
| 162 |
+
def test_join_on_single_col_dup_on_right(left_no_dup, right_w_dups, dtype):
|
| 163 |
+
# GH 46622
|
| 164 |
+
# Dups on right allowed by one_to_many constraint
|
| 165 |
+
if dtype == "string[pyarrow]":
|
| 166 |
+
pytest.importorskip("pyarrow")
|
| 167 |
+
left_no_dup = left_no_dup.astype(dtype)
|
| 168 |
+
right_w_dups.index = right_w_dups.index.astype(dtype)
|
| 169 |
+
left_no_dup.join(
|
| 170 |
+
right_w_dups,
|
| 171 |
+
on="a",
|
| 172 |
+
validate="one_to_many",
|
| 173 |
+
)
|
| 174 |
+
|
| 175 |
+
# Dups on right not allowed by one_to_one constraint
|
| 176 |
+
msg = "Merge keys are not unique in right dataset; not a one-to-one merge"
|
| 177 |
+
with pytest.raises(MergeError, match=msg):
|
| 178 |
+
left_no_dup.join(
|
| 179 |
+
right_w_dups,
|
| 180 |
+
on="a",
|
| 181 |
+
validate="one_to_one",
|
| 182 |
+
)
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
def test_join_on_single_col_dup_on_left(left_w_dups, right_no_dup):
|
| 186 |
+
# GH 46622
|
| 187 |
+
# Dups on left allowed by many_to_one constraint
|
| 188 |
+
left_w_dups.join(
|
| 189 |
+
right_no_dup,
|
| 190 |
+
on="a",
|
| 191 |
+
validate="many_to_one",
|
| 192 |
+
)
|
| 193 |
+
|
| 194 |
+
# Dups on left not allowed by one_to_one constraint
|
| 195 |
+
msg = "Merge keys are not unique in left dataset; not a one-to-one merge"
|
| 196 |
+
with pytest.raises(MergeError, match=msg):
|
| 197 |
+
left_w_dups.join(
|
| 198 |
+
right_no_dup,
|
| 199 |
+
on="a",
|
| 200 |
+
validate="one_to_one",
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def test_join_on_single_col_dup_on_both(left_w_dups, right_w_dups):
|
| 205 |
+
# GH 46622
|
| 206 |
+
# Dups on both allowed by many_to_many constraint
|
| 207 |
+
left_w_dups.join(right_w_dups, on="a", validate="many_to_many")
|
| 208 |
+
|
| 209 |
+
# Dups on both not allowed by many_to_one constraint
|
| 210 |
+
msg = "Merge keys are not unique in right dataset; not a many-to-one merge"
|
| 211 |
+
with pytest.raises(MergeError, match=msg):
|
| 212 |
+
left_w_dups.join(
|
| 213 |
+
right_w_dups,
|
| 214 |
+
on="a",
|
| 215 |
+
validate="many_to_one",
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
# Dups on both not allowed by one_to_many constraint
|
| 219 |
+
msg = "Merge keys are not unique in left dataset; not a one-to-many merge"
|
| 220 |
+
with pytest.raises(MergeError, match=msg):
|
| 221 |
+
left_w_dups.join(
|
| 222 |
+
right_w_dups,
|
| 223 |
+
on="a",
|
| 224 |
+
validate="one_to_many",
|
| 225 |
+
)
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
def test_join_on_multi_col_check_dup():
|
| 229 |
+
# GH 46622
|
| 230 |
+
# Two column join, dups in both, but jointly no dups
|
| 231 |
+
left = DataFrame(
|
| 232 |
+
{
|
| 233 |
+
"a": ["a", "a", "b", "b"],
|
| 234 |
+
"b": [0, 1, 0, 1],
|
| 235 |
+
"c": ["cat", "dog", "weasel", "horse"],
|
| 236 |
+
},
|
| 237 |
+
index=range(4),
|
| 238 |
+
).set_index(["a", "b"])
|
| 239 |
+
|
| 240 |
+
right = DataFrame(
|
| 241 |
+
{
|
| 242 |
+
"a": ["a", "a", "b"],
|
| 243 |
+
"b": [0, 1, 0],
|
| 244 |
+
"d": ["meow", "bark", "um... weasel noise?"],
|
| 245 |
+
},
|
| 246 |
+
index=range(3),
|
| 247 |
+
).set_index(["a", "b"])
|
| 248 |
+
|
| 249 |
+
expected_multi = DataFrame(
|
| 250 |
+
{
|
| 251 |
+
"a": ["a", "a", "b"],
|
| 252 |
+
"b": [0, 1, 0],
|
| 253 |
+
"c": ["cat", "dog", "weasel"],
|
| 254 |
+
"d": ["meow", "bark", "um... weasel noise?"],
|
| 255 |
+
},
|
| 256 |
+
index=range(3),
|
| 257 |
+
).set_index(["a", "b"])
|
| 258 |
+
|
| 259 |
+
# Jointly no dups allowed by one_to_one constraint
|
| 260 |
+
result = left.join(right, how="inner", validate="1:1")
|
| 261 |
+
tm.assert_frame_equal(result, expected_multi)
|
| 262 |
+
|
| 263 |
+
|
| 264 |
+
def test_join_index(float_frame):
|
| 265 |
+
# left / right
|
| 266 |
+
|
| 267 |
+
f = float_frame.loc[float_frame.index[:10], ["A", "B"]]
|
| 268 |
+
f2 = float_frame.loc[float_frame.index[5:], ["C", "D"]].iloc[::-1]
|
| 269 |
+
|
| 270 |
+
joined = f.join(f2)
|
| 271 |
+
tm.assert_index_equal(f.index, joined.index)
|
| 272 |
+
expected_columns = Index(["A", "B", "C", "D"])
|
| 273 |
+
tm.assert_index_equal(joined.columns, expected_columns)
|
| 274 |
+
|
| 275 |
+
joined = f.join(f2, how="left")
|
| 276 |
+
tm.assert_index_equal(joined.index, f.index)
|
| 277 |
+
tm.assert_index_equal(joined.columns, expected_columns)
|
| 278 |
+
|
| 279 |
+
joined = f.join(f2, how="right")
|
| 280 |
+
tm.assert_index_equal(joined.index, f2.index)
|
| 281 |
+
tm.assert_index_equal(joined.columns, expected_columns)
|
| 282 |
+
|
| 283 |
+
# inner
|
| 284 |
+
|
| 285 |
+
joined = f.join(f2, how="inner")
|
| 286 |
+
tm.assert_index_equal(joined.index, f.index[5:10])
|
| 287 |
+
tm.assert_index_equal(joined.columns, expected_columns)
|
| 288 |
+
|
| 289 |
+
# outer
|
| 290 |
+
|
| 291 |
+
joined = f.join(f2, how="outer")
|
| 292 |
+
tm.assert_index_equal(joined.index, float_frame.index.sort_values())
|
| 293 |
+
tm.assert_index_equal(joined.columns, expected_columns)
|
| 294 |
+
|
| 295 |
+
with pytest.raises(ValueError, match="join method"):
|
| 296 |
+
f.join(f2, how="foo")
|
| 297 |
+
|
| 298 |
+
# corner case - overlapping columns
|
| 299 |
+
msg = "columns overlap but no suffix"
|
| 300 |
+
for how in ("outer", "left", "inner"):
|
| 301 |
+
with pytest.raises(ValueError, match=msg):
|
| 302 |
+
float_frame.join(float_frame, how=how)
|
| 303 |
+
|
| 304 |
+
|
| 305 |
+
def test_join_index_more(float_frame):
|
| 306 |
+
af = float_frame.loc[:, ["A", "B"]]
|
| 307 |
+
bf = float_frame.loc[::2, ["C", "D"]]
|
| 308 |
+
|
| 309 |
+
expected = af.copy()
|
| 310 |
+
expected["C"] = float_frame["C"][::2]
|
| 311 |
+
expected["D"] = float_frame["D"][::2]
|
| 312 |
+
|
| 313 |
+
result = af.join(bf)
|
| 314 |
+
tm.assert_frame_equal(result, expected)
|
| 315 |
+
|
| 316 |
+
result = af.join(bf, how="right")
|
| 317 |
+
tm.assert_frame_equal(result, expected[::2])
|
| 318 |
+
|
| 319 |
+
result = bf.join(af, how="right")
|
| 320 |
+
tm.assert_frame_equal(result, expected.loc[:, result.columns])
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
def test_join_index_series(float_frame):
|
| 324 |
+
df = float_frame.copy()
|
| 325 |
+
ser = df.pop(float_frame.columns[-1])
|
| 326 |
+
joined = df.join(ser)
|
| 327 |
+
|
| 328 |
+
tm.assert_frame_equal(joined, float_frame)
|
| 329 |
+
|
| 330 |
+
ser.name = None
|
| 331 |
+
with pytest.raises(ValueError, match="must have a name"):
|
| 332 |
+
df.join(ser)
|
| 333 |
+
|
| 334 |
+
|
| 335 |
+
def test_join_overlap(float_frame):
|
| 336 |
+
df1 = float_frame.loc[:, ["A", "B", "C"]]
|
| 337 |
+
df2 = float_frame.loc[:, ["B", "C", "D"]]
|
| 338 |
+
|
| 339 |
+
joined = df1.join(df2, lsuffix="_df1", rsuffix="_df2")
|
| 340 |
+
df1_suf = df1.loc[:, ["B", "C"]].add_suffix("_df1")
|
| 341 |
+
df2_suf = df2.loc[:, ["B", "C"]].add_suffix("_df2")
|
| 342 |
+
|
| 343 |
+
no_overlap = float_frame.loc[:, ["A", "D"]]
|
| 344 |
+
expected = df1_suf.join(df2_suf).join(no_overlap)
|
| 345 |
+
|
| 346 |
+
# column order not necessarily sorted
|
| 347 |
+
tm.assert_frame_equal(joined, expected.loc[:, joined.columns])
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
def test_join_period_index(frame_with_period_index):
|
| 351 |
+
other = frame_with_period_index.rename(columns=lambda key: f"{key}{key}")
|
| 352 |
+
|
| 353 |
+
joined_values = np.concatenate([frame_with_period_index.values] * 2, axis=1)
|
| 354 |
+
|
| 355 |
+
joined_cols = frame_with_period_index.columns.append(other.columns)
|
| 356 |
+
|
| 357 |
+
joined = frame_with_period_index.join(other)
|
| 358 |
+
expected = DataFrame(
|
| 359 |
+
data=joined_values, columns=joined_cols, index=frame_with_period_index.index
|
| 360 |
+
)
|
| 361 |
+
|
| 362 |
+
tm.assert_frame_equal(joined, expected)
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
def test_join_left_sequence_non_unique_index():
|
| 366 |
+
# https://github.com/pandas-dev/pandas/issues/19607
|
| 367 |
+
df1 = DataFrame({"a": [0, 10, 20]}, index=[1, 2, 3])
|
| 368 |
+
df2 = DataFrame({"b": [100, 200, 300]}, index=[4, 3, 2])
|
| 369 |
+
df3 = DataFrame({"c": [400, 500, 600]}, index=[2, 2, 4])
|
| 370 |
+
|
| 371 |
+
joined = df1.join([df2, df3], how="left")
|
| 372 |
+
|
| 373 |
+
expected = DataFrame(
|
| 374 |
+
{
|
| 375 |
+
"a": [0, 10, 10, 20],
|
| 376 |
+
"b": [np.nan, 300, 300, 200],
|
| 377 |
+
"c": [np.nan, 400, 500, np.nan],
|
| 378 |
+
},
|
| 379 |
+
index=[1, 2, 2, 3],
|
| 380 |
+
)
|
| 381 |
+
|
| 382 |
+
tm.assert_frame_equal(joined, expected)
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
def test_join_list_series(float_frame):
|
| 386 |
+
# GH#46850
|
| 387 |
+
# Join a DataFrame with a list containing both a Series and a DataFrame
|
| 388 |
+
left = float_frame.A.to_frame()
|
| 389 |
+
right = [float_frame.B, float_frame[["C", "D"]]]
|
| 390 |
+
result = left.join(right)
|
| 391 |
+
tm.assert_frame_equal(result, float_frame)
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
@pytest.mark.parametrize("sort_kw", [True, False])
|
| 395 |
+
def test_suppress_future_warning_with_sort_kw(sort_kw):
|
| 396 |
+
a = DataFrame({"col1": [1, 2]}, index=["c", "a"])
|
| 397 |
+
|
| 398 |
+
b = DataFrame({"col2": [4, 5]}, index=["b", "a"])
|
| 399 |
+
|
| 400 |
+
c = DataFrame({"col3": [7, 8]}, index=["a", "b"])
|
| 401 |
+
|
| 402 |
+
expected = DataFrame(
|
| 403 |
+
{
|
| 404 |
+
"col1": {"a": 2.0, "b": float("nan"), "c": 1.0},
|
| 405 |
+
"col2": {"a": 5.0, "b": 4.0, "c": float("nan")},
|
| 406 |
+
"col3": {"a": 7.0, "b": 8.0, "c": float("nan")},
|
| 407 |
+
}
|
| 408 |
+
)
|
| 409 |
+
if sort_kw is False:
|
| 410 |
+
expected = expected.reindex(index=["c", "a", "b"])
|
| 411 |
+
|
| 412 |
+
with tm.assert_produces_warning(None):
|
| 413 |
+
result = a.join([b, c], how="outer", sort=sort_kw)
|
| 414 |
+
tm.assert_frame_equal(result, expected)
|
| 415 |
+
|
| 416 |
+
|
| 417 |
+
class TestDataFrameJoin:
|
| 418 |
+
def test_join(self, multiindex_dataframe_random_data):
|
| 419 |
+
frame = multiindex_dataframe_random_data
|
| 420 |
+
|
| 421 |
+
a = frame.loc[frame.index[:5], ["A"]]
|
| 422 |
+
b = frame.loc[frame.index[2:], ["B", "C"]]
|
| 423 |
+
|
| 424 |
+
joined = a.join(b, how="outer").reindex(frame.index)
|
| 425 |
+
expected = frame.copy().values.copy()
|
| 426 |
+
expected[np.isnan(joined.values)] = np.nan
|
| 427 |
+
expected = DataFrame(expected, index=frame.index, columns=frame.columns)
|
| 428 |
+
|
| 429 |
+
assert not np.isnan(joined.values).all()
|
| 430 |
+
|
| 431 |
+
tm.assert_frame_equal(joined, expected)
|
| 432 |
+
|
| 433 |
+
def test_join_segfault(self):
|
| 434 |
+
# GH#1532
|
| 435 |
+
df1 = DataFrame({"a": [1, 1], "b": [1, 2], "x": [1, 2]})
|
| 436 |
+
df2 = DataFrame({"a": [2, 2], "b": [1, 2], "y": [1, 2]})
|
| 437 |
+
df1 = df1.set_index(["a", "b"])
|
| 438 |
+
df2 = df2.set_index(["a", "b"])
|
| 439 |
+
# it works!
|
| 440 |
+
for how in ["left", "right", "outer"]:
|
| 441 |
+
df1.join(df2, how=how)
|
| 442 |
+
|
| 443 |
+
def test_join_str_datetime(self):
|
| 444 |
+
str_dates = ["20120209", "20120222"]
|
| 445 |
+
dt_dates = [datetime(2012, 2, 9), datetime(2012, 2, 22)]
|
| 446 |
+
|
| 447 |
+
A = DataFrame(str_dates, index=range(2), columns=["aa"])
|
| 448 |
+
C = DataFrame([[1, 2], [3, 4]], index=str_dates, columns=dt_dates)
|
| 449 |
+
|
| 450 |
+
tst = A.join(C, on="aa")
|
| 451 |
+
|
| 452 |
+
assert len(tst.columns) == 3
|
| 453 |
+
|
| 454 |
+
def test_join_multiindex_leftright(self):
|
| 455 |
+
# GH 10741
|
| 456 |
+
df1 = DataFrame(
|
| 457 |
+
[
|
| 458 |
+
["a", "x", 0.471780],
|
| 459 |
+
["a", "y", 0.774908],
|
| 460 |
+
["a", "z", 0.563634],
|
| 461 |
+
["b", "x", -0.353756],
|
| 462 |
+
["b", "y", 0.368062],
|
| 463 |
+
["b", "z", -1.721840],
|
| 464 |
+
["c", "x", 1],
|
| 465 |
+
["c", "y", 2],
|
| 466 |
+
["c", "z", 3],
|
| 467 |
+
],
|
| 468 |
+
columns=["first", "second", "value1"],
|
| 469 |
+
).set_index(["first", "second"])
|
| 470 |
+
|
| 471 |
+
df2 = DataFrame([["a", 10], ["b", 20]], columns=["first", "value2"]).set_index(
|
| 472 |
+
["first"]
|
| 473 |
+
)
|
| 474 |
+
|
| 475 |
+
exp = DataFrame(
|
| 476 |
+
[
|
| 477 |
+
[0.471780, 10],
|
| 478 |
+
[0.774908, 10],
|
| 479 |
+
[0.563634, 10],
|
| 480 |
+
[-0.353756, 20],
|
| 481 |
+
[0.368062, 20],
|
| 482 |
+
[-1.721840, 20],
|
| 483 |
+
[1.000000, np.nan],
|
| 484 |
+
[2.000000, np.nan],
|
| 485 |
+
[3.000000, np.nan],
|
| 486 |
+
],
|
| 487 |
+
index=df1.index,
|
| 488 |
+
columns=["value1", "value2"],
|
| 489 |
+
)
|
| 490 |
+
|
| 491 |
+
# these must be the same results (but columns are flipped)
|
| 492 |
+
tm.assert_frame_equal(df1.join(df2, how="left"), exp)
|
| 493 |
+
tm.assert_frame_equal(df2.join(df1, how="right"), exp[["value2", "value1"]])
|
| 494 |
+
|
| 495 |
+
exp_idx = MultiIndex.from_product(
|
| 496 |
+
[["a", "b"], ["x", "y", "z"]], names=["first", "second"]
|
| 497 |
+
)
|
| 498 |
+
exp = DataFrame(
|
| 499 |
+
[
|
| 500 |
+
[0.471780, 10],
|
| 501 |
+
[0.774908, 10],
|
| 502 |
+
[0.563634, 10],
|
| 503 |
+
[-0.353756, 20],
|
| 504 |
+
[0.368062, 20],
|
| 505 |
+
[-1.721840, 20],
|
| 506 |
+
],
|
| 507 |
+
index=exp_idx,
|
| 508 |
+
columns=["value1", "value2"],
|
| 509 |
+
)
|
| 510 |
+
|
| 511 |
+
tm.assert_frame_equal(df1.join(df2, how="right"), exp)
|
| 512 |
+
tm.assert_frame_equal(df2.join(df1, how="left"), exp[["value2", "value1"]])
|
| 513 |
+
|
| 514 |
+
def test_join_multiindex_dates(self):
|
| 515 |
+
# GH 33692
|
| 516 |
+
date = pd.Timestamp(2000, 1, 1).date()
|
| 517 |
+
|
| 518 |
+
df1_index = MultiIndex.from_tuples([(0, date)], names=["index_0", "date"])
|
| 519 |
+
df1 = DataFrame({"col1": [0]}, index=df1_index)
|
| 520 |
+
df2_index = MultiIndex.from_tuples([(0, date)], names=["index_0", "date"])
|
| 521 |
+
df2 = DataFrame({"col2": [0]}, index=df2_index)
|
| 522 |
+
df3_index = MultiIndex.from_tuples([(0, date)], names=["index_0", "date"])
|
| 523 |
+
df3 = DataFrame({"col3": [0]}, index=df3_index)
|
| 524 |
+
|
| 525 |
+
result = df1.join([df2, df3])
|
| 526 |
+
|
| 527 |
+
expected_index = MultiIndex.from_tuples([(0, date)], names=["index_0", "date"])
|
| 528 |
+
expected = DataFrame(
|
| 529 |
+
{"col1": [0], "col2": [0], "col3": [0]}, index=expected_index
|
| 530 |
+
)
|
| 531 |
+
|
| 532 |
+
tm.assert_equal(result, expected)
|
| 533 |
+
|
| 534 |
+
def test_merge_join_different_levels_raises(self):
|
| 535 |
+
# GH#9455
|
| 536 |
+
# GH 40993: For raising, enforced in 2.0
|
| 537 |
+
|
| 538 |
+
# first dataframe
|
| 539 |
+
df1 = DataFrame(columns=["a", "b"], data=[[1, 11], [0, 22]])
|
| 540 |
+
|
| 541 |
+
# second dataframe
|
| 542 |
+
columns = MultiIndex.from_tuples([("a", ""), ("c", "c1")])
|
| 543 |
+
df2 = DataFrame(columns=columns, data=[[1, 33], [0, 44]])
|
| 544 |
+
|
| 545 |
+
# merge
|
| 546 |
+
with pytest.raises(
|
| 547 |
+
MergeError, match="Not allowed to merge between different levels"
|
| 548 |
+
):
|
| 549 |
+
pd.merge(df1, df2, on="a")
|
| 550 |
+
|
| 551 |
+
# join, see discussion in GH#12219
|
| 552 |
+
with pytest.raises(
|
| 553 |
+
MergeError, match="Not allowed to merge between different levels"
|
| 554 |
+
):
|
| 555 |
+
df1.join(df2, on="a")
|
| 556 |
+
|
| 557 |
+
def test_frame_join_tzaware(self):
|
| 558 |
+
test1 = DataFrame(
|
| 559 |
+
np.zeros((6, 3)),
|
| 560 |
+
index=date_range(
|
| 561 |
+
"2012-11-15 00:00:00", periods=6, freq="100ms", tz="US/Central"
|
| 562 |
+
),
|
| 563 |
+
)
|
| 564 |
+
test2 = DataFrame(
|
| 565 |
+
np.zeros((3, 3)),
|
| 566 |
+
index=date_range(
|
| 567 |
+
"2012-11-15 00:00:00", periods=3, freq="250ms", tz="US/Central"
|
| 568 |
+
),
|
| 569 |
+
columns=range(3, 6),
|
| 570 |
+
)
|
| 571 |
+
|
| 572 |
+
result = test1.join(test2, how="outer")
|
| 573 |
+
expected = test1.index.union(test2.index)
|
| 574 |
+
|
| 575 |
+
tm.assert_index_equal(result.index, expected)
|
| 576 |
+
assert result.index.tz.zone == "US/Central"
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_reindex.py
ADDED
|
@@ -0,0 +1,1327 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import (
|
| 2 |
+
datetime,
|
| 3 |
+
timedelta,
|
| 4 |
+
)
|
| 5 |
+
import inspect
|
| 6 |
+
|
| 7 |
+
import numpy as np
|
| 8 |
+
import pytest
|
| 9 |
+
|
| 10 |
+
from pandas._libs.tslibs.timezones import dateutil_gettz as gettz
|
| 11 |
+
from pandas.compat import (
|
| 12 |
+
IS64,
|
| 13 |
+
is_platform_windows,
|
| 14 |
+
)
|
| 15 |
+
from pandas.compat.numpy import np_version_gt2
|
| 16 |
+
import pandas.util._test_decorators as td
|
| 17 |
+
|
| 18 |
+
import pandas as pd
|
| 19 |
+
from pandas import (
|
| 20 |
+
Categorical,
|
| 21 |
+
CategoricalIndex,
|
| 22 |
+
DataFrame,
|
| 23 |
+
Index,
|
| 24 |
+
MultiIndex,
|
| 25 |
+
Series,
|
| 26 |
+
date_range,
|
| 27 |
+
isna,
|
| 28 |
+
)
|
| 29 |
+
import pandas._testing as tm
|
| 30 |
+
from pandas.api.types import CategoricalDtype
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class TestReindexSetIndex:
|
| 34 |
+
# Tests that check both reindex and set_index
|
| 35 |
+
|
| 36 |
+
def test_dti_set_index_reindex_datetimeindex(self):
|
| 37 |
+
# GH#6631
|
| 38 |
+
df = DataFrame(np.random.default_rng(2).random(6))
|
| 39 |
+
idx1 = date_range("2011/01/01", periods=6, freq="ME", tz="US/Eastern")
|
| 40 |
+
idx2 = date_range("2013", periods=6, freq="YE", tz="Asia/Tokyo")
|
| 41 |
+
|
| 42 |
+
df = df.set_index(idx1)
|
| 43 |
+
tm.assert_index_equal(df.index, idx1)
|
| 44 |
+
df = df.reindex(idx2)
|
| 45 |
+
tm.assert_index_equal(df.index, idx2)
|
| 46 |
+
|
| 47 |
+
def test_dti_set_index_reindex_freq_with_tz(self):
|
| 48 |
+
# GH#11314 with tz
|
| 49 |
+
index = date_range(
|
| 50 |
+
datetime(2015, 10, 1), datetime(2015, 10, 1, 23), freq="h", tz="US/Eastern"
|
| 51 |
+
)
|
| 52 |
+
df = DataFrame(
|
| 53 |
+
np.random.default_rng(2).standard_normal((24, 1)),
|
| 54 |
+
columns=["a"],
|
| 55 |
+
index=index,
|
| 56 |
+
)
|
| 57 |
+
new_index = date_range(
|
| 58 |
+
datetime(2015, 10, 2), datetime(2015, 10, 2, 23), freq="h", tz="US/Eastern"
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
result = df.set_index(new_index)
|
| 62 |
+
assert result.index.freq == index.freq
|
| 63 |
+
|
| 64 |
+
def test_set_reset_index_intervalindex(self):
|
| 65 |
+
df = DataFrame({"A": range(10)})
|
| 66 |
+
ser = pd.cut(df.A, 5)
|
| 67 |
+
df["B"] = ser
|
| 68 |
+
df = df.set_index("B")
|
| 69 |
+
|
| 70 |
+
df = df.reset_index()
|
| 71 |
+
|
| 72 |
+
def test_setitem_reset_index_dtypes(self):
|
| 73 |
+
# GH 22060
|
| 74 |
+
df = DataFrame(columns=["a", "b", "c"]).astype(
|
| 75 |
+
{"a": "datetime64[ns]", "b": np.int64, "c": np.float64}
|
| 76 |
+
)
|
| 77 |
+
df1 = df.set_index(["a"])
|
| 78 |
+
df1["d"] = []
|
| 79 |
+
result = df1.reset_index()
|
| 80 |
+
expected = DataFrame(columns=["a", "b", "c", "d"], index=range(0)).astype(
|
| 81 |
+
{"a": "datetime64[ns]", "b": np.int64, "c": np.float64, "d": np.float64}
|
| 82 |
+
)
|
| 83 |
+
tm.assert_frame_equal(result, expected)
|
| 84 |
+
|
| 85 |
+
df2 = df.set_index(["a", "b"])
|
| 86 |
+
df2["d"] = []
|
| 87 |
+
result = df2.reset_index()
|
| 88 |
+
tm.assert_frame_equal(result, expected)
|
| 89 |
+
|
| 90 |
+
@pytest.mark.parametrize(
|
| 91 |
+
"timezone, year, month, day, hour",
|
| 92 |
+
[["America/Chicago", 2013, 11, 3, 1], ["America/Santiago", 2021, 4, 3, 23]],
|
| 93 |
+
)
|
| 94 |
+
def test_reindex_timestamp_with_fold(self, timezone, year, month, day, hour):
|
| 95 |
+
# see gh-40817
|
| 96 |
+
test_timezone = gettz(timezone)
|
| 97 |
+
transition_1 = pd.Timestamp(
|
| 98 |
+
year=year,
|
| 99 |
+
month=month,
|
| 100 |
+
day=day,
|
| 101 |
+
hour=hour,
|
| 102 |
+
minute=0,
|
| 103 |
+
fold=0,
|
| 104 |
+
tzinfo=test_timezone,
|
| 105 |
+
)
|
| 106 |
+
transition_2 = pd.Timestamp(
|
| 107 |
+
year=year,
|
| 108 |
+
month=month,
|
| 109 |
+
day=day,
|
| 110 |
+
hour=hour,
|
| 111 |
+
minute=0,
|
| 112 |
+
fold=1,
|
| 113 |
+
tzinfo=test_timezone,
|
| 114 |
+
)
|
| 115 |
+
df = (
|
| 116 |
+
DataFrame({"index": [transition_1, transition_2], "vals": ["a", "b"]})
|
| 117 |
+
.set_index("index")
|
| 118 |
+
.reindex(["1", "2"])
|
| 119 |
+
)
|
| 120 |
+
exp = DataFrame({"index": ["1", "2"], "vals": [np.nan, np.nan]}).set_index(
|
| 121 |
+
"index"
|
| 122 |
+
)
|
| 123 |
+
exp = exp.astype(df.vals.dtype)
|
| 124 |
+
tm.assert_frame_equal(
|
| 125 |
+
df,
|
| 126 |
+
exp,
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
class TestDataFrameSelectReindex:
|
| 131 |
+
# These are specific reindex-based tests; other indexing tests should go in
|
| 132 |
+
# test_indexing
|
| 133 |
+
|
| 134 |
+
@pytest.mark.xfail(
|
| 135 |
+
not IS64 or (is_platform_windows() and not np_version_gt2),
|
| 136 |
+
reason="Passes int32 values to DatetimeArray in make_na_array on "
|
| 137 |
+
"windows, 32bit linux builds",
|
| 138 |
+
)
|
| 139 |
+
@td.skip_array_manager_not_yet_implemented
|
| 140 |
+
def test_reindex_tzaware_fill_value(self):
|
| 141 |
+
# GH#52586
|
| 142 |
+
df = DataFrame([[1]])
|
| 143 |
+
|
| 144 |
+
ts = pd.Timestamp("2023-04-10 17:32", tz="US/Pacific")
|
| 145 |
+
res = df.reindex([0, 1], axis=1, fill_value=ts)
|
| 146 |
+
assert res.dtypes[1] == pd.DatetimeTZDtype(unit="s", tz="US/Pacific")
|
| 147 |
+
expected = DataFrame({0: [1], 1: [ts]})
|
| 148 |
+
expected[1] = expected[1].astype(res.dtypes[1])
|
| 149 |
+
tm.assert_frame_equal(res, expected)
|
| 150 |
+
|
| 151 |
+
per = ts.tz_localize(None).to_period("s")
|
| 152 |
+
res = df.reindex([0, 1], axis=1, fill_value=per)
|
| 153 |
+
assert res.dtypes[1] == pd.PeriodDtype("s")
|
| 154 |
+
expected = DataFrame({0: [1], 1: [per]})
|
| 155 |
+
tm.assert_frame_equal(res, expected)
|
| 156 |
+
|
| 157 |
+
interval = pd.Interval(ts, ts + pd.Timedelta(seconds=1))
|
| 158 |
+
res = df.reindex([0, 1], axis=1, fill_value=interval)
|
| 159 |
+
assert res.dtypes[1] == pd.IntervalDtype("datetime64[s, US/Pacific]", "right")
|
| 160 |
+
expected = DataFrame({0: [1], 1: [interval]})
|
| 161 |
+
expected[1] = expected[1].astype(res.dtypes[1])
|
| 162 |
+
tm.assert_frame_equal(res, expected)
|
| 163 |
+
|
| 164 |
+
def test_reindex_copies(self):
|
| 165 |
+
# based on asv time_reindex_axis1
|
| 166 |
+
N = 10
|
| 167 |
+
df = DataFrame(np.random.default_rng(2).standard_normal((N * 10, N)))
|
| 168 |
+
cols = np.arange(N)
|
| 169 |
+
np.random.default_rng(2).shuffle(cols)
|
| 170 |
+
|
| 171 |
+
result = df.reindex(columns=cols, copy=True)
|
| 172 |
+
assert not np.shares_memory(result[0]._values, df[0]._values)
|
| 173 |
+
|
| 174 |
+
# pass both columns and index
|
| 175 |
+
result2 = df.reindex(columns=cols, index=df.index, copy=True)
|
| 176 |
+
assert not np.shares_memory(result2[0]._values, df[0]._values)
|
| 177 |
+
|
| 178 |
+
def test_reindex_copies_ea(self, using_copy_on_write):
|
| 179 |
+
# https://github.com/pandas-dev/pandas/pull/51197
|
| 180 |
+
# also ensure to honor copy keyword for ExtensionDtypes
|
| 181 |
+
N = 10
|
| 182 |
+
df = DataFrame(
|
| 183 |
+
np.random.default_rng(2).standard_normal((N * 10, N)), dtype="Float64"
|
| 184 |
+
)
|
| 185 |
+
cols = np.arange(N)
|
| 186 |
+
np.random.default_rng(2).shuffle(cols)
|
| 187 |
+
|
| 188 |
+
result = df.reindex(columns=cols, copy=True)
|
| 189 |
+
if using_copy_on_write:
|
| 190 |
+
assert np.shares_memory(result[0].array._data, df[0].array._data)
|
| 191 |
+
else:
|
| 192 |
+
assert not np.shares_memory(result[0].array._data, df[0].array._data)
|
| 193 |
+
|
| 194 |
+
# pass both columns and index
|
| 195 |
+
result2 = df.reindex(columns=cols, index=df.index, copy=True)
|
| 196 |
+
if using_copy_on_write:
|
| 197 |
+
assert np.shares_memory(result2[0].array._data, df[0].array._data)
|
| 198 |
+
else:
|
| 199 |
+
assert not np.shares_memory(result2[0].array._data, df[0].array._data)
|
| 200 |
+
|
| 201 |
+
@td.skip_array_manager_not_yet_implemented
|
| 202 |
+
def test_reindex_date_fill_value(self):
|
| 203 |
+
# passing date to dt64 is deprecated; enforced in 2.0 to cast to object
|
| 204 |
+
arr = date_range("2016-01-01", periods=6).values.reshape(3, 2)
|
| 205 |
+
df = DataFrame(arr, columns=["A", "B"], index=range(3))
|
| 206 |
+
|
| 207 |
+
ts = df.iloc[0, 0]
|
| 208 |
+
fv = ts.date()
|
| 209 |
+
|
| 210 |
+
res = df.reindex(index=range(4), columns=["A", "B", "C"], fill_value=fv)
|
| 211 |
+
|
| 212 |
+
expected = DataFrame(
|
| 213 |
+
{"A": df["A"].tolist() + [fv], "B": df["B"].tolist() + [fv], "C": [fv] * 4},
|
| 214 |
+
dtype=object,
|
| 215 |
+
)
|
| 216 |
+
tm.assert_frame_equal(res, expected)
|
| 217 |
+
|
| 218 |
+
# only reindexing rows
|
| 219 |
+
res = df.reindex(index=range(4), fill_value=fv)
|
| 220 |
+
tm.assert_frame_equal(res, expected[["A", "B"]])
|
| 221 |
+
|
| 222 |
+
# same with a datetime-castable str
|
| 223 |
+
res = df.reindex(
|
| 224 |
+
index=range(4), columns=["A", "B", "C"], fill_value="2016-01-01"
|
| 225 |
+
)
|
| 226 |
+
expected = DataFrame(
|
| 227 |
+
{"A": df["A"].tolist() + [ts], "B": df["B"].tolist() + [ts], "C": [ts] * 4},
|
| 228 |
+
)
|
| 229 |
+
tm.assert_frame_equal(res, expected)
|
| 230 |
+
|
| 231 |
+
def test_reindex_with_multi_index(self):
|
| 232 |
+
# https://github.com/pandas-dev/pandas/issues/29896
|
| 233 |
+
# tests for reindexing a multi-indexed DataFrame with a new MultiIndex
|
| 234 |
+
#
|
| 235 |
+
# confirms that we can reindex a multi-indexed DataFrame with a new
|
| 236 |
+
# MultiIndex object correctly when using no filling, backfilling, and
|
| 237 |
+
# padding
|
| 238 |
+
#
|
| 239 |
+
# The DataFrame, `df`, used in this test is:
|
| 240 |
+
# c
|
| 241 |
+
# a b
|
| 242 |
+
# -1 0 A
|
| 243 |
+
# 1 B
|
| 244 |
+
# 2 C
|
| 245 |
+
# 3 D
|
| 246 |
+
# 4 E
|
| 247 |
+
# 5 F
|
| 248 |
+
# 6 G
|
| 249 |
+
# 0 0 A
|
| 250 |
+
# 1 B
|
| 251 |
+
# 2 C
|
| 252 |
+
# 3 D
|
| 253 |
+
# 4 E
|
| 254 |
+
# 5 F
|
| 255 |
+
# 6 G
|
| 256 |
+
# 1 0 A
|
| 257 |
+
# 1 B
|
| 258 |
+
# 2 C
|
| 259 |
+
# 3 D
|
| 260 |
+
# 4 E
|
| 261 |
+
# 5 F
|
| 262 |
+
# 6 G
|
| 263 |
+
#
|
| 264 |
+
# and the other MultiIndex, `new_multi_index`, is:
|
| 265 |
+
# 0: 0 0.5
|
| 266 |
+
# 1: 2.0
|
| 267 |
+
# 2: 5.0
|
| 268 |
+
# 3: 5.8
|
| 269 |
+
df = DataFrame(
|
| 270 |
+
{
|
| 271 |
+
"a": [-1] * 7 + [0] * 7 + [1] * 7,
|
| 272 |
+
"b": list(range(7)) * 3,
|
| 273 |
+
"c": ["A", "B", "C", "D", "E", "F", "G"] * 3,
|
| 274 |
+
}
|
| 275 |
+
).set_index(["a", "b"])
|
| 276 |
+
new_index = [0.5, 2.0, 5.0, 5.8]
|
| 277 |
+
new_multi_index = MultiIndex.from_product([[0], new_index], names=["a", "b"])
|
| 278 |
+
|
| 279 |
+
# reindexing w/o a `method` value
|
| 280 |
+
reindexed = df.reindex(new_multi_index)
|
| 281 |
+
expected = DataFrame(
|
| 282 |
+
{"a": [0] * 4, "b": new_index, "c": [np.nan, "C", "F", np.nan]}
|
| 283 |
+
).set_index(["a", "b"])
|
| 284 |
+
tm.assert_frame_equal(expected, reindexed)
|
| 285 |
+
|
| 286 |
+
# reindexing with backfilling
|
| 287 |
+
expected = DataFrame(
|
| 288 |
+
{"a": [0] * 4, "b": new_index, "c": ["B", "C", "F", "G"]}
|
| 289 |
+
).set_index(["a", "b"])
|
| 290 |
+
reindexed_with_backfilling = df.reindex(new_multi_index, method="bfill")
|
| 291 |
+
tm.assert_frame_equal(expected, reindexed_with_backfilling)
|
| 292 |
+
|
| 293 |
+
reindexed_with_backfilling = df.reindex(new_multi_index, method="backfill")
|
| 294 |
+
tm.assert_frame_equal(expected, reindexed_with_backfilling)
|
| 295 |
+
|
| 296 |
+
# reindexing with padding
|
| 297 |
+
expected = DataFrame(
|
| 298 |
+
{"a": [0] * 4, "b": new_index, "c": ["A", "C", "F", "F"]}
|
| 299 |
+
).set_index(["a", "b"])
|
| 300 |
+
reindexed_with_padding = df.reindex(new_multi_index, method="pad")
|
| 301 |
+
tm.assert_frame_equal(expected, reindexed_with_padding)
|
| 302 |
+
|
| 303 |
+
reindexed_with_padding = df.reindex(new_multi_index, method="ffill")
|
| 304 |
+
tm.assert_frame_equal(expected, reindexed_with_padding)
|
| 305 |
+
|
| 306 |
+
@pytest.mark.parametrize(
|
| 307 |
+
"method,expected_values",
|
| 308 |
+
[
|
| 309 |
+
("nearest", [0, 1, 1, 2]),
|
| 310 |
+
("pad", [np.nan, 0, 1, 1]),
|
| 311 |
+
("backfill", [0, 1, 2, 2]),
|
| 312 |
+
],
|
| 313 |
+
)
|
| 314 |
+
def test_reindex_methods(self, method, expected_values):
|
| 315 |
+
df = DataFrame({"x": list(range(5))})
|
| 316 |
+
target = np.array([-0.1, 0.9, 1.1, 1.5])
|
| 317 |
+
|
| 318 |
+
expected = DataFrame({"x": expected_values}, index=target)
|
| 319 |
+
actual = df.reindex(target, method=method)
|
| 320 |
+
tm.assert_frame_equal(expected, actual)
|
| 321 |
+
|
| 322 |
+
actual = df.reindex(target, method=method, tolerance=1)
|
| 323 |
+
tm.assert_frame_equal(expected, actual)
|
| 324 |
+
actual = df.reindex(target, method=method, tolerance=[1, 1, 1, 1])
|
| 325 |
+
tm.assert_frame_equal(expected, actual)
|
| 326 |
+
|
| 327 |
+
e2 = expected[::-1]
|
| 328 |
+
actual = df.reindex(target[::-1], method=method)
|
| 329 |
+
tm.assert_frame_equal(e2, actual)
|
| 330 |
+
|
| 331 |
+
new_order = [3, 0, 2, 1]
|
| 332 |
+
e2 = expected.iloc[new_order]
|
| 333 |
+
actual = df.reindex(target[new_order], method=method)
|
| 334 |
+
tm.assert_frame_equal(e2, actual)
|
| 335 |
+
|
| 336 |
+
switched_method = (
|
| 337 |
+
"pad" if method == "backfill" else "backfill" if method == "pad" else method
|
| 338 |
+
)
|
| 339 |
+
actual = df[::-1].reindex(target, method=switched_method)
|
| 340 |
+
tm.assert_frame_equal(expected, actual)
|
| 341 |
+
|
| 342 |
+
def test_reindex_methods_nearest_special(self):
|
| 343 |
+
df = DataFrame({"x": list(range(5))})
|
| 344 |
+
target = np.array([-0.1, 0.9, 1.1, 1.5])
|
| 345 |
+
|
| 346 |
+
expected = DataFrame({"x": [0, 1, 1, np.nan]}, index=target)
|
| 347 |
+
actual = df.reindex(target, method="nearest", tolerance=0.2)
|
| 348 |
+
tm.assert_frame_equal(expected, actual)
|
| 349 |
+
|
| 350 |
+
expected = DataFrame({"x": [0, np.nan, 1, np.nan]}, index=target)
|
| 351 |
+
actual = df.reindex(target, method="nearest", tolerance=[0.5, 0.01, 0.4, 0.1])
|
| 352 |
+
tm.assert_frame_equal(expected, actual)
|
| 353 |
+
|
| 354 |
+
def test_reindex_nearest_tz(self, tz_aware_fixture):
|
| 355 |
+
# GH26683
|
| 356 |
+
tz = tz_aware_fixture
|
| 357 |
+
idx = date_range("2019-01-01", periods=5, tz=tz)
|
| 358 |
+
df = DataFrame({"x": list(range(5))}, index=idx)
|
| 359 |
+
|
| 360 |
+
expected = df.head(3)
|
| 361 |
+
actual = df.reindex(idx[:3], method="nearest")
|
| 362 |
+
tm.assert_frame_equal(expected, actual)
|
| 363 |
+
|
| 364 |
+
def test_reindex_nearest_tz_empty_frame(self):
|
| 365 |
+
# https://github.com/pandas-dev/pandas/issues/31964
|
| 366 |
+
dti = pd.DatetimeIndex(["2016-06-26 14:27:26+00:00"])
|
| 367 |
+
df = DataFrame(index=pd.DatetimeIndex(["2016-07-04 14:00:59+00:00"]))
|
| 368 |
+
expected = DataFrame(index=dti)
|
| 369 |
+
result = df.reindex(dti, method="nearest")
|
| 370 |
+
tm.assert_frame_equal(result, expected)
|
| 371 |
+
|
| 372 |
+
def test_reindex_frame_add_nat(self):
|
| 373 |
+
rng = date_range("1/1/2000 00:00:00", periods=10, freq="10s")
|
| 374 |
+
df = DataFrame(
|
| 375 |
+
{"A": np.random.default_rng(2).standard_normal(len(rng)), "B": rng}
|
| 376 |
+
)
|
| 377 |
+
|
| 378 |
+
result = df.reindex(range(15))
|
| 379 |
+
assert np.issubdtype(result["B"].dtype, np.dtype("M8[ns]"))
|
| 380 |
+
|
| 381 |
+
mask = isna(result)["B"]
|
| 382 |
+
assert mask[-5:].all()
|
| 383 |
+
assert not mask[:-5].any()
|
| 384 |
+
|
| 385 |
+
@pytest.mark.parametrize(
|
| 386 |
+
"method, exp_values",
|
| 387 |
+
[("ffill", [0, 1, 2, 3]), ("bfill", [1.0, 2.0, 3.0, np.nan])],
|
| 388 |
+
)
|
| 389 |
+
def test_reindex_frame_tz_ffill_bfill(self, frame_or_series, method, exp_values):
|
| 390 |
+
# GH#38566
|
| 391 |
+
obj = frame_or_series(
|
| 392 |
+
[0, 1, 2, 3],
|
| 393 |
+
index=date_range("2020-01-01 00:00:00", periods=4, freq="h", tz="UTC"),
|
| 394 |
+
)
|
| 395 |
+
new_index = date_range("2020-01-01 00:01:00", periods=4, freq="h", tz="UTC")
|
| 396 |
+
result = obj.reindex(new_index, method=method, tolerance=pd.Timedelta("1 hour"))
|
| 397 |
+
expected = frame_or_series(exp_values, index=new_index)
|
| 398 |
+
tm.assert_equal(result, expected)
|
| 399 |
+
|
| 400 |
+
def test_reindex_limit(self):
|
| 401 |
+
# GH 28631
|
| 402 |
+
data = [["A", "A", "A"], ["B", "B", "B"], ["C", "C", "C"], ["D", "D", "D"]]
|
| 403 |
+
exp_data = [
|
| 404 |
+
["A", "A", "A"],
|
| 405 |
+
["B", "B", "B"],
|
| 406 |
+
["C", "C", "C"],
|
| 407 |
+
["D", "D", "D"],
|
| 408 |
+
["D", "D", "D"],
|
| 409 |
+
[np.nan, np.nan, np.nan],
|
| 410 |
+
]
|
| 411 |
+
df = DataFrame(data)
|
| 412 |
+
result = df.reindex([0, 1, 2, 3, 4, 5], method="ffill", limit=1)
|
| 413 |
+
expected = DataFrame(exp_data)
|
| 414 |
+
tm.assert_frame_equal(result, expected)
|
| 415 |
+
|
| 416 |
+
@pytest.mark.parametrize(
|
| 417 |
+
"idx, check_index_type",
|
| 418 |
+
[
|
| 419 |
+
[["C", "B", "A"], True],
|
| 420 |
+
[["F", "C", "A", "D"], True],
|
| 421 |
+
[["A"], True],
|
| 422 |
+
[["A", "B", "C"], True],
|
| 423 |
+
[["C", "A", "B"], True],
|
| 424 |
+
[["C", "B"], True],
|
| 425 |
+
[["C", "A"], True],
|
| 426 |
+
[["A", "B"], True],
|
| 427 |
+
[["B", "A", "C"], True],
|
| 428 |
+
# reindex by these causes different MultiIndex levels
|
| 429 |
+
[["D", "F"], False],
|
| 430 |
+
[["A", "C", "B"], False],
|
| 431 |
+
],
|
| 432 |
+
)
|
| 433 |
+
def test_reindex_level_verify_first_level(self, idx, check_index_type):
|
| 434 |
+
df = DataFrame(
|
| 435 |
+
{
|
| 436 |
+
"jim": list("B" * 4 + "A" * 2 + "C" * 3),
|
| 437 |
+
"joe": list("abcdeabcd")[::-1],
|
| 438 |
+
"jolie": [10, 20, 30] * 3,
|
| 439 |
+
"joline": np.random.default_rng(2).integers(0, 1000, 9),
|
| 440 |
+
}
|
| 441 |
+
)
|
| 442 |
+
icol = ["jim", "joe", "jolie"]
|
| 443 |
+
|
| 444 |
+
def f(val):
|
| 445 |
+
return np.nonzero((df["jim"] == val).to_numpy())[0]
|
| 446 |
+
|
| 447 |
+
i = np.concatenate(list(map(f, idx)))
|
| 448 |
+
left = df.set_index(icol).reindex(idx, level="jim")
|
| 449 |
+
right = df.iloc[i].set_index(icol)
|
| 450 |
+
tm.assert_frame_equal(left, right, check_index_type=check_index_type)
|
| 451 |
+
|
| 452 |
+
@pytest.mark.parametrize(
|
| 453 |
+
"idx",
|
| 454 |
+
[
|
| 455 |
+
("mid",),
|
| 456 |
+
("mid", "btm"),
|
| 457 |
+
("mid", "btm", "top"),
|
| 458 |
+
("mid",),
|
| 459 |
+
("mid", "top"),
|
| 460 |
+
("mid", "top", "btm"),
|
| 461 |
+
("btm",),
|
| 462 |
+
("btm", "mid"),
|
| 463 |
+
("btm", "mid", "top"),
|
| 464 |
+
("btm",),
|
| 465 |
+
("btm", "top"),
|
| 466 |
+
("btm", "top", "mid"),
|
| 467 |
+
("top",),
|
| 468 |
+
("top", "mid"),
|
| 469 |
+
("top", "mid", "btm"),
|
| 470 |
+
("top",),
|
| 471 |
+
("top", "btm"),
|
| 472 |
+
("top", "btm", "mid"),
|
| 473 |
+
],
|
| 474 |
+
)
|
| 475 |
+
def test_reindex_level_verify_first_level_repeats(self, idx):
|
| 476 |
+
df = DataFrame(
|
| 477 |
+
{
|
| 478 |
+
"jim": ["mid"] * 5 + ["btm"] * 8 + ["top"] * 7,
|
| 479 |
+
"joe": ["3rd"] * 2
|
| 480 |
+
+ ["1st"] * 3
|
| 481 |
+
+ ["2nd"] * 3
|
| 482 |
+
+ ["1st"] * 2
|
| 483 |
+
+ ["3rd"] * 3
|
| 484 |
+
+ ["1st"] * 2
|
| 485 |
+
+ ["3rd"] * 3
|
| 486 |
+
+ ["2nd"] * 2,
|
| 487 |
+
# this needs to be jointly unique with jim and joe or
|
| 488 |
+
# reindexing will fail ~1.5% of the time, this works
|
| 489 |
+
# out to needing unique groups of same size as joe
|
| 490 |
+
"jolie": np.concatenate(
|
| 491 |
+
[
|
| 492 |
+
np.random.default_rng(2).choice(1000, x, replace=False)
|
| 493 |
+
for x in [2, 3, 3, 2, 3, 2, 3, 2]
|
| 494 |
+
]
|
| 495 |
+
),
|
| 496 |
+
"joline": np.random.default_rng(2).standard_normal(20).round(3) * 10,
|
| 497 |
+
}
|
| 498 |
+
)
|
| 499 |
+
icol = ["jim", "joe", "jolie"]
|
| 500 |
+
|
| 501 |
+
def f(val):
|
| 502 |
+
return np.nonzero((df["jim"] == val).to_numpy())[0]
|
| 503 |
+
|
| 504 |
+
i = np.concatenate(list(map(f, idx)))
|
| 505 |
+
left = df.set_index(icol).reindex(idx, level="jim")
|
| 506 |
+
right = df.iloc[i].set_index(icol)
|
| 507 |
+
tm.assert_frame_equal(left, right)
|
| 508 |
+
|
| 509 |
+
@pytest.mark.parametrize(
|
| 510 |
+
"idx, indexer",
|
| 511 |
+
[
|
| 512 |
+
[
|
| 513 |
+
["1st", "2nd", "3rd"],
|
| 514 |
+
[2, 3, 4, 0, 1, 8, 9, 5, 6, 7, 10, 11, 12, 13, 14, 18, 19, 15, 16, 17],
|
| 515 |
+
],
|
| 516 |
+
[
|
| 517 |
+
["3rd", "2nd", "1st"],
|
| 518 |
+
[0, 1, 2, 3, 4, 10, 11, 12, 5, 6, 7, 8, 9, 15, 16, 17, 18, 19, 13, 14],
|
| 519 |
+
],
|
| 520 |
+
[["2nd", "3rd"], [0, 1, 5, 6, 7, 10, 11, 12, 18, 19, 15, 16, 17]],
|
| 521 |
+
[["3rd", "1st"], [0, 1, 2, 3, 4, 10, 11, 12, 8, 9, 15, 16, 17, 13, 14]],
|
| 522 |
+
],
|
| 523 |
+
)
|
| 524 |
+
def test_reindex_level_verify_repeats(self, idx, indexer):
|
| 525 |
+
df = DataFrame(
|
| 526 |
+
{
|
| 527 |
+
"jim": ["mid"] * 5 + ["btm"] * 8 + ["top"] * 7,
|
| 528 |
+
"joe": ["3rd"] * 2
|
| 529 |
+
+ ["1st"] * 3
|
| 530 |
+
+ ["2nd"] * 3
|
| 531 |
+
+ ["1st"] * 2
|
| 532 |
+
+ ["3rd"] * 3
|
| 533 |
+
+ ["1st"] * 2
|
| 534 |
+
+ ["3rd"] * 3
|
| 535 |
+
+ ["2nd"] * 2,
|
| 536 |
+
# this needs to be jointly unique with jim and joe or
|
| 537 |
+
# reindexing will fail ~1.5% of the time, this works
|
| 538 |
+
# out to needing unique groups of same size as joe
|
| 539 |
+
"jolie": np.concatenate(
|
| 540 |
+
[
|
| 541 |
+
np.random.default_rng(2).choice(1000, x, replace=False)
|
| 542 |
+
for x in [2, 3, 3, 2, 3, 2, 3, 2]
|
| 543 |
+
]
|
| 544 |
+
),
|
| 545 |
+
"joline": np.random.default_rng(2).standard_normal(20).round(3) * 10,
|
| 546 |
+
}
|
| 547 |
+
)
|
| 548 |
+
icol = ["jim", "joe", "jolie"]
|
| 549 |
+
left = df.set_index(icol).reindex(idx, level="joe")
|
| 550 |
+
right = df.iloc[indexer].set_index(icol)
|
| 551 |
+
tm.assert_frame_equal(left, right)
|
| 552 |
+
|
| 553 |
+
@pytest.mark.parametrize(
|
| 554 |
+
"idx, indexer, check_index_type",
|
| 555 |
+
[
|
| 556 |
+
[list("abcde"), [3, 2, 1, 0, 5, 4, 8, 7, 6], True],
|
| 557 |
+
[list("abcd"), [3, 2, 1, 0, 5, 8, 7, 6], True],
|
| 558 |
+
[list("abc"), [3, 2, 1, 8, 7, 6], True],
|
| 559 |
+
[list("eca"), [1, 3, 4, 6, 8], True],
|
| 560 |
+
[list("edc"), [0, 1, 4, 5, 6], True],
|
| 561 |
+
[list("eadbc"), [3, 0, 2, 1, 4, 5, 8, 7, 6], True],
|
| 562 |
+
[list("edwq"), [0, 4, 5], True],
|
| 563 |
+
[list("wq"), [], False],
|
| 564 |
+
],
|
| 565 |
+
)
|
| 566 |
+
def test_reindex_level_verify(self, idx, indexer, check_index_type):
|
| 567 |
+
df = DataFrame(
|
| 568 |
+
{
|
| 569 |
+
"jim": list("B" * 4 + "A" * 2 + "C" * 3),
|
| 570 |
+
"joe": list("abcdeabcd")[::-1],
|
| 571 |
+
"jolie": [10, 20, 30] * 3,
|
| 572 |
+
"joline": np.random.default_rng(2).integers(0, 1000, 9),
|
| 573 |
+
}
|
| 574 |
+
)
|
| 575 |
+
icol = ["jim", "joe", "jolie"]
|
| 576 |
+
left = df.set_index(icol).reindex(idx, level="joe")
|
| 577 |
+
right = df.iloc[indexer].set_index(icol)
|
| 578 |
+
tm.assert_frame_equal(left, right, check_index_type=check_index_type)
|
| 579 |
+
|
| 580 |
+
def test_non_monotonic_reindex_methods(self):
|
| 581 |
+
dr = date_range("2013-08-01", periods=6, freq="B")
|
| 582 |
+
data = np.random.default_rng(2).standard_normal((6, 1))
|
| 583 |
+
df = DataFrame(data, index=dr, columns=list("A"))
|
| 584 |
+
df_rev = DataFrame(data, index=dr[[3, 4, 5] + [0, 1, 2]], columns=list("A"))
|
| 585 |
+
# index is not monotonic increasing or decreasing
|
| 586 |
+
msg = "index must be monotonic increasing or decreasing"
|
| 587 |
+
with pytest.raises(ValueError, match=msg):
|
| 588 |
+
df_rev.reindex(df.index, method="pad")
|
| 589 |
+
with pytest.raises(ValueError, match=msg):
|
| 590 |
+
df_rev.reindex(df.index, method="ffill")
|
| 591 |
+
with pytest.raises(ValueError, match=msg):
|
| 592 |
+
df_rev.reindex(df.index, method="bfill")
|
| 593 |
+
with pytest.raises(ValueError, match=msg):
|
| 594 |
+
df_rev.reindex(df.index, method="nearest")
|
| 595 |
+
|
| 596 |
+
def test_reindex_sparse(self):
|
| 597 |
+
# https://github.com/pandas-dev/pandas/issues/35286
|
| 598 |
+
df = DataFrame(
|
| 599 |
+
{"A": [0, 1], "B": pd.array([0, 1], dtype=pd.SparseDtype("int64", 0))}
|
| 600 |
+
)
|
| 601 |
+
result = df.reindex([0, 2])
|
| 602 |
+
expected = DataFrame(
|
| 603 |
+
{
|
| 604 |
+
"A": [0.0, np.nan],
|
| 605 |
+
"B": pd.array([0.0, np.nan], dtype=pd.SparseDtype("float64", 0.0)),
|
| 606 |
+
},
|
| 607 |
+
index=[0, 2],
|
| 608 |
+
)
|
| 609 |
+
tm.assert_frame_equal(result, expected)
|
| 610 |
+
|
| 611 |
+
def test_reindex(self, float_frame, using_copy_on_write):
|
| 612 |
+
datetime_series = Series(
|
| 613 |
+
np.arange(30, dtype=np.float64), index=date_range("2020-01-01", periods=30)
|
| 614 |
+
)
|
| 615 |
+
|
| 616 |
+
newFrame = float_frame.reindex(datetime_series.index)
|
| 617 |
+
|
| 618 |
+
for col in newFrame.columns:
|
| 619 |
+
for idx, val in newFrame[col].items():
|
| 620 |
+
if idx in float_frame.index:
|
| 621 |
+
if np.isnan(val):
|
| 622 |
+
assert np.isnan(float_frame[col][idx])
|
| 623 |
+
else:
|
| 624 |
+
assert val == float_frame[col][idx]
|
| 625 |
+
else:
|
| 626 |
+
assert np.isnan(val)
|
| 627 |
+
|
| 628 |
+
for col, series in newFrame.items():
|
| 629 |
+
tm.assert_index_equal(series.index, newFrame.index)
|
| 630 |
+
emptyFrame = float_frame.reindex(Index([]))
|
| 631 |
+
assert len(emptyFrame.index) == 0
|
| 632 |
+
|
| 633 |
+
# Cython code should be unit-tested directly
|
| 634 |
+
nonContigFrame = float_frame.reindex(datetime_series.index[::2])
|
| 635 |
+
|
| 636 |
+
for col in nonContigFrame.columns:
|
| 637 |
+
for idx, val in nonContigFrame[col].items():
|
| 638 |
+
if idx in float_frame.index:
|
| 639 |
+
if np.isnan(val):
|
| 640 |
+
assert np.isnan(float_frame[col][idx])
|
| 641 |
+
else:
|
| 642 |
+
assert val == float_frame[col][idx]
|
| 643 |
+
else:
|
| 644 |
+
assert np.isnan(val)
|
| 645 |
+
|
| 646 |
+
for col, series in nonContigFrame.items():
|
| 647 |
+
tm.assert_index_equal(series.index, nonContigFrame.index)
|
| 648 |
+
|
| 649 |
+
# corner cases
|
| 650 |
+
|
| 651 |
+
# Same index, copies values but not index if copy=False
|
| 652 |
+
newFrame = float_frame.reindex(float_frame.index, copy=False)
|
| 653 |
+
if using_copy_on_write:
|
| 654 |
+
assert newFrame.index.is_(float_frame.index)
|
| 655 |
+
else:
|
| 656 |
+
assert newFrame.index is float_frame.index
|
| 657 |
+
|
| 658 |
+
# length zero
|
| 659 |
+
newFrame = float_frame.reindex([])
|
| 660 |
+
assert newFrame.empty
|
| 661 |
+
assert len(newFrame.columns) == len(float_frame.columns)
|
| 662 |
+
|
| 663 |
+
# length zero with columns reindexed with non-empty index
|
| 664 |
+
newFrame = float_frame.reindex([])
|
| 665 |
+
newFrame = newFrame.reindex(float_frame.index)
|
| 666 |
+
assert len(newFrame.index) == len(float_frame.index)
|
| 667 |
+
assert len(newFrame.columns) == len(float_frame.columns)
|
| 668 |
+
|
| 669 |
+
# pass non-Index
|
| 670 |
+
newFrame = float_frame.reindex(list(datetime_series.index))
|
| 671 |
+
expected = datetime_series.index._with_freq(None)
|
| 672 |
+
tm.assert_index_equal(newFrame.index, expected)
|
| 673 |
+
|
| 674 |
+
# copy with no axes
|
| 675 |
+
result = float_frame.reindex()
|
| 676 |
+
tm.assert_frame_equal(result, float_frame)
|
| 677 |
+
assert result is not float_frame
|
| 678 |
+
|
| 679 |
+
def test_reindex_nan(self):
|
| 680 |
+
df = DataFrame(
|
| 681 |
+
[[1, 2], [3, 5], [7, 11], [9, 23]],
|
| 682 |
+
index=[2, np.nan, 1, 5],
|
| 683 |
+
columns=["joe", "jim"],
|
| 684 |
+
)
|
| 685 |
+
|
| 686 |
+
i, j = [np.nan, 5, 5, np.nan, 1, 2, np.nan], [1, 3, 3, 1, 2, 0, 1]
|
| 687 |
+
tm.assert_frame_equal(df.reindex(i), df.iloc[j])
|
| 688 |
+
|
| 689 |
+
df.index = df.index.astype("object")
|
| 690 |
+
tm.assert_frame_equal(df.reindex(i), df.iloc[j], check_index_type=False)
|
| 691 |
+
|
| 692 |
+
# GH10388
|
| 693 |
+
df = DataFrame(
|
| 694 |
+
{
|
| 695 |
+
"other": ["a", "b", np.nan, "c"],
|
| 696 |
+
"date": ["2015-03-22", np.nan, "2012-01-08", np.nan],
|
| 697 |
+
"amount": [2, 3, 4, 5],
|
| 698 |
+
}
|
| 699 |
+
)
|
| 700 |
+
|
| 701 |
+
df["date"] = pd.to_datetime(df.date)
|
| 702 |
+
df["delta"] = (pd.to_datetime("2015-06-18") - df["date"]).shift(1)
|
| 703 |
+
|
| 704 |
+
left = df.set_index(["delta", "other", "date"]).reset_index()
|
| 705 |
+
right = df.reindex(columns=["delta", "other", "date", "amount"])
|
| 706 |
+
tm.assert_frame_equal(left, right)
|
| 707 |
+
|
| 708 |
+
def test_reindex_name_remains(self):
|
| 709 |
+
s = Series(np.random.default_rng(2).random(10))
|
| 710 |
+
df = DataFrame(s, index=np.arange(len(s)))
|
| 711 |
+
i = Series(np.arange(10), name="iname")
|
| 712 |
+
|
| 713 |
+
df = df.reindex(i)
|
| 714 |
+
assert df.index.name == "iname"
|
| 715 |
+
|
| 716 |
+
df = df.reindex(Index(np.arange(10), name="tmpname"))
|
| 717 |
+
assert df.index.name == "tmpname"
|
| 718 |
+
|
| 719 |
+
s = Series(np.random.default_rng(2).random(10))
|
| 720 |
+
df = DataFrame(s.T, index=np.arange(len(s)))
|
| 721 |
+
i = Series(np.arange(10), name="iname")
|
| 722 |
+
df = df.reindex(columns=i)
|
| 723 |
+
assert df.columns.name == "iname"
|
| 724 |
+
|
| 725 |
+
def test_reindex_int(self, int_frame):
|
| 726 |
+
smaller = int_frame.reindex(int_frame.index[::2])
|
| 727 |
+
|
| 728 |
+
assert smaller["A"].dtype == np.int64
|
| 729 |
+
|
| 730 |
+
bigger = smaller.reindex(int_frame.index)
|
| 731 |
+
assert bigger["A"].dtype == np.float64
|
| 732 |
+
|
| 733 |
+
smaller = int_frame.reindex(columns=["A", "B"])
|
| 734 |
+
assert smaller["A"].dtype == np.int64
|
| 735 |
+
|
| 736 |
+
def test_reindex_columns(self, float_frame):
|
| 737 |
+
new_frame = float_frame.reindex(columns=["A", "B", "E"])
|
| 738 |
+
|
| 739 |
+
tm.assert_series_equal(new_frame["B"], float_frame["B"])
|
| 740 |
+
assert np.isnan(new_frame["E"]).all()
|
| 741 |
+
assert "C" not in new_frame
|
| 742 |
+
|
| 743 |
+
# Length zero
|
| 744 |
+
new_frame = float_frame.reindex(columns=[])
|
| 745 |
+
assert new_frame.empty
|
| 746 |
+
|
| 747 |
+
def test_reindex_columns_method(self):
|
| 748 |
+
# GH 14992, reindexing over columns ignored method
|
| 749 |
+
df = DataFrame(
|
| 750 |
+
data=[[11, 12, 13], [21, 22, 23], [31, 32, 33]],
|
| 751 |
+
index=[1, 2, 4],
|
| 752 |
+
columns=[1, 2, 4],
|
| 753 |
+
dtype=float,
|
| 754 |
+
)
|
| 755 |
+
|
| 756 |
+
# default method
|
| 757 |
+
result = df.reindex(columns=range(6))
|
| 758 |
+
expected = DataFrame(
|
| 759 |
+
data=[
|
| 760 |
+
[np.nan, 11, 12, np.nan, 13, np.nan],
|
| 761 |
+
[np.nan, 21, 22, np.nan, 23, np.nan],
|
| 762 |
+
[np.nan, 31, 32, np.nan, 33, np.nan],
|
| 763 |
+
],
|
| 764 |
+
index=[1, 2, 4],
|
| 765 |
+
columns=range(6),
|
| 766 |
+
dtype=float,
|
| 767 |
+
)
|
| 768 |
+
tm.assert_frame_equal(result, expected)
|
| 769 |
+
|
| 770 |
+
# method='ffill'
|
| 771 |
+
result = df.reindex(columns=range(6), method="ffill")
|
| 772 |
+
expected = DataFrame(
|
| 773 |
+
data=[
|
| 774 |
+
[np.nan, 11, 12, 12, 13, 13],
|
| 775 |
+
[np.nan, 21, 22, 22, 23, 23],
|
| 776 |
+
[np.nan, 31, 32, 32, 33, 33],
|
| 777 |
+
],
|
| 778 |
+
index=[1, 2, 4],
|
| 779 |
+
columns=range(6),
|
| 780 |
+
dtype=float,
|
| 781 |
+
)
|
| 782 |
+
tm.assert_frame_equal(result, expected)
|
| 783 |
+
|
| 784 |
+
# method='bfill'
|
| 785 |
+
result = df.reindex(columns=range(6), method="bfill")
|
| 786 |
+
expected = DataFrame(
|
| 787 |
+
data=[
|
| 788 |
+
[11, 11, 12, 13, 13, np.nan],
|
| 789 |
+
[21, 21, 22, 23, 23, np.nan],
|
| 790 |
+
[31, 31, 32, 33, 33, np.nan],
|
| 791 |
+
],
|
| 792 |
+
index=[1, 2, 4],
|
| 793 |
+
columns=range(6),
|
| 794 |
+
dtype=float,
|
| 795 |
+
)
|
| 796 |
+
tm.assert_frame_equal(result, expected)
|
| 797 |
+
|
| 798 |
+
def test_reindex_axes(self):
|
| 799 |
+
# GH 3317, reindexing by both axes loses freq of the index
|
| 800 |
+
df = DataFrame(
|
| 801 |
+
np.ones((3, 3)),
|
| 802 |
+
index=[datetime(2012, 1, 1), datetime(2012, 1, 2), datetime(2012, 1, 3)],
|
| 803 |
+
columns=["a", "b", "c"],
|
| 804 |
+
)
|
| 805 |
+
time_freq = date_range("2012-01-01", "2012-01-03", freq="d")
|
| 806 |
+
some_cols = ["a", "b"]
|
| 807 |
+
|
| 808 |
+
index_freq = df.reindex(index=time_freq).index.freq
|
| 809 |
+
both_freq = df.reindex(index=time_freq, columns=some_cols).index.freq
|
| 810 |
+
seq_freq = df.reindex(index=time_freq).reindex(columns=some_cols).index.freq
|
| 811 |
+
assert index_freq == both_freq
|
| 812 |
+
assert index_freq == seq_freq
|
| 813 |
+
|
| 814 |
+
def test_reindex_fill_value(self):
|
| 815 |
+
df = DataFrame(np.random.default_rng(2).standard_normal((10, 4)))
|
| 816 |
+
|
| 817 |
+
# axis=0
|
| 818 |
+
result = df.reindex(list(range(15)))
|
| 819 |
+
assert np.isnan(result.values[-5:]).all()
|
| 820 |
+
|
| 821 |
+
result = df.reindex(range(15), fill_value=0)
|
| 822 |
+
expected = df.reindex(range(15)).fillna(0)
|
| 823 |
+
tm.assert_frame_equal(result, expected)
|
| 824 |
+
|
| 825 |
+
# axis=1
|
| 826 |
+
result = df.reindex(columns=range(5), fill_value=0.0)
|
| 827 |
+
expected = df.copy()
|
| 828 |
+
expected[4] = 0.0
|
| 829 |
+
tm.assert_frame_equal(result, expected)
|
| 830 |
+
|
| 831 |
+
result = df.reindex(columns=range(5), fill_value=0)
|
| 832 |
+
expected = df.copy()
|
| 833 |
+
expected[4] = 0
|
| 834 |
+
tm.assert_frame_equal(result, expected)
|
| 835 |
+
|
| 836 |
+
result = df.reindex(columns=range(5), fill_value="foo")
|
| 837 |
+
expected = df.copy()
|
| 838 |
+
expected[4] = "foo"
|
| 839 |
+
tm.assert_frame_equal(result, expected)
|
| 840 |
+
|
| 841 |
+
# other dtypes
|
| 842 |
+
df["foo"] = "foo"
|
| 843 |
+
result = df.reindex(range(15), fill_value="0")
|
| 844 |
+
expected = df.reindex(range(15)).fillna("0")
|
| 845 |
+
tm.assert_frame_equal(result, expected)
|
| 846 |
+
|
| 847 |
+
def test_reindex_uint_dtypes_fill_value(self, any_unsigned_int_numpy_dtype):
|
| 848 |
+
# GH#48184
|
| 849 |
+
df = DataFrame({"a": [1, 2], "b": [1, 2]}, dtype=any_unsigned_int_numpy_dtype)
|
| 850 |
+
result = df.reindex(columns=list("abcd"), index=[0, 1, 2, 3], fill_value=10)
|
| 851 |
+
expected = DataFrame(
|
| 852 |
+
{"a": [1, 2, 10, 10], "b": [1, 2, 10, 10], "c": 10, "d": 10},
|
| 853 |
+
dtype=any_unsigned_int_numpy_dtype,
|
| 854 |
+
)
|
| 855 |
+
tm.assert_frame_equal(result, expected)
|
| 856 |
+
|
| 857 |
+
def test_reindex_single_column_ea_index_and_columns(self, any_numeric_ea_dtype):
|
| 858 |
+
# GH#48190
|
| 859 |
+
df = DataFrame({"a": [1, 2]}, dtype=any_numeric_ea_dtype)
|
| 860 |
+
result = df.reindex(columns=list("ab"), index=[0, 1, 2], fill_value=10)
|
| 861 |
+
expected = DataFrame(
|
| 862 |
+
{"a": Series([1, 2, 10], dtype=any_numeric_ea_dtype), "b": 10}
|
| 863 |
+
)
|
| 864 |
+
tm.assert_frame_equal(result, expected)
|
| 865 |
+
|
| 866 |
+
def test_reindex_dups(self):
|
| 867 |
+
# GH4746, reindex on duplicate index error messages
|
| 868 |
+
arr = np.random.default_rng(2).standard_normal(10)
|
| 869 |
+
df = DataFrame(arr, index=[1, 2, 3, 4, 5, 1, 2, 3, 4, 5])
|
| 870 |
+
|
| 871 |
+
# set index is ok
|
| 872 |
+
result = df.copy()
|
| 873 |
+
result.index = list(range(len(df)))
|
| 874 |
+
expected = DataFrame(arr, index=list(range(len(df))))
|
| 875 |
+
tm.assert_frame_equal(result, expected)
|
| 876 |
+
|
| 877 |
+
# reindex fails
|
| 878 |
+
msg = "cannot reindex on an axis with duplicate labels"
|
| 879 |
+
with pytest.raises(ValueError, match=msg):
|
| 880 |
+
df.reindex(index=list(range(len(df))))
|
| 881 |
+
|
| 882 |
+
def test_reindex_with_duplicate_columns(self):
|
| 883 |
+
# reindex is invalid!
|
| 884 |
+
df = DataFrame(
|
| 885 |
+
[[1, 5, 7.0], [1, 5, 7.0], [1, 5, 7.0]], columns=["bar", "a", "a"]
|
| 886 |
+
)
|
| 887 |
+
msg = "cannot reindex on an axis with duplicate labels"
|
| 888 |
+
with pytest.raises(ValueError, match=msg):
|
| 889 |
+
df.reindex(columns=["bar"])
|
| 890 |
+
with pytest.raises(ValueError, match=msg):
|
| 891 |
+
df.reindex(columns=["bar", "foo"])
|
| 892 |
+
|
| 893 |
+
def test_reindex_axis_style(self):
|
| 894 |
+
# https://github.com/pandas-dev/pandas/issues/12392
|
| 895 |
+
df = DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
|
| 896 |
+
expected = DataFrame(
|
| 897 |
+
{"A": [1, 2, np.nan], "B": [4, 5, np.nan]}, index=[0, 1, 3]
|
| 898 |
+
)
|
| 899 |
+
result = df.reindex([0, 1, 3])
|
| 900 |
+
tm.assert_frame_equal(result, expected)
|
| 901 |
+
|
| 902 |
+
result = df.reindex([0, 1, 3], axis=0)
|
| 903 |
+
tm.assert_frame_equal(result, expected)
|
| 904 |
+
|
| 905 |
+
result = df.reindex([0, 1, 3], axis="index")
|
| 906 |
+
tm.assert_frame_equal(result, expected)
|
| 907 |
+
|
| 908 |
+
def test_reindex_positional_raises(self):
|
| 909 |
+
# https://github.com/pandas-dev/pandas/issues/12392
|
| 910 |
+
# Enforced in 2.0
|
| 911 |
+
df = DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
|
| 912 |
+
msg = r"reindex\(\) takes from 1 to 2 positional arguments but 3 were given"
|
| 913 |
+
with pytest.raises(TypeError, match=msg):
|
| 914 |
+
df.reindex([0, 1], ["A", "B", "C"])
|
| 915 |
+
|
| 916 |
+
def test_reindex_axis_style_raises(self):
|
| 917 |
+
# https://github.com/pandas-dev/pandas/issues/12392
|
| 918 |
+
df = DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
|
| 919 |
+
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
| 920 |
+
df.reindex([0, 1], columns=["A"], axis=1)
|
| 921 |
+
|
| 922 |
+
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
| 923 |
+
df.reindex([0, 1], columns=["A"], axis="index")
|
| 924 |
+
|
| 925 |
+
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
| 926 |
+
df.reindex(index=[0, 1], axis="index")
|
| 927 |
+
|
| 928 |
+
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
| 929 |
+
df.reindex(index=[0, 1], axis="columns")
|
| 930 |
+
|
| 931 |
+
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
| 932 |
+
df.reindex(columns=[0, 1], axis="columns")
|
| 933 |
+
|
| 934 |
+
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
| 935 |
+
df.reindex(index=[0, 1], columns=[0, 1], axis="columns")
|
| 936 |
+
|
| 937 |
+
with pytest.raises(TypeError, match="Cannot specify all"):
|
| 938 |
+
df.reindex(labels=[0, 1], index=[0], columns=["A"])
|
| 939 |
+
|
| 940 |
+
# Mixing styles
|
| 941 |
+
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
| 942 |
+
df.reindex(index=[0, 1], axis="index")
|
| 943 |
+
|
| 944 |
+
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
| 945 |
+
df.reindex(index=[0, 1], axis="columns")
|
| 946 |
+
|
| 947 |
+
# Duplicates
|
| 948 |
+
with pytest.raises(TypeError, match="multiple values"):
|
| 949 |
+
df.reindex([0, 1], labels=[0, 1])
|
| 950 |
+
|
| 951 |
+
def test_reindex_single_named_indexer(self):
|
| 952 |
+
# https://github.com/pandas-dev/pandas/issues/12392
|
| 953 |
+
df = DataFrame({"A": [1, 2, 3], "B": [1, 2, 3]})
|
| 954 |
+
result = df.reindex([0, 1], columns=["A"])
|
| 955 |
+
expected = DataFrame({"A": [1, 2]})
|
| 956 |
+
tm.assert_frame_equal(result, expected)
|
| 957 |
+
|
| 958 |
+
def test_reindex_api_equivalence(self):
|
| 959 |
+
# https://github.com/pandas-dev/pandas/issues/12392
|
| 960 |
+
# equivalence of the labels/axis and index/columns API's
|
| 961 |
+
df = DataFrame(
|
| 962 |
+
[[1, 2, 3], [3, 4, 5], [5, 6, 7]],
|
| 963 |
+
index=["a", "b", "c"],
|
| 964 |
+
columns=["d", "e", "f"],
|
| 965 |
+
)
|
| 966 |
+
|
| 967 |
+
res1 = df.reindex(["b", "a"])
|
| 968 |
+
res2 = df.reindex(index=["b", "a"])
|
| 969 |
+
res3 = df.reindex(labels=["b", "a"])
|
| 970 |
+
res4 = df.reindex(labels=["b", "a"], axis=0)
|
| 971 |
+
res5 = df.reindex(["b", "a"], axis=0)
|
| 972 |
+
for res in [res2, res3, res4, res5]:
|
| 973 |
+
tm.assert_frame_equal(res1, res)
|
| 974 |
+
|
| 975 |
+
res1 = df.reindex(columns=["e", "d"])
|
| 976 |
+
res2 = df.reindex(["e", "d"], axis=1)
|
| 977 |
+
res3 = df.reindex(labels=["e", "d"], axis=1)
|
| 978 |
+
for res in [res2, res3]:
|
| 979 |
+
tm.assert_frame_equal(res1, res)
|
| 980 |
+
|
| 981 |
+
res1 = df.reindex(index=["b", "a"], columns=["e", "d"])
|
| 982 |
+
res2 = df.reindex(columns=["e", "d"], index=["b", "a"])
|
| 983 |
+
res3 = df.reindex(labels=["b", "a"], axis=0).reindex(labels=["e", "d"], axis=1)
|
| 984 |
+
for res in [res2, res3]:
|
| 985 |
+
tm.assert_frame_equal(res1, res)
|
| 986 |
+
|
| 987 |
+
def test_reindex_boolean(self):
|
| 988 |
+
frame = DataFrame(
|
| 989 |
+
np.ones((10, 2), dtype=bool), index=np.arange(0, 20, 2), columns=[0, 2]
|
| 990 |
+
)
|
| 991 |
+
|
| 992 |
+
reindexed = frame.reindex(np.arange(10))
|
| 993 |
+
assert reindexed.values.dtype == np.object_
|
| 994 |
+
assert isna(reindexed[0][1])
|
| 995 |
+
|
| 996 |
+
reindexed = frame.reindex(columns=range(3))
|
| 997 |
+
assert reindexed.values.dtype == np.object_
|
| 998 |
+
assert isna(reindexed[1]).all()
|
| 999 |
+
|
| 1000 |
+
def test_reindex_objects(self, float_string_frame):
|
| 1001 |
+
reindexed = float_string_frame.reindex(columns=["foo", "A", "B"])
|
| 1002 |
+
assert "foo" in reindexed
|
| 1003 |
+
|
| 1004 |
+
reindexed = float_string_frame.reindex(columns=["A", "B"])
|
| 1005 |
+
assert "foo" not in reindexed
|
| 1006 |
+
|
| 1007 |
+
def test_reindex_corner(self, int_frame):
|
| 1008 |
+
index = Index(["a", "b", "c"])
|
| 1009 |
+
dm = DataFrame({}).reindex(index=[1, 2, 3])
|
| 1010 |
+
reindexed = dm.reindex(columns=index)
|
| 1011 |
+
tm.assert_index_equal(reindexed.columns, index)
|
| 1012 |
+
|
| 1013 |
+
# ints are weird
|
| 1014 |
+
smaller = int_frame.reindex(columns=["A", "B", "E"])
|
| 1015 |
+
assert smaller["E"].dtype == np.float64
|
| 1016 |
+
|
| 1017 |
+
def test_reindex_with_nans(self):
|
| 1018 |
+
df = DataFrame(
|
| 1019 |
+
[[1, 2], [3, 4], [np.nan, np.nan], [7, 8], [9, 10]],
|
| 1020 |
+
columns=["a", "b"],
|
| 1021 |
+
index=[100.0, 101.0, np.nan, 102.0, 103.0],
|
| 1022 |
+
)
|
| 1023 |
+
|
| 1024 |
+
result = df.reindex(index=[101.0, 102.0, 103.0])
|
| 1025 |
+
expected = df.iloc[[1, 3, 4]]
|
| 1026 |
+
tm.assert_frame_equal(result, expected)
|
| 1027 |
+
|
| 1028 |
+
result = df.reindex(index=[103.0])
|
| 1029 |
+
expected = df.iloc[[4]]
|
| 1030 |
+
tm.assert_frame_equal(result, expected)
|
| 1031 |
+
|
| 1032 |
+
result = df.reindex(index=[101.0])
|
| 1033 |
+
expected = df.iloc[[1]]
|
| 1034 |
+
tm.assert_frame_equal(result, expected)
|
| 1035 |
+
|
| 1036 |
+
def test_reindex_multi(self):
|
| 1037 |
+
df = DataFrame(np.random.default_rng(2).standard_normal((3, 3)))
|
| 1038 |
+
|
| 1039 |
+
result = df.reindex(index=range(4), columns=range(4))
|
| 1040 |
+
expected = df.reindex(list(range(4))).reindex(columns=range(4))
|
| 1041 |
+
|
| 1042 |
+
tm.assert_frame_equal(result, expected)
|
| 1043 |
+
|
| 1044 |
+
df = DataFrame(np.random.default_rng(2).integers(0, 10, (3, 3)))
|
| 1045 |
+
|
| 1046 |
+
result = df.reindex(index=range(4), columns=range(4))
|
| 1047 |
+
expected = df.reindex(list(range(4))).reindex(columns=range(4))
|
| 1048 |
+
|
| 1049 |
+
tm.assert_frame_equal(result, expected)
|
| 1050 |
+
|
| 1051 |
+
df = DataFrame(np.random.default_rng(2).integers(0, 10, (3, 3)))
|
| 1052 |
+
|
| 1053 |
+
result = df.reindex(index=range(2), columns=range(2))
|
| 1054 |
+
expected = df.reindex(range(2)).reindex(columns=range(2))
|
| 1055 |
+
|
| 1056 |
+
tm.assert_frame_equal(result, expected)
|
| 1057 |
+
|
| 1058 |
+
df = DataFrame(
|
| 1059 |
+
np.random.default_rng(2).standard_normal((5, 3)) + 1j,
|
| 1060 |
+
columns=["a", "b", "c"],
|
| 1061 |
+
)
|
| 1062 |
+
|
| 1063 |
+
result = df.reindex(index=[0, 1], columns=["a", "b"])
|
| 1064 |
+
expected = df.reindex([0, 1]).reindex(columns=["a", "b"])
|
| 1065 |
+
|
| 1066 |
+
tm.assert_frame_equal(result, expected)
|
| 1067 |
+
|
| 1068 |
+
def test_reindex_multi_categorical_time(self):
|
| 1069 |
+
# https://github.com/pandas-dev/pandas/issues/21390
|
| 1070 |
+
midx = MultiIndex.from_product(
|
| 1071 |
+
[
|
| 1072 |
+
Categorical(["a", "b", "c"]),
|
| 1073 |
+
Categorical(date_range("2012-01-01", periods=3, freq="h")),
|
| 1074 |
+
]
|
| 1075 |
+
)
|
| 1076 |
+
df = DataFrame({"a": range(len(midx))}, index=midx)
|
| 1077 |
+
df2 = df.iloc[[0, 1, 2, 3, 4, 5, 6, 8]]
|
| 1078 |
+
|
| 1079 |
+
result = df2.reindex(midx)
|
| 1080 |
+
expected = DataFrame({"a": [0, 1, 2, 3, 4, 5, 6, np.nan, 8]}, index=midx)
|
| 1081 |
+
tm.assert_frame_equal(result, expected)
|
| 1082 |
+
|
| 1083 |
+
def test_reindex_with_categoricalindex(self):
|
| 1084 |
+
df = DataFrame(
|
| 1085 |
+
{
|
| 1086 |
+
"A": np.arange(3, dtype="int64"),
|
| 1087 |
+
},
|
| 1088 |
+
index=CategoricalIndex(
|
| 1089 |
+
list("abc"), dtype=CategoricalDtype(list("cabe")), name="B"
|
| 1090 |
+
),
|
| 1091 |
+
)
|
| 1092 |
+
|
| 1093 |
+
# reindexing
|
| 1094 |
+
# convert to a regular index
|
| 1095 |
+
result = df.reindex(["a", "b", "e"])
|
| 1096 |
+
expected = DataFrame({"A": [0, 1, np.nan], "B": Series(list("abe"))}).set_index(
|
| 1097 |
+
"B"
|
| 1098 |
+
)
|
| 1099 |
+
tm.assert_frame_equal(result, expected, check_index_type=True)
|
| 1100 |
+
|
| 1101 |
+
result = df.reindex(["a", "b"])
|
| 1102 |
+
expected = DataFrame({"A": [0, 1], "B": Series(list("ab"))}).set_index("B")
|
| 1103 |
+
tm.assert_frame_equal(result, expected, check_index_type=True)
|
| 1104 |
+
|
| 1105 |
+
result = df.reindex(["e"])
|
| 1106 |
+
expected = DataFrame({"A": [np.nan], "B": Series(["e"])}).set_index("B")
|
| 1107 |
+
tm.assert_frame_equal(result, expected, check_index_type=True)
|
| 1108 |
+
|
| 1109 |
+
result = df.reindex(["d"])
|
| 1110 |
+
expected = DataFrame({"A": [np.nan], "B": Series(["d"])}).set_index("B")
|
| 1111 |
+
tm.assert_frame_equal(result, expected, check_index_type=True)
|
| 1112 |
+
|
| 1113 |
+
# since we are actually reindexing with a Categorical
|
| 1114 |
+
# then return a Categorical
|
| 1115 |
+
cats = list("cabe")
|
| 1116 |
+
|
| 1117 |
+
result = df.reindex(Categorical(["a", "e"], categories=cats))
|
| 1118 |
+
expected = DataFrame(
|
| 1119 |
+
{"A": [0, np.nan], "B": Series(list("ae")).astype(CategoricalDtype(cats))}
|
| 1120 |
+
).set_index("B")
|
| 1121 |
+
tm.assert_frame_equal(result, expected, check_index_type=True)
|
| 1122 |
+
|
| 1123 |
+
result = df.reindex(Categorical(["a"], categories=cats))
|
| 1124 |
+
expected = DataFrame(
|
| 1125 |
+
{"A": [0], "B": Series(list("a")).astype(CategoricalDtype(cats))}
|
| 1126 |
+
).set_index("B")
|
| 1127 |
+
tm.assert_frame_equal(result, expected, check_index_type=True)
|
| 1128 |
+
|
| 1129 |
+
result = df.reindex(["a", "b", "e"])
|
| 1130 |
+
expected = DataFrame({"A": [0, 1, np.nan], "B": Series(list("abe"))}).set_index(
|
| 1131 |
+
"B"
|
| 1132 |
+
)
|
| 1133 |
+
tm.assert_frame_equal(result, expected, check_index_type=True)
|
| 1134 |
+
|
| 1135 |
+
result = df.reindex(["a", "b"])
|
| 1136 |
+
expected = DataFrame({"A": [0, 1], "B": Series(list("ab"))}).set_index("B")
|
| 1137 |
+
tm.assert_frame_equal(result, expected, check_index_type=True)
|
| 1138 |
+
|
| 1139 |
+
result = df.reindex(["e"])
|
| 1140 |
+
expected = DataFrame({"A": [np.nan], "B": Series(["e"])}).set_index("B")
|
| 1141 |
+
tm.assert_frame_equal(result, expected, check_index_type=True)
|
| 1142 |
+
|
| 1143 |
+
# give back the type of categorical that we received
|
| 1144 |
+
result = df.reindex(Categorical(["a", "e"], categories=cats, ordered=True))
|
| 1145 |
+
expected = DataFrame(
|
| 1146 |
+
{
|
| 1147 |
+
"A": [0, np.nan],
|
| 1148 |
+
"B": Series(list("ae")).astype(CategoricalDtype(cats, ordered=True)),
|
| 1149 |
+
}
|
| 1150 |
+
).set_index("B")
|
| 1151 |
+
tm.assert_frame_equal(result, expected, check_index_type=True)
|
| 1152 |
+
|
| 1153 |
+
result = df.reindex(Categorical(["a", "d"], categories=["a", "d"]))
|
| 1154 |
+
expected = DataFrame(
|
| 1155 |
+
{
|
| 1156 |
+
"A": [0, np.nan],
|
| 1157 |
+
"B": Series(list("ad")).astype(CategoricalDtype(["a", "d"])),
|
| 1158 |
+
}
|
| 1159 |
+
).set_index("B")
|
| 1160 |
+
tm.assert_frame_equal(result, expected, check_index_type=True)
|
| 1161 |
+
|
| 1162 |
+
df2 = DataFrame(
|
| 1163 |
+
{
|
| 1164 |
+
"A": np.arange(6, dtype="int64"),
|
| 1165 |
+
},
|
| 1166 |
+
index=CategoricalIndex(
|
| 1167 |
+
list("aabbca"), dtype=CategoricalDtype(list("cabe")), name="B"
|
| 1168 |
+
),
|
| 1169 |
+
)
|
| 1170 |
+
# passed duplicate indexers are not allowed
|
| 1171 |
+
msg = "cannot reindex on an axis with duplicate labels"
|
| 1172 |
+
with pytest.raises(ValueError, match=msg):
|
| 1173 |
+
df2.reindex(["a", "b"])
|
| 1174 |
+
|
| 1175 |
+
# args NotImplemented ATM
|
| 1176 |
+
msg = r"argument {} is not implemented for CategoricalIndex\.reindex"
|
| 1177 |
+
with pytest.raises(NotImplementedError, match=msg.format("method")):
|
| 1178 |
+
df.reindex(["a"], method="ffill")
|
| 1179 |
+
with pytest.raises(NotImplementedError, match=msg.format("level")):
|
| 1180 |
+
df.reindex(["a"], level=1)
|
| 1181 |
+
with pytest.raises(NotImplementedError, match=msg.format("limit")):
|
| 1182 |
+
df.reindex(["a"], limit=2)
|
| 1183 |
+
|
| 1184 |
+
def test_reindex_signature(self):
|
| 1185 |
+
sig = inspect.signature(DataFrame.reindex)
|
| 1186 |
+
parameters = set(sig.parameters)
|
| 1187 |
+
assert parameters == {
|
| 1188 |
+
"self",
|
| 1189 |
+
"labels",
|
| 1190 |
+
"index",
|
| 1191 |
+
"columns",
|
| 1192 |
+
"axis",
|
| 1193 |
+
"limit",
|
| 1194 |
+
"copy",
|
| 1195 |
+
"level",
|
| 1196 |
+
"method",
|
| 1197 |
+
"fill_value",
|
| 1198 |
+
"tolerance",
|
| 1199 |
+
}
|
| 1200 |
+
|
| 1201 |
+
def test_reindex_multiindex_ffill_added_rows(self):
|
| 1202 |
+
# GH#23693
|
| 1203 |
+
# reindex added rows with nan values even when fill method was specified
|
| 1204 |
+
mi = MultiIndex.from_tuples([("a", "b"), ("d", "e")])
|
| 1205 |
+
df = DataFrame([[0, 7], [3, 4]], index=mi, columns=["x", "y"])
|
| 1206 |
+
mi2 = MultiIndex.from_tuples([("a", "b"), ("d", "e"), ("h", "i")])
|
| 1207 |
+
result = df.reindex(mi2, axis=0, method="ffill")
|
| 1208 |
+
expected = DataFrame([[0, 7], [3, 4], [3, 4]], index=mi2, columns=["x", "y"])
|
| 1209 |
+
tm.assert_frame_equal(result, expected)
|
| 1210 |
+
|
| 1211 |
+
@pytest.mark.parametrize(
|
| 1212 |
+
"kwargs",
|
| 1213 |
+
[
|
| 1214 |
+
{"method": "pad", "tolerance": timedelta(seconds=9)},
|
| 1215 |
+
{"method": "backfill", "tolerance": timedelta(seconds=9)},
|
| 1216 |
+
{"method": "nearest"},
|
| 1217 |
+
{"method": None},
|
| 1218 |
+
],
|
| 1219 |
+
)
|
| 1220 |
+
def test_reindex_empty_frame(self, kwargs):
|
| 1221 |
+
# GH#27315
|
| 1222 |
+
idx = date_range(start="2020", freq="30s", periods=3)
|
| 1223 |
+
df = DataFrame([], index=Index([], name="time"), columns=["a"])
|
| 1224 |
+
result = df.reindex(idx, **kwargs)
|
| 1225 |
+
expected = DataFrame({"a": [np.nan] * 3}, index=idx, dtype=object)
|
| 1226 |
+
tm.assert_frame_equal(result, expected)
|
| 1227 |
+
|
| 1228 |
+
@pytest.mark.parametrize(
|
| 1229 |
+
"src_idx",
|
| 1230 |
+
[
|
| 1231 |
+
Index([]),
|
| 1232 |
+
CategoricalIndex([]),
|
| 1233 |
+
],
|
| 1234 |
+
)
|
| 1235 |
+
@pytest.mark.parametrize(
|
| 1236 |
+
"cat_idx",
|
| 1237 |
+
[
|
| 1238 |
+
# No duplicates
|
| 1239 |
+
Index([]),
|
| 1240 |
+
CategoricalIndex([]),
|
| 1241 |
+
Index(["A", "B"]),
|
| 1242 |
+
CategoricalIndex(["A", "B"]),
|
| 1243 |
+
# Duplicates: GH#38906
|
| 1244 |
+
Index(["A", "A"]),
|
| 1245 |
+
CategoricalIndex(["A", "A"]),
|
| 1246 |
+
],
|
| 1247 |
+
)
|
| 1248 |
+
def test_reindex_empty(self, src_idx, cat_idx):
|
| 1249 |
+
df = DataFrame(columns=src_idx, index=["K"], dtype="f8")
|
| 1250 |
+
|
| 1251 |
+
result = df.reindex(columns=cat_idx)
|
| 1252 |
+
expected = DataFrame(index=["K"], columns=cat_idx, dtype="f8")
|
| 1253 |
+
tm.assert_frame_equal(result, expected)
|
| 1254 |
+
|
| 1255 |
+
@pytest.mark.parametrize("dtype", ["m8[ns]", "M8[ns]"])
|
| 1256 |
+
def test_reindex_datetimelike_to_object(self, dtype):
|
| 1257 |
+
# GH#39755 dont cast dt64/td64 to ints
|
| 1258 |
+
mi = MultiIndex.from_product([list("ABCDE"), range(2)])
|
| 1259 |
+
|
| 1260 |
+
dti = date_range("2016-01-01", periods=10)
|
| 1261 |
+
fv = np.timedelta64("NaT", "ns")
|
| 1262 |
+
if dtype == "m8[ns]":
|
| 1263 |
+
dti = dti - dti[0]
|
| 1264 |
+
fv = np.datetime64("NaT", "ns")
|
| 1265 |
+
|
| 1266 |
+
ser = Series(dti, index=mi)
|
| 1267 |
+
ser[::3] = pd.NaT
|
| 1268 |
+
|
| 1269 |
+
df = ser.unstack()
|
| 1270 |
+
|
| 1271 |
+
index = df.index.append(Index([1]))
|
| 1272 |
+
columns = df.columns.append(Index(["foo"]))
|
| 1273 |
+
|
| 1274 |
+
res = df.reindex(index=index, columns=columns, fill_value=fv)
|
| 1275 |
+
|
| 1276 |
+
expected = DataFrame(
|
| 1277 |
+
{
|
| 1278 |
+
0: df[0].tolist() + [fv],
|
| 1279 |
+
1: df[1].tolist() + [fv],
|
| 1280 |
+
"foo": np.array(["NaT"] * 6, dtype=fv.dtype),
|
| 1281 |
+
},
|
| 1282 |
+
index=index,
|
| 1283 |
+
)
|
| 1284 |
+
assert (res.dtypes[[0, 1]] == object).all()
|
| 1285 |
+
assert res.iloc[0, 0] is pd.NaT
|
| 1286 |
+
assert res.iloc[-1, 0] is fv
|
| 1287 |
+
assert res.iloc[-1, 1] is fv
|
| 1288 |
+
tm.assert_frame_equal(res, expected)
|
| 1289 |
+
|
| 1290 |
+
@pytest.mark.parametrize(
|
| 1291 |
+
"index_df,index_res,index_exp",
|
| 1292 |
+
[
|
| 1293 |
+
(
|
| 1294 |
+
CategoricalIndex([], categories=["A"]),
|
| 1295 |
+
Index(["A"]),
|
| 1296 |
+
Index(["A"]),
|
| 1297 |
+
),
|
| 1298 |
+
(
|
| 1299 |
+
CategoricalIndex([], categories=["A"]),
|
| 1300 |
+
Index(["B"]),
|
| 1301 |
+
Index(["B"]),
|
| 1302 |
+
),
|
| 1303 |
+
(
|
| 1304 |
+
CategoricalIndex([], categories=["A"]),
|
| 1305 |
+
CategoricalIndex(["A"]),
|
| 1306 |
+
CategoricalIndex(["A"]),
|
| 1307 |
+
),
|
| 1308 |
+
(
|
| 1309 |
+
CategoricalIndex([], categories=["A"]),
|
| 1310 |
+
CategoricalIndex(["B"]),
|
| 1311 |
+
CategoricalIndex(["B"]),
|
| 1312 |
+
),
|
| 1313 |
+
],
|
| 1314 |
+
)
|
| 1315 |
+
def test_reindex_not_category(self, index_df, index_res, index_exp):
|
| 1316 |
+
# GH#28690
|
| 1317 |
+
df = DataFrame(index=index_df)
|
| 1318 |
+
result = df.reindex(index=index_res)
|
| 1319 |
+
expected = DataFrame(index=index_exp)
|
| 1320 |
+
tm.assert_frame_equal(result, expected)
|
| 1321 |
+
|
| 1322 |
+
def test_invalid_method(self):
|
| 1323 |
+
df = DataFrame({"A": [1, np.nan, 2]})
|
| 1324 |
+
|
| 1325 |
+
msg = "Invalid fill method"
|
| 1326 |
+
with pytest.raises(ValueError, match=msg):
|
| 1327 |
+
df.reindex([1, 0, 2], method="asfreq")
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_rename.py
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections import ChainMap
|
| 2 |
+
import inspect
|
| 3 |
+
|
| 4 |
+
import numpy as np
|
| 5 |
+
import pytest
|
| 6 |
+
|
| 7 |
+
from pandas import (
|
| 8 |
+
DataFrame,
|
| 9 |
+
Index,
|
| 10 |
+
MultiIndex,
|
| 11 |
+
merge,
|
| 12 |
+
)
|
| 13 |
+
import pandas._testing as tm
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class TestRename:
|
| 17 |
+
def test_rename_signature(self):
|
| 18 |
+
sig = inspect.signature(DataFrame.rename)
|
| 19 |
+
parameters = set(sig.parameters)
|
| 20 |
+
assert parameters == {
|
| 21 |
+
"self",
|
| 22 |
+
"mapper",
|
| 23 |
+
"index",
|
| 24 |
+
"columns",
|
| 25 |
+
"axis",
|
| 26 |
+
"inplace",
|
| 27 |
+
"copy",
|
| 28 |
+
"level",
|
| 29 |
+
"errors",
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
def test_rename_mi(self, frame_or_series):
|
| 33 |
+
obj = frame_or_series(
|
| 34 |
+
[11, 21, 31],
|
| 35 |
+
index=MultiIndex.from_tuples([("A", x) for x in ["a", "B", "c"]]),
|
| 36 |
+
)
|
| 37 |
+
obj.rename(str.lower)
|
| 38 |
+
|
| 39 |
+
def test_rename(self, float_frame):
|
| 40 |
+
mapping = {"A": "a", "B": "b", "C": "c", "D": "d"}
|
| 41 |
+
|
| 42 |
+
renamed = float_frame.rename(columns=mapping)
|
| 43 |
+
renamed2 = float_frame.rename(columns=str.lower)
|
| 44 |
+
|
| 45 |
+
tm.assert_frame_equal(renamed, renamed2)
|
| 46 |
+
tm.assert_frame_equal(
|
| 47 |
+
renamed2.rename(columns=str.upper), float_frame, check_names=False
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
# index
|
| 51 |
+
data = {"A": {"foo": 0, "bar": 1}}
|
| 52 |
+
|
| 53 |
+
df = DataFrame(data)
|
| 54 |
+
renamed = df.rename(index={"foo": "bar", "bar": "foo"})
|
| 55 |
+
tm.assert_index_equal(renamed.index, Index(["bar", "foo"]))
|
| 56 |
+
|
| 57 |
+
renamed = df.rename(index=str.upper)
|
| 58 |
+
tm.assert_index_equal(renamed.index, Index(["FOO", "BAR"]))
|
| 59 |
+
|
| 60 |
+
# have to pass something
|
| 61 |
+
with pytest.raises(TypeError, match="must pass an index to rename"):
|
| 62 |
+
float_frame.rename()
|
| 63 |
+
|
| 64 |
+
# partial columns
|
| 65 |
+
renamed = float_frame.rename(columns={"C": "foo", "D": "bar"})
|
| 66 |
+
tm.assert_index_equal(renamed.columns, Index(["A", "B", "foo", "bar"]))
|
| 67 |
+
|
| 68 |
+
# other axis
|
| 69 |
+
renamed = float_frame.T.rename(index={"C": "foo", "D": "bar"})
|
| 70 |
+
tm.assert_index_equal(renamed.index, Index(["A", "B", "foo", "bar"]))
|
| 71 |
+
|
| 72 |
+
# index with name
|
| 73 |
+
index = Index(["foo", "bar"], name="name")
|
| 74 |
+
renamer = DataFrame(data, index=index)
|
| 75 |
+
renamed = renamer.rename(index={"foo": "bar", "bar": "foo"})
|
| 76 |
+
tm.assert_index_equal(renamed.index, Index(["bar", "foo"], name="name"))
|
| 77 |
+
assert renamed.index.name == renamer.index.name
|
| 78 |
+
|
| 79 |
+
@pytest.mark.parametrize(
|
| 80 |
+
"args,kwargs",
|
| 81 |
+
[
|
| 82 |
+
((ChainMap({"A": "a"}, {"B": "b"}),), {"axis": "columns"}),
|
| 83 |
+
((), {"columns": ChainMap({"A": "a"}, {"B": "b"})}),
|
| 84 |
+
],
|
| 85 |
+
)
|
| 86 |
+
def test_rename_chainmap(self, args, kwargs):
|
| 87 |
+
# see gh-23859
|
| 88 |
+
colAData = range(1, 11)
|
| 89 |
+
colBdata = np.random.default_rng(2).standard_normal(10)
|
| 90 |
+
|
| 91 |
+
df = DataFrame({"A": colAData, "B": colBdata})
|
| 92 |
+
result = df.rename(*args, **kwargs)
|
| 93 |
+
|
| 94 |
+
expected = DataFrame({"a": colAData, "b": colBdata})
|
| 95 |
+
tm.assert_frame_equal(result, expected)
|
| 96 |
+
|
| 97 |
+
def test_rename_multiindex(self):
|
| 98 |
+
tuples_index = [("foo1", "bar1"), ("foo2", "bar2")]
|
| 99 |
+
tuples_columns = [("fizz1", "buzz1"), ("fizz2", "buzz2")]
|
| 100 |
+
index = MultiIndex.from_tuples(tuples_index, names=["foo", "bar"])
|
| 101 |
+
columns = MultiIndex.from_tuples(tuples_columns, names=["fizz", "buzz"])
|
| 102 |
+
df = DataFrame([(0, 0), (1, 1)], index=index, columns=columns)
|
| 103 |
+
|
| 104 |
+
#
|
| 105 |
+
# without specifying level -> across all levels
|
| 106 |
+
|
| 107 |
+
renamed = df.rename(
|
| 108 |
+
index={"foo1": "foo3", "bar2": "bar3"},
|
| 109 |
+
columns={"fizz1": "fizz3", "buzz2": "buzz3"},
|
| 110 |
+
)
|
| 111 |
+
new_index = MultiIndex.from_tuples(
|
| 112 |
+
[("foo3", "bar1"), ("foo2", "bar3")], names=["foo", "bar"]
|
| 113 |
+
)
|
| 114 |
+
new_columns = MultiIndex.from_tuples(
|
| 115 |
+
[("fizz3", "buzz1"), ("fizz2", "buzz3")], names=["fizz", "buzz"]
|
| 116 |
+
)
|
| 117 |
+
tm.assert_index_equal(renamed.index, new_index)
|
| 118 |
+
tm.assert_index_equal(renamed.columns, new_columns)
|
| 119 |
+
assert renamed.index.names == df.index.names
|
| 120 |
+
assert renamed.columns.names == df.columns.names
|
| 121 |
+
|
| 122 |
+
#
|
| 123 |
+
# with specifying a level (GH13766)
|
| 124 |
+
|
| 125 |
+
# dict
|
| 126 |
+
new_columns = MultiIndex.from_tuples(
|
| 127 |
+
[("fizz3", "buzz1"), ("fizz2", "buzz2")], names=["fizz", "buzz"]
|
| 128 |
+
)
|
| 129 |
+
renamed = df.rename(columns={"fizz1": "fizz3", "buzz2": "buzz3"}, level=0)
|
| 130 |
+
tm.assert_index_equal(renamed.columns, new_columns)
|
| 131 |
+
renamed = df.rename(columns={"fizz1": "fizz3", "buzz2": "buzz3"}, level="fizz")
|
| 132 |
+
tm.assert_index_equal(renamed.columns, new_columns)
|
| 133 |
+
|
| 134 |
+
new_columns = MultiIndex.from_tuples(
|
| 135 |
+
[("fizz1", "buzz1"), ("fizz2", "buzz3")], names=["fizz", "buzz"]
|
| 136 |
+
)
|
| 137 |
+
renamed = df.rename(columns={"fizz1": "fizz3", "buzz2": "buzz3"}, level=1)
|
| 138 |
+
tm.assert_index_equal(renamed.columns, new_columns)
|
| 139 |
+
renamed = df.rename(columns={"fizz1": "fizz3", "buzz2": "buzz3"}, level="buzz")
|
| 140 |
+
tm.assert_index_equal(renamed.columns, new_columns)
|
| 141 |
+
|
| 142 |
+
# function
|
| 143 |
+
func = str.upper
|
| 144 |
+
new_columns = MultiIndex.from_tuples(
|
| 145 |
+
[("FIZZ1", "buzz1"), ("FIZZ2", "buzz2")], names=["fizz", "buzz"]
|
| 146 |
+
)
|
| 147 |
+
renamed = df.rename(columns=func, level=0)
|
| 148 |
+
tm.assert_index_equal(renamed.columns, new_columns)
|
| 149 |
+
renamed = df.rename(columns=func, level="fizz")
|
| 150 |
+
tm.assert_index_equal(renamed.columns, new_columns)
|
| 151 |
+
|
| 152 |
+
new_columns = MultiIndex.from_tuples(
|
| 153 |
+
[("fizz1", "BUZZ1"), ("fizz2", "BUZZ2")], names=["fizz", "buzz"]
|
| 154 |
+
)
|
| 155 |
+
renamed = df.rename(columns=func, level=1)
|
| 156 |
+
tm.assert_index_equal(renamed.columns, new_columns)
|
| 157 |
+
renamed = df.rename(columns=func, level="buzz")
|
| 158 |
+
tm.assert_index_equal(renamed.columns, new_columns)
|
| 159 |
+
|
| 160 |
+
# index
|
| 161 |
+
new_index = MultiIndex.from_tuples(
|
| 162 |
+
[("foo3", "bar1"), ("foo2", "bar2")], names=["foo", "bar"]
|
| 163 |
+
)
|
| 164 |
+
renamed = df.rename(index={"foo1": "foo3", "bar2": "bar3"}, level=0)
|
| 165 |
+
tm.assert_index_equal(renamed.index, new_index)
|
| 166 |
+
|
| 167 |
+
def test_rename_nocopy(self, float_frame, using_copy_on_write, warn_copy_on_write):
|
| 168 |
+
renamed = float_frame.rename(columns={"C": "foo"}, copy=False)
|
| 169 |
+
|
| 170 |
+
assert np.shares_memory(renamed["foo"]._values, float_frame["C"]._values)
|
| 171 |
+
|
| 172 |
+
with tm.assert_cow_warning(warn_copy_on_write):
|
| 173 |
+
renamed.loc[:, "foo"] = 1.0
|
| 174 |
+
if using_copy_on_write:
|
| 175 |
+
assert not (float_frame["C"] == 1.0).all()
|
| 176 |
+
else:
|
| 177 |
+
assert (float_frame["C"] == 1.0).all()
|
| 178 |
+
|
| 179 |
+
def test_rename_inplace(self, float_frame):
|
| 180 |
+
float_frame.rename(columns={"C": "foo"})
|
| 181 |
+
assert "C" in float_frame
|
| 182 |
+
assert "foo" not in float_frame
|
| 183 |
+
|
| 184 |
+
c_values = float_frame["C"]
|
| 185 |
+
float_frame = float_frame.copy()
|
| 186 |
+
return_value = float_frame.rename(columns={"C": "foo"}, inplace=True)
|
| 187 |
+
assert return_value is None
|
| 188 |
+
|
| 189 |
+
assert "C" not in float_frame
|
| 190 |
+
assert "foo" in float_frame
|
| 191 |
+
# GH 44153
|
| 192 |
+
# Used to be id(float_frame["foo"]) != c_id, but flaky in the CI
|
| 193 |
+
assert float_frame["foo"] is not c_values
|
| 194 |
+
|
| 195 |
+
def test_rename_bug(self):
|
| 196 |
+
# GH 5344
|
| 197 |
+
# rename set ref_locs, and set_index was not resetting
|
| 198 |
+
df = DataFrame({0: ["foo", "bar"], 1: ["bah", "bas"], 2: [1, 2]})
|
| 199 |
+
df = df.rename(columns={0: "a"})
|
| 200 |
+
df = df.rename(columns={1: "b"})
|
| 201 |
+
df = df.set_index(["a", "b"])
|
| 202 |
+
df.columns = ["2001-01-01"]
|
| 203 |
+
expected = DataFrame(
|
| 204 |
+
[[1], [2]],
|
| 205 |
+
index=MultiIndex.from_tuples(
|
| 206 |
+
[("foo", "bah"), ("bar", "bas")], names=["a", "b"]
|
| 207 |
+
),
|
| 208 |
+
columns=["2001-01-01"],
|
| 209 |
+
)
|
| 210 |
+
tm.assert_frame_equal(df, expected)
|
| 211 |
+
|
| 212 |
+
def test_rename_bug2(self):
|
| 213 |
+
# GH 19497
|
| 214 |
+
# rename was changing Index to MultiIndex if Index contained tuples
|
| 215 |
+
|
| 216 |
+
df = DataFrame(data=np.arange(3), index=[(0, 0), (1, 1), (2, 2)], columns=["a"])
|
| 217 |
+
df = df.rename({(1, 1): (5, 4)}, axis="index")
|
| 218 |
+
expected = DataFrame(
|
| 219 |
+
data=np.arange(3), index=[(0, 0), (5, 4), (2, 2)], columns=["a"]
|
| 220 |
+
)
|
| 221 |
+
tm.assert_frame_equal(df, expected)
|
| 222 |
+
|
| 223 |
+
def test_rename_errors_raises(self):
|
| 224 |
+
df = DataFrame(columns=["A", "B", "C", "D"])
|
| 225 |
+
with pytest.raises(KeyError, match="'E'] not found in axis"):
|
| 226 |
+
df.rename(columns={"A": "a", "E": "e"}, errors="raise")
|
| 227 |
+
|
| 228 |
+
@pytest.mark.parametrize(
|
| 229 |
+
"mapper, errors, expected_columns",
|
| 230 |
+
[
|
| 231 |
+
({"A": "a", "E": "e"}, "ignore", ["a", "B", "C", "D"]),
|
| 232 |
+
({"A": "a"}, "raise", ["a", "B", "C", "D"]),
|
| 233 |
+
(str.lower, "raise", ["a", "b", "c", "d"]),
|
| 234 |
+
],
|
| 235 |
+
)
|
| 236 |
+
def test_rename_errors(self, mapper, errors, expected_columns):
|
| 237 |
+
# GH 13473
|
| 238 |
+
# rename now works with errors parameter
|
| 239 |
+
df = DataFrame(columns=["A", "B", "C", "D"])
|
| 240 |
+
result = df.rename(columns=mapper, errors=errors)
|
| 241 |
+
expected = DataFrame(columns=expected_columns)
|
| 242 |
+
tm.assert_frame_equal(result, expected)
|
| 243 |
+
|
| 244 |
+
def test_rename_objects(self, float_string_frame):
|
| 245 |
+
renamed = float_string_frame.rename(columns=str.upper)
|
| 246 |
+
|
| 247 |
+
assert "FOO" in renamed
|
| 248 |
+
assert "foo" not in renamed
|
| 249 |
+
|
| 250 |
+
def test_rename_axis_style(self):
|
| 251 |
+
# https://github.com/pandas-dev/pandas/issues/12392
|
| 252 |
+
df = DataFrame({"A": [1, 2], "B": [1, 2]}, index=["X", "Y"])
|
| 253 |
+
expected = DataFrame({"a": [1, 2], "b": [1, 2]}, index=["X", "Y"])
|
| 254 |
+
|
| 255 |
+
result = df.rename(str.lower, axis=1)
|
| 256 |
+
tm.assert_frame_equal(result, expected)
|
| 257 |
+
|
| 258 |
+
result = df.rename(str.lower, axis="columns")
|
| 259 |
+
tm.assert_frame_equal(result, expected)
|
| 260 |
+
|
| 261 |
+
result = df.rename({"A": "a", "B": "b"}, axis=1)
|
| 262 |
+
tm.assert_frame_equal(result, expected)
|
| 263 |
+
|
| 264 |
+
result = df.rename({"A": "a", "B": "b"}, axis="columns")
|
| 265 |
+
tm.assert_frame_equal(result, expected)
|
| 266 |
+
|
| 267 |
+
# Index
|
| 268 |
+
expected = DataFrame({"A": [1, 2], "B": [1, 2]}, index=["x", "y"])
|
| 269 |
+
result = df.rename(str.lower, axis=0)
|
| 270 |
+
tm.assert_frame_equal(result, expected)
|
| 271 |
+
|
| 272 |
+
result = df.rename(str.lower, axis="index")
|
| 273 |
+
tm.assert_frame_equal(result, expected)
|
| 274 |
+
|
| 275 |
+
result = df.rename({"X": "x", "Y": "y"}, axis=0)
|
| 276 |
+
tm.assert_frame_equal(result, expected)
|
| 277 |
+
|
| 278 |
+
result = df.rename({"X": "x", "Y": "y"}, axis="index")
|
| 279 |
+
tm.assert_frame_equal(result, expected)
|
| 280 |
+
|
| 281 |
+
result = df.rename(mapper=str.lower, axis="index")
|
| 282 |
+
tm.assert_frame_equal(result, expected)
|
| 283 |
+
|
| 284 |
+
def test_rename_mapper_multi(self):
|
| 285 |
+
df = DataFrame({"A": ["a", "b"], "B": ["c", "d"], "C": [1, 2]}).set_index(
|
| 286 |
+
["A", "B"]
|
| 287 |
+
)
|
| 288 |
+
result = df.rename(str.upper)
|
| 289 |
+
expected = df.rename(index=str.upper)
|
| 290 |
+
tm.assert_frame_equal(result, expected)
|
| 291 |
+
|
| 292 |
+
def test_rename_positional_named(self):
|
| 293 |
+
# https://github.com/pandas-dev/pandas/issues/12392
|
| 294 |
+
df = DataFrame({"a": [1, 2], "b": [1, 2]}, index=["X", "Y"])
|
| 295 |
+
result = df.rename(index=str.lower, columns=str.upper)
|
| 296 |
+
expected = DataFrame({"A": [1, 2], "B": [1, 2]}, index=["x", "y"])
|
| 297 |
+
tm.assert_frame_equal(result, expected)
|
| 298 |
+
|
| 299 |
+
def test_rename_axis_style_raises(self):
|
| 300 |
+
# see gh-12392
|
| 301 |
+
df = DataFrame({"A": [1, 2], "B": [1, 2]}, index=["0", "1"])
|
| 302 |
+
|
| 303 |
+
# Named target and axis
|
| 304 |
+
over_spec_msg = "Cannot specify both 'axis' and any of 'index' or 'columns'"
|
| 305 |
+
with pytest.raises(TypeError, match=over_spec_msg):
|
| 306 |
+
df.rename(index=str.lower, axis=1)
|
| 307 |
+
|
| 308 |
+
with pytest.raises(TypeError, match=over_spec_msg):
|
| 309 |
+
df.rename(index=str.lower, axis="columns")
|
| 310 |
+
|
| 311 |
+
with pytest.raises(TypeError, match=over_spec_msg):
|
| 312 |
+
df.rename(columns=str.lower, axis="columns")
|
| 313 |
+
|
| 314 |
+
with pytest.raises(TypeError, match=over_spec_msg):
|
| 315 |
+
df.rename(index=str.lower, axis=0)
|
| 316 |
+
|
| 317 |
+
# Multiple targets and axis
|
| 318 |
+
with pytest.raises(TypeError, match=over_spec_msg):
|
| 319 |
+
df.rename(str.lower, index=str.lower, axis="columns")
|
| 320 |
+
|
| 321 |
+
# Too many targets
|
| 322 |
+
over_spec_msg = "Cannot specify both 'mapper' and any of 'index' or 'columns'"
|
| 323 |
+
with pytest.raises(TypeError, match=over_spec_msg):
|
| 324 |
+
df.rename(str.lower, index=str.lower, columns=str.lower)
|
| 325 |
+
|
| 326 |
+
# Duplicates
|
| 327 |
+
with pytest.raises(TypeError, match="multiple values"):
|
| 328 |
+
df.rename(id, mapper=id)
|
| 329 |
+
|
| 330 |
+
def test_rename_positional_raises(self):
|
| 331 |
+
# GH 29136
|
| 332 |
+
df = DataFrame(columns=["A", "B"])
|
| 333 |
+
msg = r"rename\(\) takes from 1 to 2 positional arguments"
|
| 334 |
+
|
| 335 |
+
with pytest.raises(TypeError, match=msg):
|
| 336 |
+
df.rename(None, str.lower)
|
| 337 |
+
|
| 338 |
+
def test_rename_no_mappings_raises(self):
|
| 339 |
+
# GH 29136
|
| 340 |
+
df = DataFrame([[1]])
|
| 341 |
+
msg = "must pass an index to rename"
|
| 342 |
+
with pytest.raises(TypeError, match=msg):
|
| 343 |
+
df.rename()
|
| 344 |
+
|
| 345 |
+
with pytest.raises(TypeError, match=msg):
|
| 346 |
+
df.rename(None, index=None)
|
| 347 |
+
|
| 348 |
+
with pytest.raises(TypeError, match=msg):
|
| 349 |
+
df.rename(None, columns=None)
|
| 350 |
+
|
| 351 |
+
with pytest.raises(TypeError, match=msg):
|
| 352 |
+
df.rename(None, columns=None, index=None)
|
| 353 |
+
|
| 354 |
+
def test_rename_mapper_and_positional_arguments_raises(self):
|
| 355 |
+
# GH 29136
|
| 356 |
+
df = DataFrame([[1]])
|
| 357 |
+
msg = "Cannot specify both 'mapper' and any of 'index' or 'columns'"
|
| 358 |
+
with pytest.raises(TypeError, match=msg):
|
| 359 |
+
df.rename({}, index={})
|
| 360 |
+
|
| 361 |
+
with pytest.raises(TypeError, match=msg):
|
| 362 |
+
df.rename({}, columns={})
|
| 363 |
+
|
| 364 |
+
with pytest.raises(TypeError, match=msg):
|
| 365 |
+
df.rename({}, columns={}, index={})
|
| 366 |
+
|
| 367 |
+
def test_rename_with_duplicate_columns(self):
|
| 368 |
+
# GH#4403
|
| 369 |
+
df4 = DataFrame(
|
| 370 |
+
{"RT": [0.0454], "TClose": [22.02], "TExg": [0.0422]},
|
| 371 |
+
index=MultiIndex.from_tuples(
|
| 372 |
+
[(600809, 20130331)], names=["STK_ID", "RPT_Date"]
|
| 373 |
+
),
|
| 374 |
+
)
|
| 375 |
+
|
| 376 |
+
df5 = DataFrame(
|
| 377 |
+
{
|
| 378 |
+
"RPT_Date": [20120930, 20121231, 20130331],
|
| 379 |
+
"STK_ID": [600809] * 3,
|
| 380 |
+
"STK_Name": ["饡驦", "饡驦", "饡驦"],
|
| 381 |
+
"TClose": [38.05, 41.66, 30.01],
|
| 382 |
+
},
|
| 383 |
+
index=MultiIndex.from_tuples(
|
| 384 |
+
[(600809, 20120930), (600809, 20121231), (600809, 20130331)],
|
| 385 |
+
names=["STK_ID", "RPT_Date"],
|
| 386 |
+
),
|
| 387 |
+
)
|
| 388 |
+
# TODO: can we construct this without merge?
|
| 389 |
+
k = merge(df4, df5, how="inner", left_index=True, right_index=True)
|
| 390 |
+
result = k.rename(columns={"TClose_x": "TClose", "TClose_y": "QT_Close"})
|
| 391 |
+
|
| 392 |
+
expected = DataFrame(
|
| 393 |
+
[[0.0454, 22.02, 0.0422, 20130331, 600809, "饡驦", 30.01]],
|
| 394 |
+
columns=[
|
| 395 |
+
"RT",
|
| 396 |
+
"TClose",
|
| 397 |
+
"TExg",
|
| 398 |
+
"RPT_Date",
|
| 399 |
+
"STK_ID",
|
| 400 |
+
"STK_Name",
|
| 401 |
+
"QT_Close",
|
| 402 |
+
],
|
| 403 |
+
).set_index(["STK_ID", "RPT_Date"], drop=False)
|
| 404 |
+
tm.assert_frame_equal(result, expected)
|
| 405 |
+
|
| 406 |
+
def test_rename_boolean_index(self):
|
| 407 |
+
df = DataFrame(np.arange(15).reshape(3, 5), columns=[False, True, 2, 3, 4])
|
| 408 |
+
mapper = {0: "foo", 1: "bar", 2: "bah"}
|
| 409 |
+
res = df.rename(index=mapper)
|
| 410 |
+
exp = DataFrame(
|
| 411 |
+
np.arange(15).reshape(3, 5),
|
| 412 |
+
columns=[False, True, 2, 3, 4],
|
| 413 |
+
index=["foo", "bar", "bah"],
|
| 414 |
+
)
|
| 415 |
+
tm.assert_frame_equal(res, exp)
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_reorder_levels.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pytest
|
| 3 |
+
|
| 4 |
+
from pandas import (
|
| 5 |
+
DataFrame,
|
| 6 |
+
MultiIndex,
|
| 7 |
+
)
|
| 8 |
+
import pandas._testing as tm
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class TestReorderLevels:
|
| 12 |
+
def test_reorder_levels(self, frame_or_series):
|
| 13 |
+
index = MultiIndex(
|
| 14 |
+
levels=[["bar"], ["one", "two", "three"], [0, 1]],
|
| 15 |
+
codes=[[0, 0, 0, 0, 0, 0], [0, 1, 2, 0, 1, 2], [0, 1, 0, 1, 0, 1]],
|
| 16 |
+
names=["L0", "L1", "L2"],
|
| 17 |
+
)
|
| 18 |
+
df = DataFrame({"A": np.arange(6), "B": np.arange(6)}, index=index)
|
| 19 |
+
obj = tm.get_obj(df, frame_or_series)
|
| 20 |
+
|
| 21 |
+
# no change, position
|
| 22 |
+
result = obj.reorder_levels([0, 1, 2])
|
| 23 |
+
tm.assert_equal(obj, result)
|
| 24 |
+
|
| 25 |
+
# no change, labels
|
| 26 |
+
result = obj.reorder_levels(["L0", "L1", "L2"])
|
| 27 |
+
tm.assert_equal(obj, result)
|
| 28 |
+
|
| 29 |
+
# rotate, position
|
| 30 |
+
result = obj.reorder_levels([1, 2, 0])
|
| 31 |
+
e_idx = MultiIndex(
|
| 32 |
+
levels=[["one", "two", "three"], [0, 1], ["bar"]],
|
| 33 |
+
codes=[[0, 1, 2, 0, 1, 2], [0, 1, 0, 1, 0, 1], [0, 0, 0, 0, 0, 0]],
|
| 34 |
+
names=["L1", "L2", "L0"],
|
| 35 |
+
)
|
| 36 |
+
expected = DataFrame({"A": np.arange(6), "B": np.arange(6)}, index=e_idx)
|
| 37 |
+
expected = tm.get_obj(expected, frame_or_series)
|
| 38 |
+
tm.assert_equal(result, expected)
|
| 39 |
+
|
| 40 |
+
result = obj.reorder_levels([0, 0, 0])
|
| 41 |
+
e_idx = MultiIndex(
|
| 42 |
+
levels=[["bar"], ["bar"], ["bar"]],
|
| 43 |
+
codes=[[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]],
|
| 44 |
+
names=["L0", "L0", "L0"],
|
| 45 |
+
)
|
| 46 |
+
expected = DataFrame({"A": np.arange(6), "B": np.arange(6)}, index=e_idx)
|
| 47 |
+
expected = tm.get_obj(expected, frame_or_series)
|
| 48 |
+
tm.assert_equal(result, expected)
|
| 49 |
+
|
| 50 |
+
result = obj.reorder_levels(["L0", "L0", "L0"])
|
| 51 |
+
tm.assert_equal(result, expected)
|
| 52 |
+
|
| 53 |
+
def test_reorder_levels_swaplevel_equivalence(
|
| 54 |
+
self, multiindex_year_month_day_dataframe_random_data
|
| 55 |
+
):
|
| 56 |
+
ymd = multiindex_year_month_day_dataframe_random_data
|
| 57 |
+
|
| 58 |
+
result = ymd.reorder_levels(["month", "day", "year"])
|
| 59 |
+
expected = ymd.swaplevel(0, 1).swaplevel(1, 2)
|
| 60 |
+
tm.assert_frame_equal(result, expected)
|
| 61 |
+
|
| 62 |
+
result = ymd["A"].reorder_levels(["month", "day", "year"])
|
| 63 |
+
expected = ymd["A"].swaplevel(0, 1).swaplevel(1, 2)
|
| 64 |
+
tm.assert_series_equal(result, expected)
|
| 65 |
+
|
| 66 |
+
result = ymd.T.reorder_levels(["month", "day", "year"], axis=1)
|
| 67 |
+
expected = ymd.T.swaplevel(0, 1, axis=1).swaplevel(1, 2, axis=1)
|
| 68 |
+
tm.assert_frame_equal(result, expected)
|
| 69 |
+
|
| 70 |
+
with pytest.raises(TypeError, match="hierarchical axis"):
|
| 71 |
+
ymd.reorder_levels([1, 2], axis=1)
|
| 72 |
+
|
| 73 |
+
with pytest.raises(IndexError, match="Too many levels"):
|
| 74 |
+
ymd.index.reorder_levels([1, 2, 3])
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_shift.py
ADDED
|
@@ -0,0 +1,764 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pytest
|
| 3 |
+
|
| 4 |
+
import pandas.util._test_decorators as td
|
| 5 |
+
|
| 6 |
+
import pandas as pd
|
| 7 |
+
from pandas import (
|
| 8 |
+
CategoricalIndex,
|
| 9 |
+
DataFrame,
|
| 10 |
+
Index,
|
| 11 |
+
NaT,
|
| 12 |
+
Series,
|
| 13 |
+
date_range,
|
| 14 |
+
offsets,
|
| 15 |
+
)
|
| 16 |
+
import pandas._testing as tm
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class TestDataFrameShift:
|
| 20 |
+
def test_shift_axis1_with_valid_fill_value_one_array(self):
|
| 21 |
+
# Case with axis=1 that does not go through the "len(arrays)>1" path
|
| 22 |
+
# in DataFrame.shift
|
| 23 |
+
data = np.random.default_rng(2).standard_normal((5, 3))
|
| 24 |
+
df = DataFrame(data)
|
| 25 |
+
res = df.shift(axis=1, periods=1, fill_value=12345)
|
| 26 |
+
expected = df.T.shift(periods=1, fill_value=12345).T
|
| 27 |
+
tm.assert_frame_equal(res, expected)
|
| 28 |
+
|
| 29 |
+
# same but with an 1D ExtensionArray backing it
|
| 30 |
+
df2 = df[[0]].astype("Float64")
|
| 31 |
+
res2 = df2.shift(axis=1, periods=1, fill_value=12345)
|
| 32 |
+
expected2 = DataFrame([12345] * 5, dtype="Float64")
|
| 33 |
+
tm.assert_frame_equal(res2, expected2)
|
| 34 |
+
|
| 35 |
+
def test_shift_deprecate_freq_and_fill_value(self, frame_or_series):
|
| 36 |
+
# Can't pass both!
|
| 37 |
+
obj = frame_or_series(
|
| 38 |
+
np.random.default_rng(2).standard_normal(5),
|
| 39 |
+
index=date_range("1/1/2000", periods=5, freq="h"),
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
msg = (
|
| 43 |
+
"Passing a 'freq' together with a 'fill_value' silently ignores the "
|
| 44 |
+
"fill_value"
|
| 45 |
+
)
|
| 46 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 47 |
+
obj.shift(1, fill_value=1, freq="h")
|
| 48 |
+
|
| 49 |
+
if frame_or_series is DataFrame:
|
| 50 |
+
obj.columns = date_range("1/1/2000", periods=1, freq="h")
|
| 51 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 52 |
+
obj.shift(1, axis=1, fill_value=1, freq="h")
|
| 53 |
+
|
| 54 |
+
@pytest.mark.parametrize(
|
| 55 |
+
"input_data, output_data",
|
| 56 |
+
[(np.empty(shape=(0,)), []), (np.ones(shape=(2,)), [np.nan, 1.0])],
|
| 57 |
+
)
|
| 58 |
+
def test_shift_non_writable_array(self, input_data, output_data, frame_or_series):
|
| 59 |
+
# GH21049 Verify whether non writable numpy array is shiftable
|
| 60 |
+
input_data.setflags(write=False)
|
| 61 |
+
|
| 62 |
+
result = frame_or_series(input_data).shift(1)
|
| 63 |
+
if frame_or_series is not Series:
|
| 64 |
+
# need to explicitly specify columns in the empty case
|
| 65 |
+
expected = frame_or_series(
|
| 66 |
+
output_data,
|
| 67 |
+
index=range(len(output_data)),
|
| 68 |
+
columns=range(1),
|
| 69 |
+
dtype="float64",
|
| 70 |
+
)
|
| 71 |
+
else:
|
| 72 |
+
expected = frame_or_series(output_data, dtype="float64")
|
| 73 |
+
|
| 74 |
+
tm.assert_equal(result, expected)
|
| 75 |
+
|
| 76 |
+
def test_shift_mismatched_freq(self, frame_or_series):
|
| 77 |
+
ts = frame_or_series(
|
| 78 |
+
np.random.default_rng(2).standard_normal(5),
|
| 79 |
+
index=date_range("1/1/2000", periods=5, freq="h"),
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
result = ts.shift(1, freq="5min")
|
| 83 |
+
exp_index = ts.index.shift(1, freq="5min")
|
| 84 |
+
tm.assert_index_equal(result.index, exp_index)
|
| 85 |
+
|
| 86 |
+
# GH#1063, multiple of same base
|
| 87 |
+
result = ts.shift(1, freq="4h")
|
| 88 |
+
exp_index = ts.index + offsets.Hour(4)
|
| 89 |
+
tm.assert_index_equal(result.index, exp_index)
|
| 90 |
+
|
| 91 |
+
@pytest.mark.parametrize(
|
| 92 |
+
"obj",
|
| 93 |
+
[
|
| 94 |
+
Series([np.arange(5)]),
|
| 95 |
+
date_range("1/1/2011", periods=24, freq="h"),
|
| 96 |
+
Series(range(5), index=date_range("2017", periods=5)),
|
| 97 |
+
],
|
| 98 |
+
)
|
| 99 |
+
@pytest.mark.parametrize("shift_size", [0, 1, 2])
|
| 100 |
+
def test_shift_always_copy(self, obj, shift_size, frame_or_series):
|
| 101 |
+
# GH#22397
|
| 102 |
+
if frame_or_series is not Series:
|
| 103 |
+
obj = obj.to_frame()
|
| 104 |
+
assert obj.shift(shift_size) is not obj
|
| 105 |
+
|
| 106 |
+
def test_shift_object_non_scalar_fill(self):
|
| 107 |
+
# shift requires scalar fill_value except for object dtype
|
| 108 |
+
ser = Series(range(3))
|
| 109 |
+
with pytest.raises(ValueError, match="fill_value must be a scalar"):
|
| 110 |
+
ser.shift(1, fill_value=[])
|
| 111 |
+
|
| 112 |
+
df = ser.to_frame()
|
| 113 |
+
with pytest.raises(ValueError, match="fill_value must be a scalar"):
|
| 114 |
+
df.shift(1, fill_value=np.arange(3))
|
| 115 |
+
|
| 116 |
+
obj_ser = ser.astype(object)
|
| 117 |
+
result = obj_ser.shift(1, fill_value={})
|
| 118 |
+
assert result[0] == {}
|
| 119 |
+
|
| 120 |
+
obj_df = obj_ser.to_frame()
|
| 121 |
+
result = obj_df.shift(1, fill_value={})
|
| 122 |
+
assert result.iloc[0, 0] == {}
|
| 123 |
+
|
| 124 |
+
def test_shift_int(self, datetime_frame, frame_or_series):
|
| 125 |
+
ts = tm.get_obj(datetime_frame, frame_or_series).astype(int)
|
| 126 |
+
shifted = ts.shift(1)
|
| 127 |
+
expected = ts.astype(float).shift(1)
|
| 128 |
+
tm.assert_equal(shifted, expected)
|
| 129 |
+
|
| 130 |
+
@pytest.mark.parametrize("dtype", ["int32", "int64"])
|
| 131 |
+
def test_shift_32bit_take(self, frame_or_series, dtype):
|
| 132 |
+
# 32-bit taking
|
| 133 |
+
# GH#8129
|
| 134 |
+
index = date_range("2000-01-01", periods=5)
|
| 135 |
+
arr = np.arange(5, dtype=dtype)
|
| 136 |
+
s1 = frame_or_series(arr, index=index)
|
| 137 |
+
p = arr[1]
|
| 138 |
+
result = s1.shift(periods=p)
|
| 139 |
+
expected = frame_or_series([np.nan, 0, 1, 2, 3], index=index)
|
| 140 |
+
tm.assert_equal(result, expected)
|
| 141 |
+
|
| 142 |
+
@pytest.mark.parametrize("periods", [1, 2, 3, 4])
|
| 143 |
+
def test_shift_preserve_freqstr(self, periods, frame_or_series):
|
| 144 |
+
# GH#21275
|
| 145 |
+
obj = frame_or_series(
|
| 146 |
+
range(periods),
|
| 147 |
+
index=date_range("2016-1-1 00:00:00", periods=periods, freq="h"),
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
result = obj.shift(1, "2h")
|
| 151 |
+
|
| 152 |
+
expected = frame_or_series(
|
| 153 |
+
range(periods),
|
| 154 |
+
index=date_range("2016-1-1 02:00:00", periods=periods, freq="h"),
|
| 155 |
+
)
|
| 156 |
+
tm.assert_equal(result, expected)
|
| 157 |
+
|
| 158 |
+
def test_shift_dst(self, frame_or_series):
|
| 159 |
+
# GH#13926
|
| 160 |
+
dates = date_range("2016-11-06", freq="h", periods=10, tz="US/Eastern")
|
| 161 |
+
obj = frame_or_series(dates)
|
| 162 |
+
|
| 163 |
+
res = obj.shift(0)
|
| 164 |
+
tm.assert_equal(res, obj)
|
| 165 |
+
assert tm.get_dtype(res) == "datetime64[ns, US/Eastern]"
|
| 166 |
+
|
| 167 |
+
res = obj.shift(1)
|
| 168 |
+
exp_vals = [NaT] + dates.astype(object).values.tolist()[:9]
|
| 169 |
+
exp = frame_or_series(exp_vals)
|
| 170 |
+
tm.assert_equal(res, exp)
|
| 171 |
+
assert tm.get_dtype(res) == "datetime64[ns, US/Eastern]"
|
| 172 |
+
|
| 173 |
+
res = obj.shift(-2)
|
| 174 |
+
exp_vals = dates.astype(object).values.tolist()[2:] + [NaT, NaT]
|
| 175 |
+
exp = frame_or_series(exp_vals)
|
| 176 |
+
tm.assert_equal(res, exp)
|
| 177 |
+
assert tm.get_dtype(res) == "datetime64[ns, US/Eastern]"
|
| 178 |
+
|
| 179 |
+
@pytest.mark.parametrize("ex", [10, -10, 20, -20])
|
| 180 |
+
def test_shift_dst_beyond(self, frame_or_series, ex):
|
| 181 |
+
# GH#13926
|
| 182 |
+
dates = date_range("2016-11-06", freq="h", periods=10, tz="US/Eastern")
|
| 183 |
+
obj = frame_or_series(dates)
|
| 184 |
+
res = obj.shift(ex)
|
| 185 |
+
exp = frame_or_series([NaT] * 10, dtype="datetime64[ns, US/Eastern]")
|
| 186 |
+
tm.assert_equal(res, exp)
|
| 187 |
+
assert tm.get_dtype(res) == "datetime64[ns, US/Eastern]"
|
| 188 |
+
|
| 189 |
+
def test_shift_by_zero(self, datetime_frame, frame_or_series):
|
| 190 |
+
# shift by 0
|
| 191 |
+
obj = tm.get_obj(datetime_frame, frame_or_series)
|
| 192 |
+
unshifted = obj.shift(0)
|
| 193 |
+
tm.assert_equal(unshifted, obj)
|
| 194 |
+
|
| 195 |
+
def test_shift(self, datetime_frame):
|
| 196 |
+
# naive shift
|
| 197 |
+
ser = datetime_frame["A"]
|
| 198 |
+
|
| 199 |
+
shifted = datetime_frame.shift(5)
|
| 200 |
+
tm.assert_index_equal(shifted.index, datetime_frame.index)
|
| 201 |
+
|
| 202 |
+
shifted_ser = ser.shift(5)
|
| 203 |
+
tm.assert_series_equal(shifted["A"], shifted_ser)
|
| 204 |
+
|
| 205 |
+
shifted = datetime_frame.shift(-5)
|
| 206 |
+
tm.assert_index_equal(shifted.index, datetime_frame.index)
|
| 207 |
+
|
| 208 |
+
shifted_ser = ser.shift(-5)
|
| 209 |
+
tm.assert_series_equal(shifted["A"], shifted_ser)
|
| 210 |
+
|
| 211 |
+
unshifted = datetime_frame.shift(5).shift(-5)
|
| 212 |
+
tm.assert_numpy_array_equal(
|
| 213 |
+
unshifted.dropna().values, datetime_frame.values[:-5]
|
| 214 |
+
)
|
| 215 |
+
|
| 216 |
+
unshifted_ser = ser.shift(5).shift(-5)
|
| 217 |
+
tm.assert_numpy_array_equal(unshifted_ser.dropna().values, ser.values[:-5])
|
| 218 |
+
|
| 219 |
+
def test_shift_by_offset(self, datetime_frame, frame_or_series):
|
| 220 |
+
# shift by DateOffset
|
| 221 |
+
obj = tm.get_obj(datetime_frame, frame_or_series)
|
| 222 |
+
offset = offsets.BDay()
|
| 223 |
+
|
| 224 |
+
shifted = obj.shift(5, freq=offset)
|
| 225 |
+
assert len(shifted) == len(obj)
|
| 226 |
+
unshifted = shifted.shift(-5, freq=offset)
|
| 227 |
+
tm.assert_equal(unshifted, obj)
|
| 228 |
+
|
| 229 |
+
shifted2 = obj.shift(5, freq="B")
|
| 230 |
+
tm.assert_equal(shifted, shifted2)
|
| 231 |
+
|
| 232 |
+
unshifted = obj.shift(0, freq=offset)
|
| 233 |
+
tm.assert_equal(unshifted, obj)
|
| 234 |
+
|
| 235 |
+
d = obj.index[0]
|
| 236 |
+
shifted_d = d + offset * 5
|
| 237 |
+
if frame_or_series is DataFrame:
|
| 238 |
+
tm.assert_series_equal(obj.xs(d), shifted.xs(shifted_d), check_names=False)
|
| 239 |
+
else:
|
| 240 |
+
tm.assert_almost_equal(obj.at[d], shifted.at[shifted_d])
|
| 241 |
+
|
| 242 |
+
def test_shift_with_periodindex(self, frame_or_series):
|
| 243 |
+
# Shifting with PeriodIndex
|
| 244 |
+
ps = DataFrame(
|
| 245 |
+
np.arange(4, dtype=float), index=pd.period_range("2020-01-01", periods=4)
|
| 246 |
+
)
|
| 247 |
+
ps = tm.get_obj(ps, frame_or_series)
|
| 248 |
+
|
| 249 |
+
shifted = ps.shift(1)
|
| 250 |
+
unshifted = shifted.shift(-1)
|
| 251 |
+
tm.assert_index_equal(shifted.index, ps.index)
|
| 252 |
+
tm.assert_index_equal(unshifted.index, ps.index)
|
| 253 |
+
if frame_or_series is DataFrame:
|
| 254 |
+
tm.assert_numpy_array_equal(
|
| 255 |
+
unshifted.iloc[:, 0].dropna().values, ps.iloc[:-1, 0].values
|
| 256 |
+
)
|
| 257 |
+
else:
|
| 258 |
+
tm.assert_numpy_array_equal(unshifted.dropna().values, ps.values[:-1])
|
| 259 |
+
|
| 260 |
+
shifted2 = ps.shift(1, "D")
|
| 261 |
+
shifted3 = ps.shift(1, offsets.Day())
|
| 262 |
+
tm.assert_equal(shifted2, shifted3)
|
| 263 |
+
tm.assert_equal(ps, shifted2.shift(-1, "D"))
|
| 264 |
+
|
| 265 |
+
msg = "does not match PeriodIndex freq"
|
| 266 |
+
with pytest.raises(ValueError, match=msg):
|
| 267 |
+
ps.shift(freq="W")
|
| 268 |
+
|
| 269 |
+
# legacy support
|
| 270 |
+
shifted4 = ps.shift(1, freq="D")
|
| 271 |
+
tm.assert_equal(shifted2, shifted4)
|
| 272 |
+
|
| 273 |
+
shifted5 = ps.shift(1, freq=offsets.Day())
|
| 274 |
+
tm.assert_equal(shifted5, shifted4)
|
| 275 |
+
|
| 276 |
+
def test_shift_other_axis(self):
|
| 277 |
+
# shift other axis
|
| 278 |
+
# GH#6371
|
| 279 |
+
df = DataFrame(np.random.default_rng(2).random((10, 5)))
|
| 280 |
+
expected = pd.concat(
|
| 281 |
+
[DataFrame(np.nan, index=df.index, columns=[0]), df.iloc[:, 0:-1]],
|
| 282 |
+
ignore_index=True,
|
| 283 |
+
axis=1,
|
| 284 |
+
)
|
| 285 |
+
result = df.shift(1, axis=1)
|
| 286 |
+
tm.assert_frame_equal(result, expected)
|
| 287 |
+
|
| 288 |
+
def test_shift_named_axis(self):
|
| 289 |
+
# shift named axis
|
| 290 |
+
df = DataFrame(np.random.default_rng(2).random((10, 5)))
|
| 291 |
+
expected = pd.concat(
|
| 292 |
+
[DataFrame(np.nan, index=df.index, columns=[0]), df.iloc[:, 0:-1]],
|
| 293 |
+
ignore_index=True,
|
| 294 |
+
axis=1,
|
| 295 |
+
)
|
| 296 |
+
result = df.shift(1, axis="columns")
|
| 297 |
+
tm.assert_frame_equal(result, expected)
|
| 298 |
+
|
| 299 |
+
def test_shift_other_axis_with_freq(self, datetime_frame):
|
| 300 |
+
obj = datetime_frame.T
|
| 301 |
+
offset = offsets.BDay()
|
| 302 |
+
|
| 303 |
+
# GH#47039
|
| 304 |
+
shifted = obj.shift(5, freq=offset, axis=1)
|
| 305 |
+
assert len(shifted) == len(obj)
|
| 306 |
+
unshifted = shifted.shift(-5, freq=offset, axis=1)
|
| 307 |
+
tm.assert_equal(unshifted, obj)
|
| 308 |
+
|
| 309 |
+
def test_shift_bool(self):
|
| 310 |
+
df = DataFrame({"high": [True, False], "low": [False, False]})
|
| 311 |
+
rs = df.shift(1)
|
| 312 |
+
xp = DataFrame(
|
| 313 |
+
np.array([[np.nan, np.nan], [True, False]], dtype=object),
|
| 314 |
+
columns=["high", "low"],
|
| 315 |
+
)
|
| 316 |
+
tm.assert_frame_equal(rs, xp)
|
| 317 |
+
|
| 318 |
+
def test_shift_categorical1(self, frame_or_series):
|
| 319 |
+
# GH#9416
|
| 320 |
+
obj = frame_or_series(["a", "b", "c", "d"], dtype="category")
|
| 321 |
+
|
| 322 |
+
rt = obj.shift(1).shift(-1)
|
| 323 |
+
tm.assert_equal(obj.iloc[:-1], rt.dropna())
|
| 324 |
+
|
| 325 |
+
def get_cat_values(ndframe):
|
| 326 |
+
# For Series we could just do ._values; for DataFrame
|
| 327 |
+
# we may be able to do this if we ever have 2D Categoricals
|
| 328 |
+
return ndframe._mgr.arrays[0]
|
| 329 |
+
|
| 330 |
+
cat = get_cat_values(obj)
|
| 331 |
+
|
| 332 |
+
sp1 = obj.shift(1)
|
| 333 |
+
tm.assert_index_equal(obj.index, sp1.index)
|
| 334 |
+
assert np.all(get_cat_values(sp1).codes[:1] == -1)
|
| 335 |
+
assert np.all(cat.codes[:-1] == get_cat_values(sp1).codes[1:])
|
| 336 |
+
|
| 337 |
+
sn2 = obj.shift(-2)
|
| 338 |
+
tm.assert_index_equal(obj.index, sn2.index)
|
| 339 |
+
assert np.all(get_cat_values(sn2).codes[-2:] == -1)
|
| 340 |
+
assert np.all(cat.codes[2:] == get_cat_values(sn2).codes[:-2])
|
| 341 |
+
|
| 342 |
+
tm.assert_index_equal(cat.categories, get_cat_values(sp1).categories)
|
| 343 |
+
tm.assert_index_equal(cat.categories, get_cat_values(sn2).categories)
|
| 344 |
+
|
| 345 |
+
def test_shift_categorical(self):
|
| 346 |
+
# GH#9416
|
| 347 |
+
s1 = Series(["a", "b", "c"], dtype="category")
|
| 348 |
+
s2 = Series(["A", "B", "C"], dtype="category")
|
| 349 |
+
df = DataFrame({"one": s1, "two": s2})
|
| 350 |
+
rs = df.shift(1)
|
| 351 |
+
xp = DataFrame({"one": s1.shift(1), "two": s2.shift(1)})
|
| 352 |
+
tm.assert_frame_equal(rs, xp)
|
| 353 |
+
|
| 354 |
+
def test_shift_categorical_fill_value(self, frame_or_series):
|
| 355 |
+
ts = frame_or_series(["a", "b", "c", "d"], dtype="category")
|
| 356 |
+
res = ts.shift(1, fill_value="a")
|
| 357 |
+
expected = frame_or_series(
|
| 358 |
+
pd.Categorical(
|
| 359 |
+
["a", "a", "b", "c"], categories=["a", "b", "c", "d"], ordered=False
|
| 360 |
+
)
|
| 361 |
+
)
|
| 362 |
+
tm.assert_equal(res, expected)
|
| 363 |
+
|
| 364 |
+
# check for incorrect fill_value
|
| 365 |
+
msg = r"Cannot setitem on a Categorical with a new category \(f\)"
|
| 366 |
+
with pytest.raises(TypeError, match=msg):
|
| 367 |
+
ts.shift(1, fill_value="f")
|
| 368 |
+
|
| 369 |
+
def test_shift_fill_value(self, frame_or_series):
|
| 370 |
+
# GH#24128
|
| 371 |
+
dti = date_range("1/1/2000", periods=5, freq="h")
|
| 372 |
+
|
| 373 |
+
ts = frame_or_series([1.0, 2.0, 3.0, 4.0, 5.0], index=dti)
|
| 374 |
+
exp = frame_or_series([0.0, 1.0, 2.0, 3.0, 4.0], index=dti)
|
| 375 |
+
# check that fill value works
|
| 376 |
+
result = ts.shift(1, fill_value=0.0)
|
| 377 |
+
tm.assert_equal(result, exp)
|
| 378 |
+
|
| 379 |
+
exp = frame_or_series([0.0, 0.0, 1.0, 2.0, 3.0], index=dti)
|
| 380 |
+
result = ts.shift(2, fill_value=0.0)
|
| 381 |
+
tm.assert_equal(result, exp)
|
| 382 |
+
|
| 383 |
+
ts = frame_or_series([1, 2, 3])
|
| 384 |
+
res = ts.shift(2, fill_value=0)
|
| 385 |
+
assert tm.get_dtype(res) == tm.get_dtype(ts)
|
| 386 |
+
|
| 387 |
+
# retain integer dtype
|
| 388 |
+
obj = frame_or_series([1, 2, 3, 4, 5], index=dti)
|
| 389 |
+
exp = frame_or_series([0, 1, 2, 3, 4], index=dti)
|
| 390 |
+
result = obj.shift(1, fill_value=0)
|
| 391 |
+
tm.assert_equal(result, exp)
|
| 392 |
+
|
| 393 |
+
exp = frame_or_series([0, 0, 1, 2, 3], index=dti)
|
| 394 |
+
result = obj.shift(2, fill_value=0)
|
| 395 |
+
tm.assert_equal(result, exp)
|
| 396 |
+
|
| 397 |
+
def test_shift_empty(self):
|
| 398 |
+
# Regression test for GH#8019
|
| 399 |
+
df = DataFrame({"foo": []})
|
| 400 |
+
rs = df.shift(-1)
|
| 401 |
+
|
| 402 |
+
tm.assert_frame_equal(df, rs)
|
| 403 |
+
|
| 404 |
+
def test_shift_duplicate_columns(self):
|
| 405 |
+
# GH#9092; verify that position-based shifting works
|
| 406 |
+
# in the presence of duplicate columns
|
| 407 |
+
column_lists = [list(range(5)), [1] * 5, [1, 1, 2, 2, 1]]
|
| 408 |
+
data = np.random.default_rng(2).standard_normal((20, 5))
|
| 409 |
+
|
| 410 |
+
shifted = []
|
| 411 |
+
for columns in column_lists:
|
| 412 |
+
df = DataFrame(data.copy(), columns=columns)
|
| 413 |
+
for s in range(5):
|
| 414 |
+
df.iloc[:, s] = df.iloc[:, s].shift(s + 1)
|
| 415 |
+
df.columns = range(5)
|
| 416 |
+
shifted.append(df)
|
| 417 |
+
|
| 418 |
+
# sanity check the base case
|
| 419 |
+
nulls = shifted[0].isna().sum()
|
| 420 |
+
tm.assert_series_equal(nulls, Series(range(1, 6), dtype="int64"))
|
| 421 |
+
|
| 422 |
+
# check all answers are the same
|
| 423 |
+
tm.assert_frame_equal(shifted[0], shifted[1])
|
| 424 |
+
tm.assert_frame_equal(shifted[0], shifted[2])
|
| 425 |
+
|
| 426 |
+
def test_shift_axis1_multiple_blocks(self, using_array_manager):
|
| 427 |
+
# GH#35488
|
| 428 |
+
df1 = DataFrame(np.random.default_rng(2).integers(1000, size=(5, 3)))
|
| 429 |
+
df2 = DataFrame(np.random.default_rng(2).integers(1000, size=(5, 2)))
|
| 430 |
+
df3 = pd.concat([df1, df2], axis=1)
|
| 431 |
+
if not using_array_manager:
|
| 432 |
+
assert len(df3._mgr.blocks) == 2
|
| 433 |
+
|
| 434 |
+
result = df3.shift(2, axis=1)
|
| 435 |
+
|
| 436 |
+
expected = df3.take([-1, -1, 0, 1, 2], axis=1)
|
| 437 |
+
# Explicit cast to float to avoid implicit cast when setting nan.
|
| 438 |
+
# Column names aren't unique, so directly calling `expected.astype` won't work.
|
| 439 |
+
expected = expected.pipe(
|
| 440 |
+
lambda df: df.set_axis(range(df.shape[1]), axis=1)
|
| 441 |
+
.astype({0: "float", 1: "float"})
|
| 442 |
+
.set_axis(df.columns, axis=1)
|
| 443 |
+
)
|
| 444 |
+
expected.iloc[:, :2] = np.nan
|
| 445 |
+
expected.columns = df3.columns
|
| 446 |
+
|
| 447 |
+
tm.assert_frame_equal(result, expected)
|
| 448 |
+
|
| 449 |
+
# Case with periods < 0
|
| 450 |
+
# rebuild df3 because `take` call above consolidated
|
| 451 |
+
df3 = pd.concat([df1, df2], axis=1)
|
| 452 |
+
if not using_array_manager:
|
| 453 |
+
assert len(df3._mgr.blocks) == 2
|
| 454 |
+
result = df3.shift(-2, axis=1)
|
| 455 |
+
|
| 456 |
+
expected = df3.take([2, 3, 4, -1, -1], axis=1)
|
| 457 |
+
# Explicit cast to float to avoid implicit cast when setting nan.
|
| 458 |
+
# Column names aren't unique, so directly calling `expected.astype` won't work.
|
| 459 |
+
expected = expected.pipe(
|
| 460 |
+
lambda df: df.set_axis(range(df.shape[1]), axis=1)
|
| 461 |
+
.astype({3: "float", 4: "float"})
|
| 462 |
+
.set_axis(df.columns, axis=1)
|
| 463 |
+
)
|
| 464 |
+
expected.iloc[:, -2:] = np.nan
|
| 465 |
+
expected.columns = df3.columns
|
| 466 |
+
|
| 467 |
+
tm.assert_frame_equal(result, expected)
|
| 468 |
+
|
| 469 |
+
@td.skip_array_manager_not_yet_implemented # TODO(ArrayManager) axis=1 support
|
| 470 |
+
def test_shift_axis1_multiple_blocks_with_int_fill(self):
|
| 471 |
+
# GH#42719
|
| 472 |
+
rng = np.random.default_rng(2)
|
| 473 |
+
df1 = DataFrame(rng.integers(1000, size=(5, 3), dtype=int))
|
| 474 |
+
df2 = DataFrame(rng.integers(1000, size=(5, 2), dtype=int))
|
| 475 |
+
df3 = pd.concat([df1.iloc[:4, 1:3], df2.iloc[:4, :]], axis=1)
|
| 476 |
+
result = df3.shift(2, axis=1, fill_value=np.int_(0))
|
| 477 |
+
assert len(df3._mgr.blocks) == 2
|
| 478 |
+
|
| 479 |
+
expected = df3.take([-1, -1, 0, 1], axis=1)
|
| 480 |
+
expected.iloc[:, :2] = np.int_(0)
|
| 481 |
+
expected.columns = df3.columns
|
| 482 |
+
|
| 483 |
+
tm.assert_frame_equal(result, expected)
|
| 484 |
+
|
| 485 |
+
# Case with periods < 0
|
| 486 |
+
df3 = pd.concat([df1.iloc[:4, 1:3], df2.iloc[:4, :]], axis=1)
|
| 487 |
+
result = df3.shift(-2, axis=1, fill_value=np.int_(0))
|
| 488 |
+
assert len(df3._mgr.blocks) == 2
|
| 489 |
+
|
| 490 |
+
expected = df3.take([2, 3, -1, -1], axis=1)
|
| 491 |
+
expected.iloc[:, -2:] = np.int_(0)
|
| 492 |
+
expected.columns = df3.columns
|
| 493 |
+
|
| 494 |
+
tm.assert_frame_equal(result, expected)
|
| 495 |
+
|
| 496 |
+
def test_period_index_frame_shift_with_freq(self, frame_or_series):
|
| 497 |
+
ps = DataFrame(range(4), index=pd.period_range("2020-01-01", periods=4))
|
| 498 |
+
ps = tm.get_obj(ps, frame_or_series)
|
| 499 |
+
|
| 500 |
+
shifted = ps.shift(1, freq="infer")
|
| 501 |
+
unshifted = shifted.shift(-1, freq="infer")
|
| 502 |
+
tm.assert_equal(unshifted, ps)
|
| 503 |
+
|
| 504 |
+
shifted2 = ps.shift(freq="D")
|
| 505 |
+
tm.assert_equal(shifted, shifted2)
|
| 506 |
+
|
| 507 |
+
shifted3 = ps.shift(freq=offsets.Day())
|
| 508 |
+
tm.assert_equal(shifted, shifted3)
|
| 509 |
+
|
| 510 |
+
def test_datetime_frame_shift_with_freq(self, datetime_frame, frame_or_series):
|
| 511 |
+
dtobj = tm.get_obj(datetime_frame, frame_or_series)
|
| 512 |
+
shifted = dtobj.shift(1, freq="infer")
|
| 513 |
+
unshifted = shifted.shift(-1, freq="infer")
|
| 514 |
+
tm.assert_equal(dtobj, unshifted)
|
| 515 |
+
|
| 516 |
+
shifted2 = dtobj.shift(freq=dtobj.index.freq)
|
| 517 |
+
tm.assert_equal(shifted, shifted2)
|
| 518 |
+
|
| 519 |
+
inferred_ts = DataFrame(
|
| 520 |
+
datetime_frame.values,
|
| 521 |
+
Index(np.asarray(datetime_frame.index)),
|
| 522 |
+
columns=datetime_frame.columns,
|
| 523 |
+
)
|
| 524 |
+
inferred_ts = tm.get_obj(inferred_ts, frame_or_series)
|
| 525 |
+
shifted = inferred_ts.shift(1, freq="infer")
|
| 526 |
+
expected = dtobj.shift(1, freq="infer")
|
| 527 |
+
expected.index = expected.index._with_freq(None)
|
| 528 |
+
tm.assert_equal(shifted, expected)
|
| 529 |
+
|
| 530 |
+
unshifted = shifted.shift(-1, freq="infer")
|
| 531 |
+
tm.assert_equal(unshifted, inferred_ts)
|
| 532 |
+
|
| 533 |
+
def test_period_index_frame_shift_with_freq_error(self, frame_or_series):
|
| 534 |
+
ps = DataFrame(range(4), index=pd.period_range("2020-01-01", periods=4))
|
| 535 |
+
ps = tm.get_obj(ps, frame_or_series)
|
| 536 |
+
msg = "Given freq M does not match PeriodIndex freq D"
|
| 537 |
+
with pytest.raises(ValueError, match=msg):
|
| 538 |
+
ps.shift(freq="M")
|
| 539 |
+
|
| 540 |
+
def test_datetime_frame_shift_with_freq_error(
|
| 541 |
+
self, datetime_frame, frame_or_series
|
| 542 |
+
):
|
| 543 |
+
dtobj = tm.get_obj(datetime_frame, frame_or_series)
|
| 544 |
+
no_freq = dtobj.iloc[[0, 5, 7]]
|
| 545 |
+
msg = "Freq was not set in the index hence cannot be inferred"
|
| 546 |
+
with pytest.raises(ValueError, match=msg):
|
| 547 |
+
no_freq.shift(freq="infer")
|
| 548 |
+
|
| 549 |
+
def test_shift_dt64values_int_fill_deprecated(self):
|
| 550 |
+
# GH#31971
|
| 551 |
+
ser = Series([pd.Timestamp("2020-01-01"), pd.Timestamp("2020-01-02")])
|
| 552 |
+
|
| 553 |
+
with pytest.raises(TypeError, match="value should be a"):
|
| 554 |
+
ser.shift(1, fill_value=0)
|
| 555 |
+
|
| 556 |
+
df = ser.to_frame()
|
| 557 |
+
with pytest.raises(TypeError, match="value should be a"):
|
| 558 |
+
df.shift(1, fill_value=0)
|
| 559 |
+
|
| 560 |
+
# axis = 1
|
| 561 |
+
df2 = DataFrame({"A": ser, "B": ser})
|
| 562 |
+
df2._consolidate_inplace()
|
| 563 |
+
|
| 564 |
+
result = df2.shift(1, axis=1, fill_value=0)
|
| 565 |
+
expected = DataFrame({"A": [0, 0], "B": df2["A"]})
|
| 566 |
+
tm.assert_frame_equal(result, expected)
|
| 567 |
+
|
| 568 |
+
# same thing but not consolidated; pre-2.0 we got different behavior
|
| 569 |
+
df3 = DataFrame({"A": ser})
|
| 570 |
+
df3["B"] = ser
|
| 571 |
+
assert len(df3._mgr.arrays) == 2
|
| 572 |
+
result = df3.shift(1, axis=1, fill_value=0)
|
| 573 |
+
tm.assert_frame_equal(result, expected)
|
| 574 |
+
|
| 575 |
+
@pytest.mark.parametrize(
|
| 576 |
+
"as_cat",
|
| 577 |
+
[
|
| 578 |
+
pytest.param(
|
| 579 |
+
True,
|
| 580 |
+
marks=pytest.mark.xfail(
|
| 581 |
+
reason="_can_hold_element incorrectly always returns True"
|
| 582 |
+
),
|
| 583 |
+
),
|
| 584 |
+
False,
|
| 585 |
+
],
|
| 586 |
+
)
|
| 587 |
+
@pytest.mark.parametrize(
|
| 588 |
+
"vals",
|
| 589 |
+
[
|
| 590 |
+
date_range("2020-01-01", periods=2),
|
| 591 |
+
date_range("2020-01-01", periods=2, tz="US/Pacific"),
|
| 592 |
+
pd.period_range("2020-01-01", periods=2, freq="D"),
|
| 593 |
+
pd.timedelta_range("2020 Days", periods=2, freq="D"),
|
| 594 |
+
pd.interval_range(0, 3, periods=2),
|
| 595 |
+
pytest.param(
|
| 596 |
+
pd.array([1, 2], dtype="Int64"),
|
| 597 |
+
marks=pytest.mark.xfail(
|
| 598 |
+
reason="_can_hold_element incorrectly always returns True"
|
| 599 |
+
),
|
| 600 |
+
),
|
| 601 |
+
pytest.param(
|
| 602 |
+
pd.array([1, 2], dtype="Float32"),
|
| 603 |
+
marks=pytest.mark.xfail(
|
| 604 |
+
reason="_can_hold_element incorrectly always returns True"
|
| 605 |
+
),
|
| 606 |
+
),
|
| 607 |
+
],
|
| 608 |
+
ids=lambda x: str(x.dtype),
|
| 609 |
+
)
|
| 610 |
+
def test_shift_dt64values_axis1_invalid_fill(self, vals, as_cat):
|
| 611 |
+
# GH#44564
|
| 612 |
+
ser = Series(vals)
|
| 613 |
+
if as_cat:
|
| 614 |
+
ser = ser.astype("category")
|
| 615 |
+
|
| 616 |
+
df = DataFrame({"A": ser})
|
| 617 |
+
result = df.shift(-1, axis=1, fill_value="foo")
|
| 618 |
+
expected = DataFrame({"A": ["foo", "foo"]})
|
| 619 |
+
tm.assert_frame_equal(result, expected)
|
| 620 |
+
|
| 621 |
+
# same thing but multiple blocks
|
| 622 |
+
df2 = DataFrame({"A": ser, "B": ser})
|
| 623 |
+
df2._consolidate_inplace()
|
| 624 |
+
|
| 625 |
+
result = df2.shift(-1, axis=1, fill_value="foo")
|
| 626 |
+
expected = DataFrame({"A": df2["B"], "B": ["foo", "foo"]})
|
| 627 |
+
tm.assert_frame_equal(result, expected)
|
| 628 |
+
|
| 629 |
+
# same thing but not consolidated
|
| 630 |
+
df3 = DataFrame({"A": ser})
|
| 631 |
+
df3["B"] = ser
|
| 632 |
+
assert len(df3._mgr.arrays) == 2
|
| 633 |
+
result = df3.shift(-1, axis=1, fill_value="foo")
|
| 634 |
+
tm.assert_frame_equal(result, expected)
|
| 635 |
+
|
| 636 |
+
def test_shift_axis1_categorical_columns(self):
|
| 637 |
+
# GH#38434
|
| 638 |
+
ci = CategoricalIndex(["a", "b", "c"])
|
| 639 |
+
df = DataFrame(
|
| 640 |
+
{"a": [1, 3], "b": [2, 4], "c": [5, 6]}, index=ci[:-1], columns=ci
|
| 641 |
+
)
|
| 642 |
+
result = df.shift(axis=1)
|
| 643 |
+
|
| 644 |
+
expected = DataFrame(
|
| 645 |
+
{"a": [np.nan, np.nan], "b": [1, 3], "c": [2, 4]}, index=ci[:-1], columns=ci
|
| 646 |
+
)
|
| 647 |
+
tm.assert_frame_equal(result, expected)
|
| 648 |
+
|
| 649 |
+
# periods != 1
|
| 650 |
+
result = df.shift(2, axis=1)
|
| 651 |
+
expected = DataFrame(
|
| 652 |
+
{"a": [np.nan, np.nan], "b": [np.nan, np.nan], "c": [1, 3]},
|
| 653 |
+
index=ci[:-1],
|
| 654 |
+
columns=ci,
|
| 655 |
+
)
|
| 656 |
+
tm.assert_frame_equal(result, expected)
|
| 657 |
+
|
| 658 |
+
def test_shift_axis1_many_periods(self):
|
| 659 |
+
# GH#44978 periods > len(columns)
|
| 660 |
+
df = DataFrame(np.random.default_rng(2).random((5, 3)))
|
| 661 |
+
shifted = df.shift(6, axis=1, fill_value=None)
|
| 662 |
+
|
| 663 |
+
expected = df * np.nan
|
| 664 |
+
tm.assert_frame_equal(shifted, expected)
|
| 665 |
+
|
| 666 |
+
shifted2 = df.shift(-6, axis=1, fill_value=None)
|
| 667 |
+
tm.assert_frame_equal(shifted2, expected)
|
| 668 |
+
|
| 669 |
+
def test_shift_with_offsets_freq(self):
|
| 670 |
+
df = DataFrame({"x": [1, 2, 3]}, index=date_range("2000", periods=3))
|
| 671 |
+
shifted = df.shift(freq="1MS")
|
| 672 |
+
expected = DataFrame(
|
| 673 |
+
{"x": [1, 2, 3]},
|
| 674 |
+
index=date_range(start="02/01/2000", end="02/01/2000", periods=3),
|
| 675 |
+
)
|
| 676 |
+
tm.assert_frame_equal(shifted, expected)
|
| 677 |
+
|
| 678 |
+
def test_shift_with_iterable_basic_functionality(self):
|
| 679 |
+
# GH#44424
|
| 680 |
+
data = {"a": [1, 2, 3], "b": [4, 5, 6]}
|
| 681 |
+
shifts = [0, 1, 2]
|
| 682 |
+
|
| 683 |
+
df = DataFrame(data)
|
| 684 |
+
shifted = df.shift(shifts)
|
| 685 |
+
|
| 686 |
+
expected = DataFrame(
|
| 687 |
+
{
|
| 688 |
+
"a_0": [1, 2, 3],
|
| 689 |
+
"b_0": [4, 5, 6],
|
| 690 |
+
"a_1": [np.nan, 1.0, 2.0],
|
| 691 |
+
"b_1": [np.nan, 4.0, 5.0],
|
| 692 |
+
"a_2": [np.nan, np.nan, 1.0],
|
| 693 |
+
"b_2": [np.nan, np.nan, 4.0],
|
| 694 |
+
}
|
| 695 |
+
)
|
| 696 |
+
tm.assert_frame_equal(expected, shifted)
|
| 697 |
+
|
| 698 |
+
def test_shift_with_iterable_series(self):
|
| 699 |
+
# GH#44424
|
| 700 |
+
data = {"a": [1, 2, 3]}
|
| 701 |
+
shifts = [0, 1, 2]
|
| 702 |
+
|
| 703 |
+
df = DataFrame(data)
|
| 704 |
+
s = df["a"]
|
| 705 |
+
tm.assert_frame_equal(s.shift(shifts), df.shift(shifts))
|
| 706 |
+
|
| 707 |
+
def test_shift_with_iterable_freq_and_fill_value(self):
|
| 708 |
+
# GH#44424
|
| 709 |
+
df = DataFrame(
|
| 710 |
+
np.random.default_rng(2).standard_normal(5),
|
| 711 |
+
index=date_range("1/1/2000", periods=5, freq="h"),
|
| 712 |
+
)
|
| 713 |
+
|
| 714 |
+
tm.assert_frame_equal(
|
| 715 |
+
# rename because shift with an iterable leads to str column names
|
| 716 |
+
df.shift([1], fill_value=1).rename(columns=lambda x: int(x[0])),
|
| 717 |
+
df.shift(1, fill_value=1),
|
| 718 |
+
)
|
| 719 |
+
|
| 720 |
+
tm.assert_frame_equal(
|
| 721 |
+
df.shift([1], freq="h").rename(columns=lambda x: int(x[0])),
|
| 722 |
+
df.shift(1, freq="h"),
|
| 723 |
+
)
|
| 724 |
+
|
| 725 |
+
msg = (
|
| 726 |
+
"Passing a 'freq' together with a 'fill_value' silently ignores the "
|
| 727 |
+
"fill_value"
|
| 728 |
+
)
|
| 729 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 730 |
+
df.shift([1, 2], fill_value=1, freq="h")
|
| 731 |
+
|
| 732 |
+
def test_shift_with_iterable_check_other_arguments(self):
|
| 733 |
+
# GH#44424
|
| 734 |
+
data = {"a": [1, 2], "b": [4, 5]}
|
| 735 |
+
shifts = [0, 1]
|
| 736 |
+
df = DataFrame(data)
|
| 737 |
+
|
| 738 |
+
# test suffix
|
| 739 |
+
shifted = df[["a"]].shift(shifts, suffix="_suffix")
|
| 740 |
+
expected = DataFrame({"a_suffix_0": [1, 2], "a_suffix_1": [np.nan, 1.0]})
|
| 741 |
+
tm.assert_frame_equal(shifted, expected)
|
| 742 |
+
|
| 743 |
+
# check bad inputs when doing multiple shifts
|
| 744 |
+
msg = "If `periods` contains multiple shifts, `axis` cannot be 1."
|
| 745 |
+
with pytest.raises(ValueError, match=msg):
|
| 746 |
+
df.shift(shifts, axis=1)
|
| 747 |
+
|
| 748 |
+
msg = "Periods must be integer, but s is <class 'str'>."
|
| 749 |
+
with pytest.raises(TypeError, match=msg):
|
| 750 |
+
df.shift(["s"])
|
| 751 |
+
|
| 752 |
+
msg = "If `periods` is an iterable, it cannot be empty."
|
| 753 |
+
with pytest.raises(ValueError, match=msg):
|
| 754 |
+
df.shift([])
|
| 755 |
+
|
| 756 |
+
msg = "Cannot specify `suffix` if `periods` is an int."
|
| 757 |
+
with pytest.raises(ValueError, match=msg):
|
| 758 |
+
df.shift(1, suffix="fails")
|
| 759 |
+
|
| 760 |
+
def test_shift_axis_one_empty(self):
|
| 761 |
+
# GH#57301
|
| 762 |
+
df = DataFrame()
|
| 763 |
+
result = df.shift(1, axis=1)
|
| 764 |
+
tm.assert_frame_equal(result, df)
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_swaplevel.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
|
| 3 |
+
from pandas import DataFrame
|
| 4 |
+
import pandas._testing as tm
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class TestSwaplevel:
|
| 8 |
+
def test_swaplevel(self, multiindex_dataframe_random_data):
|
| 9 |
+
frame = multiindex_dataframe_random_data
|
| 10 |
+
|
| 11 |
+
swapped = frame["A"].swaplevel()
|
| 12 |
+
swapped2 = frame["A"].swaplevel(0)
|
| 13 |
+
swapped3 = frame["A"].swaplevel(0, 1)
|
| 14 |
+
swapped4 = frame["A"].swaplevel("first", "second")
|
| 15 |
+
assert not swapped.index.equals(frame.index)
|
| 16 |
+
tm.assert_series_equal(swapped, swapped2)
|
| 17 |
+
tm.assert_series_equal(swapped, swapped3)
|
| 18 |
+
tm.assert_series_equal(swapped, swapped4)
|
| 19 |
+
|
| 20 |
+
back = swapped.swaplevel()
|
| 21 |
+
back2 = swapped.swaplevel(0)
|
| 22 |
+
back3 = swapped.swaplevel(0, 1)
|
| 23 |
+
back4 = swapped.swaplevel("second", "first")
|
| 24 |
+
assert back.index.equals(frame.index)
|
| 25 |
+
tm.assert_series_equal(back, back2)
|
| 26 |
+
tm.assert_series_equal(back, back3)
|
| 27 |
+
tm.assert_series_equal(back, back4)
|
| 28 |
+
|
| 29 |
+
ft = frame.T
|
| 30 |
+
swapped = ft.swaplevel("first", "second", axis=1)
|
| 31 |
+
exp = frame.swaplevel("first", "second").T
|
| 32 |
+
tm.assert_frame_equal(swapped, exp)
|
| 33 |
+
|
| 34 |
+
msg = "Can only swap levels on a hierarchical axis."
|
| 35 |
+
with pytest.raises(TypeError, match=msg):
|
| 36 |
+
DataFrame(range(3)).swaplevel()
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_to_dict_of_blocks.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pytest
|
| 3 |
+
|
| 4 |
+
from pandas._config import using_string_dtype
|
| 5 |
+
|
| 6 |
+
import pandas.util._test_decorators as td
|
| 7 |
+
|
| 8 |
+
from pandas import (
|
| 9 |
+
DataFrame,
|
| 10 |
+
MultiIndex,
|
| 11 |
+
)
|
| 12 |
+
import pandas._testing as tm
|
| 13 |
+
from pandas.core.arrays import NumpyExtensionArray
|
| 14 |
+
|
| 15 |
+
pytestmark = td.skip_array_manager_invalid_test
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class TestToDictOfBlocks:
|
| 19 |
+
@pytest.mark.filterwarnings("ignore:Setting a value on a view:FutureWarning")
|
| 20 |
+
def test_no_copy_blocks(self, float_frame, using_copy_on_write):
|
| 21 |
+
# GH#9607
|
| 22 |
+
df = DataFrame(float_frame, copy=True)
|
| 23 |
+
column = df.columns[0]
|
| 24 |
+
|
| 25 |
+
_last_df = None
|
| 26 |
+
# use the copy=False, change a column
|
| 27 |
+
blocks = df._to_dict_of_blocks()
|
| 28 |
+
for _df in blocks.values():
|
| 29 |
+
_last_df = _df
|
| 30 |
+
if column in _df:
|
| 31 |
+
_df.loc[:, column] = _df[column] + 1
|
| 32 |
+
|
| 33 |
+
if not using_copy_on_write:
|
| 34 |
+
# make sure we did change the original DataFrame
|
| 35 |
+
assert _last_df is not None and _last_df[column].equals(df[column])
|
| 36 |
+
else:
|
| 37 |
+
assert _last_df is not None and not _last_df[column].equals(df[column])
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
@pytest.mark.xfail(using_string_dtype(), reason="TODO(infer_string)")
|
| 41 |
+
def test_to_dict_of_blocks_item_cache(using_copy_on_write, warn_copy_on_write):
|
| 42 |
+
# Calling to_dict_of_blocks should not poison item_cache
|
| 43 |
+
df = DataFrame({"a": [1, 2, 3, 4], "b": ["a", "b", "c", "d"]})
|
| 44 |
+
df["c"] = NumpyExtensionArray(np.array([1, 2, None, 3], dtype=object))
|
| 45 |
+
mgr = df._mgr
|
| 46 |
+
assert len(mgr.blocks) == 3 # i.e. not consolidated
|
| 47 |
+
|
| 48 |
+
ser = df["b"] # populations item_cache["b"]
|
| 49 |
+
|
| 50 |
+
df._to_dict_of_blocks()
|
| 51 |
+
|
| 52 |
+
if using_copy_on_write:
|
| 53 |
+
with pytest.raises(ValueError, match="read-only"):
|
| 54 |
+
ser.values[0] = "foo"
|
| 55 |
+
elif warn_copy_on_write:
|
| 56 |
+
ser.values[0] = "foo"
|
| 57 |
+
assert df.loc[0, "b"] == "foo"
|
| 58 |
+
# with warning mode, the item cache is disabled
|
| 59 |
+
assert df["b"] is not ser
|
| 60 |
+
else:
|
| 61 |
+
# Check that the to_dict_of_blocks didn't break link between ser and df
|
| 62 |
+
ser.values[0] = "foo"
|
| 63 |
+
assert df.loc[0, "b"] == "foo"
|
| 64 |
+
|
| 65 |
+
assert df["b"] is ser
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def test_set_change_dtype_slice():
|
| 69 |
+
# GH#8850
|
| 70 |
+
cols = MultiIndex.from_tuples([("1st", "a"), ("2nd", "b"), ("3rd", "c")])
|
| 71 |
+
df = DataFrame([[1.0, 2, 3], [4.0, 5, 6]], columns=cols)
|
| 72 |
+
df["2nd"] = df["2nd"] * 2.0
|
| 73 |
+
|
| 74 |
+
blocks = df._to_dict_of_blocks()
|
| 75 |
+
assert sorted(blocks.keys()) == ["float64", "int64"]
|
| 76 |
+
tm.assert_frame_equal(
|
| 77 |
+
blocks["float64"], DataFrame([[1.0, 4.0], [4.0, 10.0]], columns=cols[:2])
|
| 78 |
+
)
|
| 79 |
+
tm.assert_frame_equal(blocks["int64"], DataFrame([[3], [6]], columns=cols[2:]))
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_tz_convert.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pytest
|
| 3 |
+
|
| 4 |
+
from pandas import (
|
| 5 |
+
DataFrame,
|
| 6 |
+
Index,
|
| 7 |
+
MultiIndex,
|
| 8 |
+
Series,
|
| 9 |
+
date_range,
|
| 10 |
+
)
|
| 11 |
+
import pandas._testing as tm
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class TestTZConvert:
|
| 15 |
+
def test_tz_convert(self, frame_or_series):
|
| 16 |
+
rng = date_range("1/1/2011", periods=200, freq="D", tz="US/Eastern")
|
| 17 |
+
|
| 18 |
+
obj = DataFrame({"a": 1}, index=rng)
|
| 19 |
+
obj = tm.get_obj(obj, frame_or_series)
|
| 20 |
+
|
| 21 |
+
result = obj.tz_convert("Europe/Berlin")
|
| 22 |
+
expected = DataFrame({"a": 1}, rng.tz_convert("Europe/Berlin"))
|
| 23 |
+
expected = tm.get_obj(expected, frame_or_series)
|
| 24 |
+
|
| 25 |
+
assert result.index.tz.zone == "Europe/Berlin"
|
| 26 |
+
tm.assert_equal(result, expected)
|
| 27 |
+
|
| 28 |
+
def test_tz_convert_axis1(self):
|
| 29 |
+
rng = date_range("1/1/2011", periods=200, freq="D", tz="US/Eastern")
|
| 30 |
+
|
| 31 |
+
obj = DataFrame({"a": 1}, index=rng)
|
| 32 |
+
|
| 33 |
+
obj = obj.T
|
| 34 |
+
result = obj.tz_convert("Europe/Berlin", axis=1)
|
| 35 |
+
assert result.columns.tz.zone == "Europe/Berlin"
|
| 36 |
+
|
| 37 |
+
expected = DataFrame({"a": 1}, rng.tz_convert("Europe/Berlin"))
|
| 38 |
+
|
| 39 |
+
tm.assert_equal(result, expected.T)
|
| 40 |
+
|
| 41 |
+
def test_tz_convert_naive(self, frame_or_series):
|
| 42 |
+
# can't convert tz-naive
|
| 43 |
+
rng = date_range("1/1/2011", periods=200, freq="D")
|
| 44 |
+
ts = Series(1, index=rng)
|
| 45 |
+
ts = frame_or_series(ts)
|
| 46 |
+
|
| 47 |
+
with pytest.raises(TypeError, match="Cannot convert tz-naive"):
|
| 48 |
+
ts.tz_convert("US/Eastern")
|
| 49 |
+
|
| 50 |
+
@pytest.mark.parametrize("fn", ["tz_localize", "tz_convert"])
|
| 51 |
+
def test_tz_convert_and_localize(self, fn):
|
| 52 |
+
l0 = date_range("20140701", periods=5, freq="D")
|
| 53 |
+
l1 = date_range("20140701", periods=5, freq="D")
|
| 54 |
+
|
| 55 |
+
int_idx = Index(range(5))
|
| 56 |
+
|
| 57 |
+
if fn == "tz_convert":
|
| 58 |
+
l0 = l0.tz_localize("UTC")
|
| 59 |
+
l1 = l1.tz_localize("UTC")
|
| 60 |
+
|
| 61 |
+
for idx in [l0, l1]:
|
| 62 |
+
l0_expected = getattr(idx, fn)("US/Pacific")
|
| 63 |
+
l1_expected = getattr(idx, fn)("US/Pacific")
|
| 64 |
+
|
| 65 |
+
df1 = DataFrame(np.ones(5), index=l0)
|
| 66 |
+
df1 = getattr(df1, fn)("US/Pacific")
|
| 67 |
+
tm.assert_index_equal(df1.index, l0_expected)
|
| 68 |
+
|
| 69 |
+
# MultiIndex
|
| 70 |
+
# GH7846
|
| 71 |
+
df2 = DataFrame(np.ones(5), MultiIndex.from_arrays([l0, l1]))
|
| 72 |
+
|
| 73 |
+
# freq is not preserved in MultiIndex construction
|
| 74 |
+
l1_expected = l1_expected._with_freq(None)
|
| 75 |
+
l0_expected = l0_expected._with_freq(None)
|
| 76 |
+
l1 = l1._with_freq(None)
|
| 77 |
+
l0 = l0._with_freq(None)
|
| 78 |
+
|
| 79 |
+
df3 = getattr(df2, fn)("US/Pacific", level=0)
|
| 80 |
+
assert not df3.index.levels[0].equals(l0)
|
| 81 |
+
tm.assert_index_equal(df3.index.levels[0], l0_expected)
|
| 82 |
+
tm.assert_index_equal(df3.index.levels[1], l1)
|
| 83 |
+
assert not df3.index.levels[1].equals(l1_expected)
|
| 84 |
+
|
| 85 |
+
df3 = getattr(df2, fn)("US/Pacific", level=1)
|
| 86 |
+
tm.assert_index_equal(df3.index.levels[0], l0)
|
| 87 |
+
assert not df3.index.levels[0].equals(l0_expected)
|
| 88 |
+
tm.assert_index_equal(df3.index.levels[1], l1_expected)
|
| 89 |
+
assert not df3.index.levels[1].equals(l1)
|
| 90 |
+
|
| 91 |
+
df4 = DataFrame(np.ones(5), MultiIndex.from_arrays([int_idx, l0]))
|
| 92 |
+
|
| 93 |
+
# TODO: untested
|
| 94 |
+
getattr(df4, fn)("US/Pacific", level=1)
|
| 95 |
+
|
| 96 |
+
tm.assert_index_equal(df3.index.levels[0], l0)
|
| 97 |
+
assert not df3.index.levels[0].equals(l0_expected)
|
| 98 |
+
tm.assert_index_equal(df3.index.levels[1], l1_expected)
|
| 99 |
+
assert not df3.index.levels[1].equals(l1)
|
| 100 |
+
|
| 101 |
+
# Bad Inputs
|
| 102 |
+
|
| 103 |
+
# Not DatetimeIndex / PeriodIndex
|
| 104 |
+
with pytest.raises(TypeError, match="DatetimeIndex"):
|
| 105 |
+
df = DataFrame(index=int_idx)
|
| 106 |
+
getattr(df, fn)("US/Pacific")
|
| 107 |
+
|
| 108 |
+
# Not DatetimeIndex / PeriodIndex
|
| 109 |
+
with pytest.raises(TypeError, match="DatetimeIndex"):
|
| 110 |
+
df = DataFrame(np.ones(5), MultiIndex.from_arrays([int_idx, l0]))
|
| 111 |
+
getattr(df, fn)("US/Pacific", level=0)
|
| 112 |
+
|
| 113 |
+
# Invalid level
|
| 114 |
+
with pytest.raises(ValueError, match="not valid"):
|
| 115 |
+
df = DataFrame(index=l0)
|
| 116 |
+
getattr(df, fn)("US/Pacific", level=1)
|
| 117 |
+
|
| 118 |
+
@pytest.mark.parametrize("copy", [True, False])
|
| 119 |
+
def test_tz_convert_copy_inplace_mutate(self, copy, frame_or_series):
|
| 120 |
+
# GH#6326
|
| 121 |
+
obj = frame_or_series(
|
| 122 |
+
np.arange(0, 5),
|
| 123 |
+
index=date_range("20131027", periods=5, freq="h", tz="Europe/Berlin"),
|
| 124 |
+
)
|
| 125 |
+
orig = obj.copy()
|
| 126 |
+
result = obj.tz_convert("UTC", copy=copy)
|
| 127 |
+
expected = frame_or_series(np.arange(0, 5), index=obj.index.tz_convert("UTC"))
|
| 128 |
+
tm.assert_equal(result, expected)
|
| 129 |
+
tm.assert_equal(obj, orig)
|
| 130 |
+
assert result.index is not obj.index
|
| 131 |
+
assert result is not obj
|
py311/lib/python3.11/site-packages/pandas/tests/frame/methods/test_values.py
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pytest
|
| 3 |
+
|
| 4 |
+
import pandas.util._test_decorators as td
|
| 5 |
+
|
| 6 |
+
from pandas import (
|
| 7 |
+
DataFrame,
|
| 8 |
+
NaT,
|
| 9 |
+
Series,
|
| 10 |
+
Timestamp,
|
| 11 |
+
date_range,
|
| 12 |
+
period_range,
|
| 13 |
+
)
|
| 14 |
+
import pandas._testing as tm
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class TestDataFrameValues:
|
| 18 |
+
@td.skip_array_manager_invalid_test
|
| 19 |
+
def test_values(self, float_frame, using_copy_on_write):
|
| 20 |
+
if using_copy_on_write:
|
| 21 |
+
with pytest.raises(ValueError, match="read-only"):
|
| 22 |
+
float_frame.values[:, 0] = 5.0
|
| 23 |
+
assert (float_frame.values[:, 0] != 5).all()
|
| 24 |
+
else:
|
| 25 |
+
float_frame.values[:, 0] = 5.0
|
| 26 |
+
assert (float_frame.values[:, 0] == 5).all()
|
| 27 |
+
|
| 28 |
+
def test_more_values(self, float_string_frame):
|
| 29 |
+
values = float_string_frame.values
|
| 30 |
+
assert values.shape[1] == len(float_string_frame.columns)
|
| 31 |
+
|
| 32 |
+
def test_values_mixed_dtypes(self, float_frame, float_string_frame):
|
| 33 |
+
frame = float_frame
|
| 34 |
+
arr = frame.values
|
| 35 |
+
|
| 36 |
+
frame_cols = frame.columns
|
| 37 |
+
for i, row in enumerate(arr):
|
| 38 |
+
for j, value in enumerate(row):
|
| 39 |
+
col = frame_cols[j]
|
| 40 |
+
if np.isnan(value):
|
| 41 |
+
assert np.isnan(frame[col].iloc[i])
|
| 42 |
+
else:
|
| 43 |
+
assert value == frame[col].iloc[i]
|
| 44 |
+
|
| 45 |
+
# mixed type
|
| 46 |
+
arr = float_string_frame[["foo", "A"]].values
|
| 47 |
+
assert arr[0, 0] == "bar"
|
| 48 |
+
|
| 49 |
+
df = DataFrame({"complex": [1j, 2j, 3j], "real": [1, 2, 3]})
|
| 50 |
+
arr = df.values
|
| 51 |
+
assert arr[0, 0] == 1j
|
| 52 |
+
|
| 53 |
+
def test_values_duplicates(self):
|
| 54 |
+
df = DataFrame(
|
| 55 |
+
[[1, 2, "a", "b"], [1, 2, "a", "b"]], columns=["one", "one", "two", "two"]
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
result = df.values
|
| 59 |
+
expected = np.array([[1, 2, "a", "b"], [1, 2, "a", "b"]], dtype=object)
|
| 60 |
+
|
| 61 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 62 |
+
|
| 63 |
+
def test_values_with_duplicate_columns(self):
|
| 64 |
+
df = DataFrame([[1, 2.5], [3, 4.5]], index=[1, 2], columns=["x", "x"])
|
| 65 |
+
result = df.values
|
| 66 |
+
expected = np.array([[1, 2.5], [3, 4.5]])
|
| 67 |
+
assert (result == expected).all().all()
|
| 68 |
+
|
| 69 |
+
@pytest.mark.parametrize("constructor", [date_range, period_range])
|
| 70 |
+
def test_values_casts_datetimelike_to_object(self, constructor):
|
| 71 |
+
series = Series(constructor("2000-01-01", periods=10, freq="D"))
|
| 72 |
+
|
| 73 |
+
expected = series.astype("object")
|
| 74 |
+
|
| 75 |
+
df = DataFrame(
|
| 76 |
+
{"a": series, "b": np.random.default_rng(2).standard_normal(len(series))}
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
result = df.values.squeeze()
|
| 80 |
+
assert (result[:, 0] == expected.values).all()
|
| 81 |
+
|
| 82 |
+
df = DataFrame({"a": series, "b": ["foo"] * len(series)})
|
| 83 |
+
|
| 84 |
+
result = df.values.squeeze()
|
| 85 |
+
assert (result[:, 0] == expected.values).all()
|
| 86 |
+
|
| 87 |
+
def test_frame_values_with_tz(self):
|
| 88 |
+
tz = "US/Central"
|
| 89 |
+
df = DataFrame({"A": date_range("2000", periods=4, tz=tz)})
|
| 90 |
+
result = df.values
|
| 91 |
+
expected = np.array(
|
| 92 |
+
[
|
| 93 |
+
[Timestamp("2000-01-01", tz=tz)],
|
| 94 |
+
[Timestamp("2000-01-02", tz=tz)],
|
| 95 |
+
[Timestamp("2000-01-03", tz=tz)],
|
| 96 |
+
[Timestamp("2000-01-04", tz=tz)],
|
| 97 |
+
]
|
| 98 |
+
)
|
| 99 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 100 |
+
|
| 101 |
+
# two columns, homogeneous
|
| 102 |
+
|
| 103 |
+
df["B"] = df["A"]
|
| 104 |
+
result = df.values
|
| 105 |
+
expected = np.concatenate([expected, expected], axis=1)
|
| 106 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 107 |
+
|
| 108 |
+
# three columns, heterogeneous
|
| 109 |
+
est = "US/Eastern"
|
| 110 |
+
df["C"] = df["A"].dt.tz_convert(est)
|
| 111 |
+
|
| 112 |
+
new = np.array(
|
| 113 |
+
[
|
| 114 |
+
[Timestamp("2000-01-01T01:00:00", tz=est)],
|
| 115 |
+
[Timestamp("2000-01-02T01:00:00", tz=est)],
|
| 116 |
+
[Timestamp("2000-01-03T01:00:00", tz=est)],
|
| 117 |
+
[Timestamp("2000-01-04T01:00:00", tz=est)],
|
| 118 |
+
]
|
| 119 |
+
)
|
| 120 |
+
expected = np.concatenate([expected, new], axis=1)
|
| 121 |
+
result = df.values
|
| 122 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 123 |
+
|
| 124 |
+
def test_interleave_with_tzaware(self, timezone_frame):
|
| 125 |
+
# interleave with object
|
| 126 |
+
result = timezone_frame.assign(D="foo").values
|
| 127 |
+
expected = np.array(
|
| 128 |
+
[
|
| 129 |
+
[
|
| 130 |
+
Timestamp("2013-01-01 00:00:00"),
|
| 131 |
+
Timestamp("2013-01-02 00:00:00"),
|
| 132 |
+
Timestamp("2013-01-03 00:00:00"),
|
| 133 |
+
],
|
| 134 |
+
[
|
| 135 |
+
Timestamp("2013-01-01 00:00:00-0500", tz="US/Eastern"),
|
| 136 |
+
NaT,
|
| 137 |
+
Timestamp("2013-01-03 00:00:00-0500", tz="US/Eastern"),
|
| 138 |
+
],
|
| 139 |
+
[
|
| 140 |
+
Timestamp("2013-01-01 00:00:00+0100", tz="CET"),
|
| 141 |
+
NaT,
|
| 142 |
+
Timestamp("2013-01-03 00:00:00+0100", tz="CET"),
|
| 143 |
+
],
|
| 144 |
+
["foo", "foo", "foo"],
|
| 145 |
+
],
|
| 146 |
+
dtype=object,
|
| 147 |
+
).T
|
| 148 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 149 |
+
|
| 150 |
+
# interleave with only datetime64[ns]
|
| 151 |
+
result = timezone_frame.values
|
| 152 |
+
expected = np.array(
|
| 153 |
+
[
|
| 154 |
+
[
|
| 155 |
+
Timestamp("2013-01-01 00:00:00"),
|
| 156 |
+
Timestamp("2013-01-02 00:00:00"),
|
| 157 |
+
Timestamp("2013-01-03 00:00:00"),
|
| 158 |
+
],
|
| 159 |
+
[
|
| 160 |
+
Timestamp("2013-01-01 00:00:00-0500", tz="US/Eastern"),
|
| 161 |
+
NaT,
|
| 162 |
+
Timestamp("2013-01-03 00:00:00-0500", tz="US/Eastern"),
|
| 163 |
+
],
|
| 164 |
+
[
|
| 165 |
+
Timestamp("2013-01-01 00:00:00+0100", tz="CET"),
|
| 166 |
+
NaT,
|
| 167 |
+
Timestamp("2013-01-03 00:00:00+0100", tz="CET"),
|
| 168 |
+
],
|
| 169 |
+
],
|
| 170 |
+
dtype=object,
|
| 171 |
+
).T
|
| 172 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 173 |
+
|
| 174 |
+
def test_values_interleave_non_unique_cols(self):
|
| 175 |
+
df = DataFrame(
|
| 176 |
+
[[Timestamp("20130101"), 3.5], [Timestamp("20130102"), 4.5]],
|
| 177 |
+
columns=["x", "x"],
|
| 178 |
+
index=[1, 2],
|
| 179 |
+
)
|
| 180 |
+
|
| 181 |
+
df_unique = df.copy()
|
| 182 |
+
df_unique.columns = ["x", "y"]
|
| 183 |
+
assert df_unique.values.shape == df.values.shape
|
| 184 |
+
tm.assert_numpy_array_equal(df_unique.values[0], df.values[0])
|
| 185 |
+
tm.assert_numpy_array_equal(df_unique.values[1], df.values[1])
|
| 186 |
+
|
| 187 |
+
def test_values_numeric_cols(self, float_frame):
|
| 188 |
+
float_frame["foo"] = "bar"
|
| 189 |
+
|
| 190 |
+
values = float_frame[["A", "B", "C", "D"]].values
|
| 191 |
+
assert values.dtype == np.float64
|
| 192 |
+
|
| 193 |
+
def test_values_lcd(self, mixed_float_frame, mixed_int_frame):
|
| 194 |
+
# mixed lcd
|
| 195 |
+
values = mixed_float_frame[["A", "B", "C", "D"]].values
|
| 196 |
+
assert values.dtype == np.float64
|
| 197 |
+
|
| 198 |
+
values = mixed_float_frame[["A", "B", "C"]].values
|
| 199 |
+
assert values.dtype == np.float32
|
| 200 |
+
|
| 201 |
+
values = mixed_float_frame[["C"]].values
|
| 202 |
+
assert values.dtype == np.float16
|
| 203 |
+
|
| 204 |
+
# GH#10364
|
| 205 |
+
# B uint64 forces float because there are other signed int types
|
| 206 |
+
values = mixed_int_frame[["A", "B", "C", "D"]].values
|
| 207 |
+
assert values.dtype == np.float64
|
| 208 |
+
|
| 209 |
+
values = mixed_int_frame[["A", "D"]].values
|
| 210 |
+
assert values.dtype == np.int64
|
| 211 |
+
|
| 212 |
+
# B uint64 forces float because there are other signed int types
|
| 213 |
+
values = mixed_int_frame[["A", "B", "C"]].values
|
| 214 |
+
assert values.dtype == np.float64
|
| 215 |
+
|
| 216 |
+
# as B and C are both unsigned, no forcing to float is needed
|
| 217 |
+
values = mixed_int_frame[["B", "C"]].values
|
| 218 |
+
assert values.dtype == np.uint64
|
| 219 |
+
|
| 220 |
+
values = mixed_int_frame[["A", "C"]].values
|
| 221 |
+
assert values.dtype == np.int32
|
| 222 |
+
|
| 223 |
+
values = mixed_int_frame[["C", "D"]].values
|
| 224 |
+
assert values.dtype == np.int64
|
| 225 |
+
|
| 226 |
+
values = mixed_int_frame[["A"]].values
|
| 227 |
+
assert values.dtype == np.int32
|
| 228 |
+
|
| 229 |
+
values = mixed_int_frame[["C"]].values
|
| 230 |
+
assert values.dtype == np.uint8
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
class TestPrivateValues:
|
| 234 |
+
@td.skip_array_manager_invalid_test
|
| 235 |
+
def test_private_values_dt64tz(self, using_copy_on_write):
|
| 236 |
+
dta = date_range("2000", periods=4, tz="US/Central")._data.reshape(-1, 1)
|
| 237 |
+
|
| 238 |
+
df = DataFrame(dta, columns=["A"])
|
| 239 |
+
tm.assert_equal(df._values, dta)
|
| 240 |
+
|
| 241 |
+
if using_copy_on_write:
|
| 242 |
+
assert not np.shares_memory(df._values._ndarray, dta._ndarray)
|
| 243 |
+
else:
|
| 244 |
+
# we have a view
|
| 245 |
+
assert np.shares_memory(df._values._ndarray, dta._ndarray)
|
| 246 |
+
|
| 247 |
+
# TimedeltaArray
|
| 248 |
+
tda = dta - dta
|
| 249 |
+
df2 = df - df
|
| 250 |
+
tm.assert_equal(df2._values, tda)
|
| 251 |
+
|
| 252 |
+
@td.skip_array_manager_invalid_test
|
| 253 |
+
def test_private_values_dt64tz_multicol(self, using_copy_on_write):
|
| 254 |
+
dta = date_range("2000", periods=8, tz="US/Central")._data.reshape(-1, 2)
|
| 255 |
+
|
| 256 |
+
df = DataFrame(dta, columns=["A", "B"])
|
| 257 |
+
tm.assert_equal(df._values, dta)
|
| 258 |
+
|
| 259 |
+
if using_copy_on_write:
|
| 260 |
+
assert not np.shares_memory(df._values._ndarray, dta._ndarray)
|
| 261 |
+
else:
|
| 262 |
+
# we have a view
|
| 263 |
+
assert np.shares_memory(df._values._ndarray, dta._ndarray)
|
| 264 |
+
|
| 265 |
+
# TimedeltaArray
|
| 266 |
+
tda = dta - dta
|
| 267 |
+
df2 = df - df
|
| 268 |
+
tm.assert_equal(df2._values, tda)
|
| 269 |
+
|
| 270 |
+
def test_private_values_dt64_multiblock(self):
|
| 271 |
+
dta = date_range("2000", periods=8)._data
|
| 272 |
+
|
| 273 |
+
df = DataFrame({"A": dta[:4]}, copy=False)
|
| 274 |
+
df["B"] = dta[4:]
|
| 275 |
+
|
| 276 |
+
assert len(df._mgr.arrays) == 2
|
| 277 |
+
|
| 278 |
+
result = df._values
|
| 279 |
+
expected = dta.reshape(2, 4).T
|
| 280 |
+
tm.assert_equal(result, expected)
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/interval/test_arithmetic.py
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import timedelta
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pytest
|
| 5 |
+
|
| 6 |
+
from pandas import (
|
| 7 |
+
Interval,
|
| 8 |
+
Timedelta,
|
| 9 |
+
Timestamp,
|
| 10 |
+
)
|
| 11 |
+
import pandas._testing as tm
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class TestIntervalArithmetic:
|
| 15 |
+
def test_interval_add(self, closed):
|
| 16 |
+
interval = Interval(0, 1, closed=closed)
|
| 17 |
+
expected = Interval(1, 2, closed=closed)
|
| 18 |
+
|
| 19 |
+
result = interval + 1
|
| 20 |
+
assert result == expected
|
| 21 |
+
|
| 22 |
+
result = 1 + interval
|
| 23 |
+
assert result == expected
|
| 24 |
+
|
| 25 |
+
result = interval
|
| 26 |
+
result += 1
|
| 27 |
+
assert result == expected
|
| 28 |
+
|
| 29 |
+
msg = r"unsupported operand type\(s\) for \+"
|
| 30 |
+
with pytest.raises(TypeError, match=msg):
|
| 31 |
+
interval + interval
|
| 32 |
+
|
| 33 |
+
with pytest.raises(TypeError, match=msg):
|
| 34 |
+
interval + "foo"
|
| 35 |
+
|
| 36 |
+
def test_interval_sub(self, closed):
|
| 37 |
+
interval = Interval(0, 1, closed=closed)
|
| 38 |
+
expected = Interval(-1, 0, closed=closed)
|
| 39 |
+
|
| 40 |
+
result = interval - 1
|
| 41 |
+
assert result == expected
|
| 42 |
+
|
| 43 |
+
result = interval
|
| 44 |
+
result -= 1
|
| 45 |
+
assert result == expected
|
| 46 |
+
|
| 47 |
+
msg = r"unsupported operand type\(s\) for -"
|
| 48 |
+
with pytest.raises(TypeError, match=msg):
|
| 49 |
+
interval - interval
|
| 50 |
+
|
| 51 |
+
with pytest.raises(TypeError, match=msg):
|
| 52 |
+
interval - "foo"
|
| 53 |
+
|
| 54 |
+
def test_interval_mult(self, closed):
|
| 55 |
+
interval = Interval(0, 1, closed=closed)
|
| 56 |
+
expected = Interval(0, 2, closed=closed)
|
| 57 |
+
|
| 58 |
+
result = interval * 2
|
| 59 |
+
assert result == expected
|
| 60 |
+
|
| 61 |
+
result = 2 * interval
|
| 62 |
+
assert result == expected
|
| 63 |
+
|
| 64 |
+
result = interval
|
| 65 |
+
result *= 2
|
| 66 |
+
assert result == expected
|
| 67 |
+
|
| 68 |
+
msg = r"unsupported operand type\(s\) for \*"
|
| 69 |
+
with pytest.raises(TypeError, match=msg):
|
| 70 |
+
interval * interval
|
| 71 |
+
|
| 72 |
+
msg = r"can\'t multiply sequence by non-int"
|
| 73 |
+
with pytest.raises(TypeError, match=msg):
|
| 74 |
+
interval * "foo"
|
| 75 |
+
|
| 76 |
+
def test_interval_div(self, closed):
|
| 77 |
+
interval = Interval(0, 1, closed=closed)
|
| 78 |
+
expected = Interval(0, 0.5, closed=closed)
|
| 79 |
+
|
| 80 |
+
result = interval / 2.0
|
| 81 |
+
assert result == expected
|
| 82 |
+
|
| 83 |
+
result = interval
|
| 84 |
+
result /= 2.0
|
| 85 |
+
assert result == expected
|
| 86 |
+
|
| 87 |
+
msg = r"unsupported operand type\(s\) for /"
|
| 88 |
+
with pytest.raises(TypeError, match=msg):
|
| 89 |
+
interval / interval
|
| 90 |
+
|
| 91 |
+
with pytest.raises(TypeError, match=msg):
|
| 92 |
+
interval / "foo"
|
| 93 |
+
|
| 94 |
+
def test_interval_floordiv(self, closed):
|
| 95 |
+
interval = Interval(1, 2, closed=closed)
|
| 96 |
+
expected = Interval(0, 1, closed=closed)
|
| 97 |
+
|
| 98 |
+
result = interval // 2
|
| 99 |
+
assert result == expected
|
| 100 |
+
|
| 101 |
+
result = interval
|
| 102 |
+
result //= 2
|
| 103 |
+
assert result == expected
|
| 104 |
+
|
| 105 |
+
msg = r"unsupported operand type\(s\) for //"
|
| 106 |
+
with pytest.raises(TypeError, match=msg):
|
| 107 |
+
interval // interval
|
| 108 |
+
|
| 109 |
+
with pytest.raises(TypeError, match=msg):
|
| 110 |
+
interval // "foo"
|
| 111 |
+
|
| 112 |
+
@pytest.mark.parametrize("method", ["__add__", "__sub__"])
|
| 113 |
+
@pytest.mark.parametrize(
|
| 114 |
+
"interval",
|
| 115 |
+
[
|
| 116 |
+
Interval(
|
| 117 |
+
Timestamp("2017-01-01 00:00:00"), Timestamp("2018-01-01 00:00:00")
|
| 118 |
+
),
|
| 119 |
+
Interval(Timedelta(days=7), Timedelta(days=14)),
|
| 120 |
+
],
|
| 121 |
+
)
|
| 122 |
+
@pytest.mark.parametrize(
|
| 123 |
+
"delta", [Timedelta(days=7), timedelta(7), np.timedelta64(7, "D")]
|
| 124 |
+
)
|
| 125 |
+
def test_time_interval_add_subtract_timedelta(self, interval, delta, method):
|
| 126 |
+
# https://github.com/pandas-dev/pandas/issues/32023
|
| 127 |
+
result = getattr(interval, method)(delta)
|
| 128 |
+
left = getattr(interval.left, method)(delta)
|
| 129 |
+
right = getattr(interval.right, method)(delta)
|
| 130 |
+
expected = Interval(left, right)
|
| 131 |
+
|
| 132 |
+
assert result == expected
|
| 133 |
+
|
| 134 |
+
@pytest.mark.parametrize("interval", [Interval(1, 2), Interval(1.0, 2.0)])
|
| 135 |
+
@pytest.mark.parametrize(
|
| 136 |
+
"delta", [Timedelta(days=7), timedelta(7), np.timedelta64(7, "D")]
|
| 137 |
+
)
|
| 138 |
+
def test_numeric_interval_add_timedelta_raises(self, interval, delta):
|
| 139 |
+
# https://github.com/pandas-dev/pandas/issues/32023
|
| 140 |
+
msg = "|".join(
|
| 141 |
+
[
|
| 142 |
+
"unsupported operand",
|
| 143 |
+
"cannot use operands",
|
| 144 |
+
"Only numeric, Timestamp and Timedelta endpoints are allowed",
|
| 145 |
+
]
|
| 146 |
+
)
|
| 147 |
+
with pytest.raises((TypeError, ValueError), match=msg):
|
| 148 |
+
interval + delta
|
| 149 |
+
|
| 150 |
+
with pytest.raises((TypeError, ValueError), match=msg):
|
| 151 |
+
delta + interval
|
| 152 |
+
|
| 153 |
+
@pytest.mark.parametrize("klass", [timedelta, np.timedelta64, Timedelta])
|
| 154 |
+
def test_timedelta_add_timestamp_interval(self, klass):
|
| 155 |
+
delta = klass(0)
|
| 156 |
+
expected = Interval(Timestamp("2020-01-01"), Timestamp("2020-02-01"))
|
| 157 |
+
|
| 158 |
+
result = delta + expected
|
| 159 |
+
assert result == expected
|
| 160 |
+
|
| 161 |
+
result = expected + delta
|
| 162 |
+
assert result == expected
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
class TestIntervalComparisons:
|
| 166 |
+
def test_interval_equal(self):
|
| 167 |
+
assert Interval(0, 1) == Interval(0, 1, closed="right")
|
| 168 |
+
assert Interval(0, 1) != Interval(0, 1, closed="left")
|
| 169 |
+
assert Interval(0, 1) != 0
|
| 170 |
+
|
| 171 |
+
def test_interval_comparison(self):
|
| 172 |
+
msg = (
|
| 173 |
+
"'<' not supported between instances of "
|
| 174 |
+
"'pandas._libs.interval.Interval' and 'int'"
|
| 175 |
+
)
|
| 176 |
+
with pytest.raises(TypeError, match=msg):
|
| 177 |
+
Interval(0, 1) < 2
|
| 178 |
+
|
| 179 |
+
assert Interval(0, 1) < Interval(1, 2)
|
| 180 |
+
assert Interval(0, 1) < Interval(0, 2)
|
| 181 |
+
assert Interval(0, 1) < Interval(0.5, 1.5)
|
| 182 |
+
assert Interval(0, 1) <= Interval(0, 1)
|
| 183 |
+
assert Interval(0, 1) > Interval(-1, 2)
|
| 184 |
+
assert Interval(0, 1) >= Interval(0, 1)
|
| 185 |
+
|
| 186 |
+
def test_equality_comparison_broadcasts_over_array(self):
|
| 187 |
+
# https://github.com/pandas-dev/pandas/issues/35931
|
| 188 |
+
interval = Interval(0, 1)
|
| 189 |
+
arr = np.array([interval, interval])
|
| 190 |
+
result = interval == arr
|
| 191 |
+
expected = np.array([True, True])
|
| 192 |
+
tm.assert_numpy_array_equal(result, expected)
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/period/__init__.py
ADDED
|
File without changes
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/period/test_arithmetic.py
ADDED
|
@@ -0,0 +1,486 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import timedelta
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pytest
|
| 5 |
+
|
| 6 |
+
from pandas._libs.tslibs.period import IncompatibleFrequency
|
| 7 |
+
|
| 8 |
+
from pandas import (
|
| 9 |
+
NaT,
|
| 10 |
+
Period,
|
| 11 |
+
Timedelta,
|
| 12 |
+
Timestamp,
|
| 13 |
+
offsets,
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class TestPeriodArithmetic:
|
| 18 |
+
def test_add_overflow_raises(self):
|
| 19 |
+
# GH#55503
|
| 20 |
+
per = Timestamp.max.to_period("ns")
|
| 21 |
+
|
| 22 |
+
msg = "|".join(
|
| 23 |
+
[
|
| 24 |
+
"Python int too large to convert to C long",
|
| 25 |
+
# windows, 32bit linux builds
|
| 26 |
+
"int too big to convert",
|
| 27 |
+
]
|
| 28 |
+
)
|
| 29 |
+
with pytest.raises(OverflowError, match=msg):
|
| 30 |
+
per + 1
|
| 31 |
+
|
| 32 |
+
msg = "value too large"
|
| 33 |
+
with pytest.raises(OverflowError, match=msg):
|
| 34 |
+
per + Timedelta(1)
|
| 35 |
+
with pytest.raises(OverflowError, match=msg):
|
| 36 |
+
per + offsets.Nano(1)
|
| 37 |
+
|
| 38 |
+
def test_period_add_integer(self):
|
| 39 |
+
per1 = Period(freq="D", year=2008, month=1, day=1)
|
| 40 |
+
per2 = Period(freq="D", year=2008, month=1, day=2)
|
| 41 |
+
assert per1 + 1 == per2
|
| 42 |
+
assert 1 + per1 == per2
|
| 43 |
+
|
| 44 |
+
def test_period_add_invalid(self):
|
| 45 |
+
# GH#4731
|
| 46 |
+
per1 = Period(freq="D", year=2008, month=1, day=1)
|
| 47 |
+
per2 = Period(freq="D", year=2008, month=1, day=2)
|
| 48 |
+
|
| 49 |
+
msg = "|".join(
|
| 50 |
+
[
|
| 51 |
+
r"unsupported operand type\(s\)",
|
| 52 |
+
"can only concatenate str",
|
| 53 |
+
"must be str, not Period",
|
| 54 |
+
]
|
| 55 |
+
)
|
| 56 |
+
with pytest.raises(TypeError, match=msg):
|
| 57 |
+
per1 + "str"
|
| 58 |
+
with pytest.raises(TypeError, match=msg):
|
| 59 |
+
"str" + per1
|
| 60 |
+
with pytest.raises(TypeError, match=msg):
|
| 61 |
+
per1 + per2
|
| 62 |
+
|
| 63 |
+
def test_period_sub_period_annual(self):
|
| 64 |
+
left, right = Period("2011", freq="Y"), Period("2007", freq="Y")
|
| 65 |
+
result = left - right
|
| 66 |
+
assert result == 4 * right.freq
|
| 67 |
+
|
| 68 |
+
msg = r"Input has different freq=M from Period\(freq=Y-DEC\)"
|
| 69 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 70 |
+
left - Period("2007-01", freq="M")
|
| 71 |
+
|
| 72 |
+
def test_period_sub_period(self):
|
| 73 |
+
per1 = Period("2011-01-01", freq="D")
|
| 74 |
+
per2 = Period("2011-01-15", freq="D")
|
| 75 |
+
|
| 76 |
+
off = per1.freq
|
| 77 |
+
assert per1 - per2 == -14 * off
|
| 78 |
+
assert per2 - per1 == 14 * off
|
| 79 |
+
|
| 80 |
+
msg = r"Input has different freq=M from Period\(freq=D\)"
|
| 81 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 82 |
+
per1 - Period("2011-02", freq="M")
|
| 83 |
+
|
| 84 |
+
@pytest.mark.parametrize("n", [1, 2, 3, 4])
|
| 85 |
+
def test_sub_n_gt_1_ticks(self, tick_classes, n):
|
| 86 |
+
# GH#23878
|
| 87 |
+
p1 = Period("19910905", freq=tick_classes(n))
|
| 88 |
+
p2 = Period("19920406", freq=tick_classes(n))
|
| 89 |
+
|
| 90 |
+
expected = Period(str(p2), freq=p2.freq.base) - Period(
|
| 91 |
+
str(p1), freq=p1.freq.base
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
assert (p2 - p1) == expected
|
| 95 |
+
|
| 96 |
+
@pytest.mark.parametrize("normalize", [True, False])
|
| 97 |
+
@pytest.mark.parametrize("n", [1, 2, 3, 4])
|
| 98 |
+
@pytest.mark.parametrize(
|
| 99 |
+
"offset, kwd_name",
|
| 100 |
+
[
|
| 101 |
+
(offsets.YearEnd, "month"),
|
| 102 |
+
(offsets.QuarterEnd, "startingMonth"),
|
| 103 |
+
(offsets.MonthEnd, None),
|
| 104 |
+
(offsets.Week, "weekday"),
|
| 105 |
+
],
|
| 106 |
+
)
|
| 107 |
+
def test_sub_n_gt_1_offsets(self, offset, kwd_name, n, normalize):
|
| 108 |
+
# GH#23878
|
| 109 |
+
kwds = {kwd_name: 3} if kwd_name is not None else {}
|
| 110 |
+
p1_d = "19910905"
|
| 111 |
+
p2_d = "19920406"
|
| 112 |
+
p1 = Period(p1_d, freq=offset(n, normalize, **kwds))
|
| 113 |
+
p2 = Period(p2_d, freq=offset(n, normalize, **kwds))
|
| 114 |
+
|
| 115 |
+
expected = Period(p2_d, freq=p2.freq.base) - Period(p1_d, freq=p1.freq.base)
|
| 116 |
+
|
| 117 |
+
assert (p2 - p1) == expected
|
| 118 |
+
|
| 119 |
+
def test_period_add_offset(self):
|
| 120 |
+
# freq is DateOffset
|
| 121 |
+
for freq in ["Y", "2Y", "3Y"]:
|
| 122 |
+
per = Period("2011", freq=freq)
|
| 123 |
+
exp = Period("2013", freq=freq)
|
| 124 |
+
assert per + offsets.YearEnd(2) == exp
|
| 125 |
+
assert offsets.YearEnd(2) + per == exp
|
| 126 |
+
|
| 127 |
+
for off in [
|
| 128 |
+
offsets.YearBegin(2),
|
| 129 |
+
offsets.MonthBegin(1),
|
| 130 |
+
offsets.Minute(),
|
| 131 |
+
np.timedelta64(365, "D"),
|
| 132 |
+
timedelta(365),
|
| 133 |
+
]:
|
| 134 |
+
msg = "Input has different freq|Input cannot be converted to Period"
|
| 135 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 136 |
+
per + off
|
| 137 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 138 |
+
off + per
|
| 139 |
+
|
| 140 |
+
for freq in ["M", "2M", "3M"]:
|
| 141 |
+
per = Period("2011-03", freq=freq)
|
| 142 |
+
exp = Period("2011-05", freq=freq)
|
| 143 |
+
assert per + offsets.MonthEnd(2) == exp
|
| 144 |
+
assert offsets.MonthEnd(2) + per == exp
|
| 145 |
+
|
| 146 |
+
exp = Period("2012-03", freq=freq)
|
| 147 |
+
assert per + offsets.MonthEnd(12) == exp
|
| 148 |
+
assert offsets.MonthEnd(12) + per == exp
|
| 149 |
+
|
| 150 |
+
msg = "|".join(
|
| 151 |
+
[
|
| 152 |
+
"Input has different freq",
|
| 153 |
+
"Input cannot be converted to Period",
|
| 154 |
+
]
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
for off in [
|
| 158 |
+
offsets.YearBegin(2),
|
| 159 |
+
offsets.MonthBegin(1),
|
| 160 |
+
offsets.Minute(),
|
| 161 |
+
np.timedelta64(365, "D"),
|
| 162 |
+
timedelta(365),
|
| 163 |
+
]:
|
| 164 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 165 |
+
per + off
|
| 166 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 167 |
+
off + per
|
| 168 |
+
|
| 169 |
+
# freq is Tick
|
| 170 |
+
for freq in ["D", "2D", "3D"]:
|
| 171 |
+
per = Period("2011-04-01", freq=freq)
|
| 172 |
+
|
| 173 |
+
exp = Period("2011-04-06", freq=freq)
|
| 174 |
+
assert per + offsets.Day(5) == exp
|
| 175 |
+
assert offsets.Day(5) + per == exp
|
| 176 |
+
|
| 177 |
+
exp = Period("2011-04-02", freq=freq)
|
| 178 |
+
assert per + offsets.Hour(24) == exp
|
| 179 |
+
assert offsets.Hour(24) + per == exp
|
| 180 |
+
|
| 181 |
+
exp = Period("2011-04-03", freq=freq)
|
| 182 |
+
assert per + np.timedelta64(2, "D") == exp
|
| 183 |
+
assert np.timedelta64(2, "D") + per == exp
|
| 184 |
+
|
| 185 |
+
exp = Period("2011-04-02", freq=freq)
|
| 186 |
+
assert per + np.timedelta64(3600 * 24, "s") == exp
|
| 187 |
+
assert np.timedelta64(3600 * 24, "s") + per == exp
|
| 188 |
+
|
| 189 |
+
exp = Period("2011-03-30", freq=freq)
|
| 190 |
+
assert per + timedelta(-2) == exp
|
| 191 |
+
assert timedelta(-2) + per == exp
|
| 192 |
+
|
| 193 |
+
exp = Period("2011-04-03", freq=freq)
|
| 194 |
+
assert per + timedelta(hours=48) == exp
|
| 195 |
+
assert timedelta(hours=48) + per == exp
|
| 196 |
+
|
| 197 |
+
msg = "|".join(
|
| 198 |
+
[
|
| 199 |
+
"Input has different freq",
|
| 200 |
+
"Input cannot be converted to Period",
|
| 201 |
+
]
|
| 202 |
+
)
|
| 203 |
+
|
| 204 |
+
for off in [
|
| 205 |
+
offsets.YearBegin(2),
|
| 206 |
+
offsets.MonthBegin(1),
|
| 207 |
+
offsets.Minute(),
|
| 208 |
+
np.timedelta64(4, "h"),
|
| 209 |
+
timedelta(hours=23),
|
| 210 |
+
]:
|
| 211 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 212 |
+
per + off
|
| 213 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 214 |
+
off + per
|
| 215 |
+
|
| 216 |
+
for freq in ["h", "2h", "3h"]:
|
| 217 |
+
per = Period("2011-04-01 09:00", freq=freq)
|
| 218 |
+
|
| 219 |
+
exp = Period("2011-04-03 09:00", freq=freq)
|
| 220 |
+
assert per + offsets.Day(2) == exp
|
| 221 |
+
assert offsets.Day(2) + per == exp
|
| 222 |
+
|
| 223 |
+
exp = Period("2011-04-01 12:00", freq=freq)
|
| 224 |
+
assert per + offsets.Hour(3) == exp
|
| 225 |
+
assert offsets.Hour(3) + per == exp
|
| 226 |
+
|
| 227 |
+
msg = "cannot use operands with types"
|
| 228 |
+
exp = Period("2011-04-01 12:00", freq=freq)
|
| 229 |
+
assert per + np.timedelta64(3, "h") == exp
|
| 230 |
+
assert np.timedelta64(3, "h") + per == exp
|
| 231 |
+
|
| 232 |
+
exp = Period("2011-04-01 10:00", freq=freq)
|
| 233 |
+
assert per + np.timedelta64(3600, "s") == exp
|
| 234 |
+
assert np.timedelta64(3600, "s") + per == exp
|
| 235 |
+
|
| 236 |
+
exp = Period("2011-04-01 11:00", freq=freq)
|
| 237 |
+
assert per + timedelta(minutes=120) == exp
|
| 238 |
+
assert timedelta(minutes=120) + per == exp
|
| 239 |
+
|
| 240 |
+
exp = Period("2011-04-05 12:00", freq=freq)
|
| 241 |
+
assert per + timedelta(days=4, minutes=180) == exp
|
| 242 |
+
assert timedelta(days=4, minutes=180) + per == exp
|
| 243 |
+
|
| 244 |
+
msg = "|".join(
|
| 245 |
+
[
|
| 246 |
+
"Input has different freq",
|
| 247 |
+
"Input cannot be converted to Period",
|
| 248 |
+
]
|
| 249 |
+
)
|
| 250 |
+
|
| 251 |
+
for off in [
|
| 252 |
+
offsets.YearBegin(2),
|
| 253 |
+
offsets.MonthBegin(1),
|
| 254 |
+
offsets.Minute(),
|
| 255 |
+
np.timedelta64(3200, "s"),
|
| 256 |
+
timedelta(hours=23, minutes=30),
|
| 257 |
+
]:
|
| 258 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 259 |
+
per + off
|
| 260 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 261 |
+
off + per
|
| 262 |
+
|
| 263 |
+
def test_period_sub_offset(self):
|
| 264 |
+
# freq is DateOffset
|
| 265 |
+
msg = "|".join(
|
| 266 |
+
[
|
| 267 |
+
"Input has different freq",
|
| 268 |
+
"Input cannot be converted to Period",
|
| 269 |
+
]
|
| 270 |
+
)
|
| 271 |
+
|
| 272 |
+
for freq in ["Y", "2Y", "3Y"]:
|
| 273 |
+
per = Period("2011", freq=freq)
|
| 274 |
+
assert per - offsets.YearEnd(2) == Period("2009", freq=freq)
|
| 275 |
+
|
| 276 |
+
for off in [
|
| 277 |
+
offsets.YearBegin(2),
|
| 278 |
+
offsets.MonthBegin(1),
|
| 279 |
+
offsets.Minute(),
|
| 280 |
+
np.timedelta64(365, "D"),
|
| 281 |
+
timedelta(365),
|
| 282 |
+
]:
|
| 283 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 284 |
+
per - off
|
| 285 |
+
|
| 286 |
+
for freq in ["M", "2M", "3M"]:
|
| 287 |
+
per = Period("2011-03", freq=freq)
|
| 288 |
+
assert per - offsets.MonthEnd(2) == Period("2011-01", freq=freq)
|
| 289 |
+
assert per - offsets.MonthEnd(12) == Period("2010-03", freq=freq)
|
| 290 |
+
|
| 291 |
+
for off in [
|
| 292 |
+
offsets.YearBegin(2),
|
| 293 |
+
offsets.MonthBegin(1),
|
| 294 |
+
offsets.Minute(),
|
| 295 |
+
np.timedelta64(365, "D"),
|
| 296 |
+
timedelta(365),
|
| 297 |
+
]:
|
| 298 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 299 |
+
per - off
|
| 300 |
+
|
| 301 |
+
# freq is Tick
|
| 302 |
+
for freq in ["D", "2D", "3D"]:
|
| 303 |
+
per = Period("2011-04-01", freq=freq)
|
| 304 |
+
assert per - offsets.Day(5) == Period("2011-03-27", freq=freq)
|
| 305 |
+
assert per - offsets.Hour(24) == Period("2011-03-31", freq=freq)
|
| 306 |
+
assert per - np.timedelta64(2, "D") == Period("2011-03-30", freq=freq)
|
| 307 |
+
assert per - np.timedelta64(3600 * 24, "s") == Period(
|
| 308 |
+
"2011-03-31", freq=freq
|
| 309 |
+
)
|
| 310 |
+
assert per - timedelta(-2) == Period("2011-04-03", freq=freq)
|
| 311 |
+
assert per - timedelta(hours=48) == Period("2011-03-30", freq=freq)
|
| 312 |
+
|
| 313 |
+
for off in [
|
| 314 |
+
offsets.YearBegin(2),
|
| 315 |
+
offsets.MonthBegin(1),
|
| 316 |
+
offsets.Minute(),
|
| 317 |
+
np.timedelta64(4, "h"),
|
| 318 |
+
timedelta(hours=23),
|
| 319 |
+
]:
|
| 320 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 321 |
+
per - off
|
| 322 |
+
|
| 323 |
+
for freq in ["h", "2h", "3h"]:
|
| 324 |
+
per = Period("2011-04-01 09:00", freq=freq)
|
| 325 |
+
assert per - offsets.Day(2) == Period("2011-03-30 09:00", freq=freq)
|
| 326 |
+
assert per - offsets.Hour(3) == Period("2011-04-01 06:00", freq=freq)
|
| 327 |
+
assert per - np.timedelta64(3, "h") == Period("2011-04-01 06:00", freq=freq)
|
| 328 |
+
assert per - np.timedelta64(3600, "s") == Period(
|
| 329 |
+
"2011-04-01 08:00", freq=freq
|
| 330 |
+
)
|
| 331 |
+
assert per - timedelta(minutes=120) == Period("2011-04-01 07:00", freq=freq)
|
| 332 |
+
assert per - timedelta(days=4, minutes=180) == Period(
|
| 333 |
+
"2011-03-28 06:00", freq=freq
|
| 334 |
+
)
|
| 335 |
+
|
| 336 |
+
for off in [
|
| 337 |
+
offsets.YearBegin(2),
|
| 338 |
+
offsets.MonthBegin(1),
|
| 339 |
+
offsets.Minute(),
|
| 340 |
+
np.timedelta64(3200, "s"),
|
| 341 |
+
timedelta(hours=23, minutes=30),
|
| 342 |
+
]:
|
| 343 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 344 |
+
per - off
|
| 345 |
+
|
| 346 |
+
@pytest.mark.parametrize("freq", ["M", "2M", "3M"])
|
| 347 |
+
def test_period_addsub_nat(self, freq):
|
| 348 |
+
# GH#13071
|
| 349 |
+
per = Period("2011-01", freq=freq)
|
| 350 |
+
|
| 351 |
+
# For subtraction, NaT is treated as another Period object
|
| 352 |
+
assert NaT - per is NaT
|
| 353 |
+
assert per - NaT is NaT
|
| 354 |
+
|
| 355 |
+
# For addition, NaT is treated as offset-like
|
| 356 |
+
assert NaT + per is NaT
|
| 357 |
+
assert per + NaT is NaT
|
| 358 |
+
|
| 359 |
+
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s", "m"])
|
| 360 |
+
def test_period_add_sub_td64_nat(self, unit):
|
| 361 |
+
# GH#47196
|
| 362 |
+
per = Period("2022-06-01", "D")
|
| 363 |
+
nat = np.timedelta64("NaT", unit)
|
| 364 |
+
|
| 365 |
+
assert per + nat is NaT
|
| 366 |
+
assert nat + per is NaT
|
| 367 |
+
assert per - nat is NaT
|
| 368 |
+
|
| 369 |
+
with pytest.raises(TypeError, match="unsupported operand"):
|
| 370 |
+
nat - per
|
| 371 |
+
|
| 372 |
+
def test_period_ops_offset(self):
|
| 373 |
+
per = Period("2011-04-01", freq="D")
|
| 374 |
+
result = per + offsets.Day()
|
| 375 |
+
exp = Period("2011-04-02", freq="D")
|
| 376 |
+
assert result == exp
|
| 377 |
+
|
| 378 |
+
result = per - offsets.Day(2)
|
| 379 |
+
exp = Period("2011-03-30", freq="D")
|
| 380 |
+
assert result == exp
|
| 381 |
+
|
| 382 |
+
msg = r"Input cannot be converted to Period\(freq=D\)"
|
| 383 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 384 |
+
per + offsets.Hour(2)
|
| 385 |
+
|
| 386 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 387 |
+
per - offsets.Hour(2)
|
| 388 |
+
|
| 389 |
+
def test_period_add_timestamp_raises(self):
|
| 390 |
+
# GH#17983
|
| 391 |
+
ts = Timestamp("2017")
|
| 392 |
+
per = Period("2017", freq="M")
|
| 393 |
+
|
| 394 |
+
msg = r"unsupported operand type\(s\) for \+: 'Timestamp' and 'Period'"
|
| 395 |
+
with pytest.raises(TypeError, match=msg):
|
| 396 |
+
ts + per
|
| 397 |
+
|
| 398 |
+
msg = r"unsupported operand type\(s\) for \+: 'Period' and 'Timestamp'"
|
| 399 |
+
with pytest.raises(TypeError, match=msg):
|
| 400 |
+
per + ts
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
class TestPeriodComparisons:
|
| 404 |
+
def test_period_comparison_same_freq(self):
|
| 405 |
+
jan = Period("2000-01", "M")
|
| 406 |
+
feb = Period("2000-02", "M")
|
| 407 |
+
|
| 408 |
+
assert not jan == feb
|
| 409 |
+
assert jan != feb
|
| 410 |
+
assert jan < feb
|
| 411 |
+
assert jan <= feb
|
| 412 |
+
assert not jan > feb
|
| 413 |
+
assert not jan >= feb
|
| 414 |
+
|
| 415 |
+
def test_period_comparison_same_period_different_object(self):
|
| 416 |
+
# Separate Period objects for the same period
|
| 417 |
+
left = Period("2000-01", "M")
|
| 418 |
+
right = Period("2000-01", "M")
|
| 419 |
+
|
| 420 |
+
assert left == right
|
| 421 |
+
assert left >= right
|
| 422 |
+
assert left <= right
|
| 423 |
+
assert not left < right
|
| 424 |
+
assert not left > right
|
| 425 |
+
|
| 426 |
+
def test_period_comparison_mismatched_freq(self):
|
| 427 |
+
jan = Period("2000-01", "M")
|
| 428 |
+
day = Period("2012-01-01", "D")
|
| 429 |
+
|
| 430 |
+
assert not jan == day
|
| 431 |
+
assert jan != day
|
| 432 |
+
msg = r"Input has different freq=D from Period\(freq=M\)"
|
| 433 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 434 |
+
jan < day
|
| 435 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 436 |
+
jan <= day
|
| 437 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 438 |
+
jan > day
|
| 439 |
+
with pytest.raises(IncompatibleFrequency, match=msg):
|
| 440 |
+
jan >= day
|
| 441 |
+
|
| 442 |
+
def test_period_comparison_invalid_type(self):
|
| 443 |
+
jan = Period("2000-01", "M")
|
| 444 |
+
|
| 445 |
+
assert not jan == 1
|
| 446 |
+
assert jan != 1
|
| 447 |
+
|
| 448 |
+
int_or_per = "'(Period|int)'"
|
| 449 |
+
msg = f"not supported between instances of {int_or_per} and {int_or_per}"
|
| 450 |
+
for left, right in [(jan, 1), (1, jan)]:
|
| 451 |
+
with pytest.raises(TypeError, match=msg):
|
| 452 |
+
left > right
|
| 453 |
+
with pytest.raises(TypeError, match=msg):
|
| 454 |
+
left >= right
|
| 455 |
+
with pytest.raises(TypeError, match=msg):
|
| 456 |
+
left < right
|
| 457 |
+
with pytest.raises(TypeError, match=msg):
|
| 458 |
+
left <= right
|
| 459 |
+
|
| 460 |
+
def test_period_comparison_nat(self):
|
| 461 |
+
per = Period("2011-01-01", freq="D")
|
| 462 |
+
|
| 463 |
+
ts = Timestamp("2011-01-01")
|
| 464 |
+
# confirm Period('NaT') work identical with Timestamp('NaT')
|
| 465 |
+
for left, right in [
|
| 466 |
+
(NaT, per),
|
| 467 |
+
(per, NaT),
|
| 468 |
+
(NaT, ts),
|
| 469 |
+
(ts, NaT),
|
| 470 |
+
]:
|
| 471 |
+
assert not left < right
|
| 472 |
+
assert not left > right
|
| 473 |
+
assert not left == right
|
| 474 |
+
assert left != right
|
| 475 |
+
assert not left <= right
|
| 476 |
+
assert not left >= right
|
| 477 |
+
|
| 478 |
+
@pytest.mark.parametrize(
|
| 479 |
+
"zerodim_arr, expected",
|
| 480 |
+
((np.array(0), False), (np.array(Period("2000-01", "M")), True)),
|
| 481 |
+
)
|
| 482 |
+
def test_period_comparison_numpy_zerodim_arr(self, zerodim_arr, expected):
|
| 483 |
+
per = Period("2000-01", "M")
|
| 484 |
+
|
| 485 |
+
assert (per == zerodim_arr) is expected
|
| 486 |
+
assert (zerodim_arr == per) is expected
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/period/test_asfreq.py
ADDED
|
@@ -0,0 +1,828 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
|
| 3 |
+
from pandas._libs.tslibs.period import INVALID_FREQ_ERR_MSG
|
| 4 |
+
from pandas.errors import OutOfBoundsDatetime
|
| 5 |
+
|
| 6 |
+
from pandas import (
|
| 7 |
+
Period,
|
| 8 |
+
Timestamp,
|
| 9 |
+
offsets,
|
| 10 |
+
)
|
| 11 |
+
import pandas._testing as tm
|
| 12 |
+
|
| 13 |
+
bday_msg = "Period with BDay freq is deprecated"
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class TestFreqConversion:
|
| 17 |
+
"""Test frequency conversion of date objects"""
|
| 18 |
+
|
| 19 |
+
@pytest.mark.filterwarnings("ignore:Period with BDay:FutureWarning")
|
| 20 |
+
@pytest.mark.parametrize("freq", ["Y", "Q", "M", "W", "B", "D"])
|
| 21 |
+
def test_asfreq_near_zero(self, freq):
|
| 22 |
+
# GH#19643, GH#19650
|
| 23 |
+
per = Period("0001-01-01", freq=freq)
|
| 24 |
+
tup1 = (per.year, per.hour, per.day)
|
| 25 |
+
|
| 26 |
+
prev = per - 1
|
| 27 |
+
assert prev.ordinal == per.ordinal - 1
|
| 28 |
+
tup2 = (prev.year, prev.month, prev.day)
|
| 29 |
+
assert tup2 < tup1
|
| 30 |
+
|
| 31 |
+
def test_asfreq_near_zero_weekly(self):
|
| 32 |
+
# GH#19834
|
| 33 |
+
per1 = Period("0001-01-01", "D") + 6
|
| 34 |
+
per2 = Period("0001-01-01", "D") - 6
|
| 35 |
+
week1 = per1.asfreq("W")
|
| 36 |
+
week2 = per2.asfreq("W")
|
| 37 |
+
assert week1 != week2
|
| 38 |
+
assert week1.asfreq("D", "E") >= per1
|
| 39 |
+
assert week2.asfreq("D", "S") <= per2
|
| 40 |
+
|
| 41 |
+
def test_to_timestamp_out_of_bounds(self):
|
| 42 |
+
# GH#19643, used to incorrectly give Timestamp in 1754
|
| 43 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 44 |
+
per = Period("0001-01-01", freq="B")
|
| 45 |
+
msg = "Out of bounds nanosecond timestamp"
|
| 46 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 47 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 48 |
+
per.to_timestamp()
|
| 49 |
+
|
| 50 |
+
def test_asfreq_corner(self):
|
| 51 |
+
val = Period(freq="Y", year=2007)
|
| 52 |
+
result1 = val.asfreq("5min")
|
| 53 |
+
result2 = val.asfreq("min")
|
| 54 |
+
expected = Period("2007-12-31 23:59", freq="min")
|
| 55 |
+
assert result1.ordinal == expected.ordinal
|
| 56 |
+
assert result1.freqstr == "5min"
|
| 57 |
+
assert result2.ordinal == expected.ordinal
|
| 58 |
+
assert result2.freqstr == "min"
|
| 59 |
+
|
| 60 |
+
def test_conv_annual(self):
|
| 61 |
+
# frequency conversion tests: from Annual Frequency
|
| 62 |
+
|
| 63 |
+
ival_A = Period(freq="Y", year=2007)
|
| 64 |
+
|
| 65 |
+
ival_AJAN = Period(freq="Y-JAN", year=2007)
|
| 66 |
+
ival_AJUN = Period(freq="Y-JUN", year=2007)
|
| 67 |
+
ival_ANOV = Period(freq="Y-NOV", year=2007)
|
| 68 |
+
|
| 69 |
+
ival_A_to_Q_start = Period(freq="Q", year=2007, quarter=1)
|
| 70 |
+
ival_A_to_Q_end = Period(freq="Q", year=2007, quarter=4)
|
| 71 |
+
ival_A_to_M_start = Period(freq="M", year=2007, month=1)
|
| 72 |
+
ival_A_to_M_end = Period(freq="M", year=2007, month=12)
|
| 73 |
+
ival_A_to_W_start = Period(freq="W", year=2007, month=1, day=1)
|
| 74 |
+
ival_A_to_W_end = Period(freq="W", year=2007, month=12, day=31)
|
| 75 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 76 |
+
ival_A_to_B_start = Period(freq="B", year=2007, month=1, day=1)
|
| 77 |
+
ival_A_to_B_end = Period(freq="B", year=2007, month=12, day=31)
|
| 78 |
+
ival_A_to_D_start = Period(freq="D", year=2007, month=1, day=1)
|
| 79 |
+
ival_A_to_D_end = Period(freq="D", year=2007, month=12, day=31)
|
| 80 |
+
ival_A_to_H_start = Period(freq="h", year=2007, month=1, day=1, hour=0)
|
| 81 |
+
ival_A_to_H_end = Period(freq="h", year=2007, month=12, day=31, hour=23)
|
| 82 |
+
ival_A_to_T_start = Period(
|
| 83 |
+
freq="Min", year=2007, month=1, day=1, hour=0, minute=0
|
| 84 |
+
)
|
| 85 |
+
ival_A_to_T_end = Period(
|
| 86 |
+
freq="Min", year=2007, month=12, day=31, hour=23, minute=59
|
| 87 |
+
)
|
| 88 |
+
ival_A_to_S_start = Period(
|
| 89 |
+
freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
| 90 |
+
)
|
| 91 |
+
ival_A_to_S_end = Period(
|
| 92 |
+
freq="s", year=2007, month=12, day=31, hour=23, minute=59, second=59
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
ival_AJAN_to_D_end = Period(freq="D", year=2007, month=1, day=31)
|
| 96 |
+
ival_AJAN_to_D_start = Period(freq="D", year=2006, month=2, day=1)
|
| 97 |
+
ival_AJUN_to_D_end = Period(freq="D", year=2007, month=6, day=30)
|
| 98 |
+
ival_AJUN_to_D_start = Period(freq="D", year=2006, month=7, day=1)
|
| 99 |
+
ival_ANOV_to_D_end = Period(freq="D", year=2007, month=11, day=30)
|
| 100 |
+
ival_ANOV_to_D_start = Period(freq="D", year=2006, month=12, day=1)
|
| 101 |
+
|
| 102 |
+
assert ival_A.asfreq("Q", "s") == ival_A_to_Q_start
|
| 103 |
+
assert ival_A.asfreq("Q", "e") == ival_A_to_Q_end
|
| 104 |
+
assert ival_A.asfreq("M", "s") == ival_A_to_M_start
|
| 105 |
+
assert ival_A.asfreq("M", "E") == ival_A_to_M_end
|
| 106 |
+
assert ival_A.asfreq("W", "s") == ival_A_to_W_start
|
| 107 |
+
assert ival_A.asfreq("W", "E") == ival_A_to_W_end
|
| 108 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 109 |
+
assert ival_A.asfreq("B", "s") == ival_A_to_B_start
|
| 110 |
+
assert ival_A.asfreq("B", "E") == ival_A_to_B_end
|
| 111 |
+
assert ival_A.asfreq("D", "s") == ival_A_to_D_start
|
| 112 |
+
assert ival_A.asfreq("D", "E") == ival_A_to_D_end
|
| 113 |
+
msg = "'H' is deprecated and will be removed in a future version."
|
| 114 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 115 |
+
assert ival_A.asfreq("H", "s") == ival_A_to_H_start
|
| 116 |
+
assert ival_A.asfreq("H", "E") == ival_A_to_H_end
|
| 117 |
+
assert ival_A.asfreq("min", "s") == ival_A_to_T_start
|
| 118 |
+
assert ival_A.asfreq("min", "E") == ival_A_to_T_end
|
| 119 |
+
msg = "'T' is deprecated and will be removed in a future version."
|
| 120 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 121 |
+
assert ival_A.asfreq("T", "s") == ival_A_to_T_start
|
| 122 |
+
assert ival_A.asfreq("T", "E") == ival_A_to_T_end
|
| 123 |
+
msg = "'S' is deprecated and will be removed in a future version."
|
| 124 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 125 |
+
assert ival_A.asfreq("S", "S") == ival_A_to_S_start
|
| 126 |
+
assert ival_A.asfreq("S", "E") == ival_A_to_S_end
|
| 127 |
+
|
| 128 |
+
assert ival_AJAN.asfreq("D", "s") == ival_AJAN_to_D_start
|
| 129 |
+
assert ival_AJAN.asfreq("D", "E") == ival_AJAN_to_D_end
|
| 130 |
+
|
| 131 |
+
assert ival_AJUN.asfreq("D", "s") == ival_AJUN_to_D_start
|
| 132 |
+
assert ival_AJUN.asfreq("D", "E") == ival_AJUN_to_D_end
|
| 133 |
+
|
| 134 |
+
assert ival_ANOV.asfreq("D", "s") == ival_ANOV_to_D_start
|
| 135 |
+
assert ival_ANOV.asfreq("D", "E") == ival_ANOV_to_D_end
|
| 136 |
+
|
| 137 |
+
assert ival_A.asfreq("Y") == ival_A
|
| 138 |
+
|
| 139 |
+
def test_conv_quarterly(self):
|
| 140 |
+
# frequency conversion tests: from Quarterly Frequency
|
| 141 |
+
|
| 142 |
+
ival_Q = Period(freq="Q", year=2007, quarter=1)
|
| 143 |
+
ival_Q_end_of_year = Period(freq="Q", year=2007, quarter=4)
|
| 144 |
+
|
| 145 |
+
ival_QEJAN = Period(freq="Q-JAN", year=2007, quarter=1)
|
| 146 |
+
ival_QEJUN = Period(freq="Q-JUN", year=2007, quarter=1)
|
| 147 |
+
|
| 148 |
+
ival_Q_to_A = Period(freq="Y", year=2007)
|
| 149 |
+
ival_Q_to_M_start = Period(freq="M", year=2007, month=1)
|
| 150 |
+
ival_Q_to_M_end = Period(freq="M", year=2007, month=3)
|
| 151 |
+
ival_Q_to_W_start = Period(freq="W", year=2007, month=1, day=1)
|
| 152 |
+
ival_Q_to_W_end = Period(freq="W", year=2007, month=3, day=31)
|
| 153 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 154 |
+
ival_Q_to_B_start = Period(freq="B", year=2007, month=1, day=1)
|
| 155 |
+
ival_Q_to_B_end = Period(freq="B", year=2007, month=3, day=30)
|
| 156 |
+
ival_Q_to_D_start = Period(freq="D", year=2007, month=1, day=1)
|
| 157 |
+
ival_Q_to_D_end = Period(freq="D", year=2007, month=3, day=31)
|
| 158 |
+
ival_Q_to_H_start = Period(freq="h", year=2007, month=1, day=1, hour=0)
|
| 159 |
+
ival_Q_to_H_end = Period(freq="h", year=2007, month=3, day=31, hour=23)
|
| 160 |
+
ival_Q_to_T_start = Period(
|
| 161 |
+
freq="Min", year=2007, month=1, day=1, hour=0, minute=0
|
| 162 |
+
)
|
| 163 |
+
ival_Q_to_T_end = Period(
|
| 164 |
+
freq="Min", year=2007, month=3, day=31, hour=23, minute=59
|
| 165 |
+
)
|
| 166 |
+
ival_Q_to_S_start = Period(
|
| 167 |
+
freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
| 168 |
+
)
|
| 169 |
+
ival_Q_to_S_end = Period(
|
| 170 |
+
freq="s", year=2007, month=3, day=31, hour=23, minute=59, second=59
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
ival_QEJAN_to_D_start = Period(freq="D", year=2006, month=2, day=1)
|
| 174 |
+
ival_QEJAN_to_D_end = Period(freq="D", year=2006, month=4, day=30)
|
| 175 |
+
|
| 176 |
+
ival_QEJUN_to_D_start = Period(freq="D", year=2006, month=7, day=1)
|
| 177 |
+
ival_QEJUN_to_D_end = Period(freq="D", year=2006, month=9, day=30)
|
| 178 |
+
|
| 179 |
+
assert ival_Q.asfreq("Y") == ival_Q_to_A
|
| 180 |
+
assert ival_Q_end_of_year.asfreq("Y") == ival_Q_to_A
|
| 181 |
+
|
| 182 |
+
assert ival_Q.asfreq("M", "s") == ival_Q_to_M_start
|
| 183 |
+
assert ival_Q.asfreq("M", "E") == ival_Q_to_M_end
|
| 184 |
+
assert ival_Q.asfreq("W", "s") == ival_Q_to_W_start
|
| 185 |
+
assert ival_Q.asfreq("W", "E") == ival_Q_to_W_end
|
| 186 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 187 |
+
assert ival_Q.asfreq("B", "s") == ival_Q_to_B_start
|
| 188 |
+
assert ival_Q.asfreq("B", "E") == ival_Q_to_B_end
|
| 189 |
+
assert ival_Q.asfreq("D", "s") == ival_Q_to_D_start
|
| 190 |
+
assert ival_Q.asfreq("D", "E") == ival_Q_to_D_end
|
| 191 |
+
assert ival_Q.asfreq("h", "s") == ival_Q_to_H_start
|
| 192 |
+
assert ival_Q.asfreq("h", "E") == ival_Q_to_H_end
|
| 193 |
+
assert ival_Q.asfreq("Min", "s") == ival_Q_to_T_start
|
| 194 |
+
assert ival_Q.asfreq("Min", "E") == ival_Q_to_T_end
|
| 195 |
+
assert ival_Q.asfreq("s", "s") == ival_Q_to_S_start
|
| 196 |
+
assert ival_Q.asfreq("s", "E") == ival_Q_to_S_end
|
| 197 |
+
|
| 198 |
+
assert ival_QEJAN.asfreq("D", "s") == ival_QEJAN_to_D_start
|
| 199 |
+
assert ival_QEJAN.asfreq("D", "E") == ival_QEJAN_to_D_end
|
| 200 |
+
assert ival_QEJUN.asfreq("D", "s") == ival_QEJUN_to_D_start
|
| 201 |
+
assert ival_QEJUN.asfreq("D", "E") == ival_QEJUN_to_D_end
|
| 202 |
+
|
| 203 |
+
assert ival_Q.asfreq("Q") == ival_Q
|
| 204 |
+
|
| 205 |
+
def test_conv_monthly(self):
|
| 206 |
+
# frequency conversion tests: from Monthly Frequency
|
| 207 |
+
|
| 208 |
+
ival_M = Period(freq="M", year=2007, month=1)
|
| 209 |
+
ival_M_end_of_year = Period(freq="M", year=2007, month=12)
|
| 210 |
+
ival_M_end_of_quarter = Period(freq="M", year=2007, month=3)
|
| 211 |
+
ival_M_to_A = Period(freq="Y", year=2007)
|
| 212 |
+
ival_M_to_Q = Period(freq="Q", year=2007, quarter=1)
|
| 213 |
+
ival_M_to_W_start = Period(freq="W", year=2007, month=1, day=1)
|
| 214 |
+
ival_M_to_W_end = Period(freq="W", year=2007, month=1, day=31)
|
| 215 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 216 |
+
ival_M_to_B_start = Period(freq="B", year=2007, month=1, day=1)
|
| 217 |
+
ival_M_to_B_end = Period(freq="B", year=2007, month=1, day=31)
|
| 218 |
+
ival_M_to_D_start = Period(freq="D", year=2007, month=1, day=1)
|
| 219 |
+
ival_M_to_D_end = Period(freq="D", year=2007, month=1, day=31)
|
| 220 |
+
ival_M_to_H_start = Period(freq="h", year=2007, month=1, day=1, hour=0)
|
| 221 |
+
ival_M_to_H_end = Period(freq="h", year=2007, month=1, day=31, hour=23)
|
| 222 |
+
ival_M_to_T_start = Period(
|
| 223 |
+
freq="Min", year=2007, month=1, day=1, hour=0, minute=0
|
| 224 |
+
)
|
| 225 |
+
ival_M_to_T_end = Period(
|
| 226 |
+
freq="Min", year=2007, month=1, day=31, hour=23, minute=59
|
| 227 |
+
)
|
| 228 |
+
ival_M_to_S_start = Period(
|
| 229 |
+
freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
| 230 |
+
)
|
| 231 |
+
ival_M_to_S_end = Period(
|
| 232 |
+
freq="s", year=2007, month=1, day=31, hour=23, minute=59, second=59
|
| 233 |
+
)
|
| 234 |
+
|
| 235 |
+
assert ival_M.asfreq("Y") == ival_M_to_A
|
| 236 |
+
assert ival_M_end_of_year.asfreq("Y") == ival_M_to_A
|
| 237 |
+
assert ival_M.asfreq("Q") == ival_M_to_Q
|
| 238 |
+
assert ival_M_end_of_quarter.asfreq("Q") == ival_M_to_Q
|
| 239 |
+
|
| 240 |
+
assert ival_M.asfreq("W", "s") == ival_M_to_W_start
|
| 241 |
+
assert ival_M.asfreq("W", "E") == ival_M_to_W_end
|
| 242 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 243 |
+
assert ival_M.asfreq("B", "s") == ival_M_to_B_start
|
| 244 |
+
assert ival_M.asfreq("B", "E") == ival_M_to_B_end
|
| 245 |
+
assert ival_M.asfreq("D", "s") == ival_M_to_D_start
|
| 246 |
+
assert ival_M.asfreq("D", "E") == ival_M_to_D_end
|
| 247 |
+
assert ival_M.asfreq("h", "s") == ival_M_to_H_start
|
| 248 |
+
assert ival_M.asfreq("h", "E") == ival_M_to_H_end
|
| 249 |
+
assert ival_M.asfreq("Min", "s") == ival_M_to_T_start
|
| 250 |
+
assert ival_M.asfreq("Min", "E") == ival_M_to_T_end
|
| 251 |
+
assert ival_M.asfreq("s", "s") == ival_M_to_S_start
|
| 252 |
+
assert ival_M.asfreq("s", "E") == ival_M_to_S_end
|
| 253 |
+
|
| 254 |
+
assert ival_M.asfreq("M") == ival_M
|
| 255 |
+
|
| 256 |
+
def test_conv_weekly(self):
|
| 257 |
+
# frequency conversion tests: from Weekly Frequency
|
| 258 |
+
ival_W = Period(freq="W", year=2007, month=1, day=1)
|
| 259 |
+
|
| 260 |
+
ival_WSUN = Period(freq="W", year=2007, month=1, day=7)
|
| 261 |
+
ival_WSAT = Period(freq="W-SAT", year=2007, month=1, day=6)
|
| 262 |
+
ival_WFRI = Period(freq="W-FRI", year=2007, month=1, day=5)
|
| 263 |
+
ival_WTHU = Period(freq="W-THU", year=2007, month=1, day=4)
|
| 264 |
+
ival_WWED = Period(freq="W-WED", year=2007, month=1, day=3)
|
| 265 |
+
ival_WTUE = Period(freq="W-TUE", year=2007, month=1, day=2)
|
| 266 |
+
ival_WMON = Period(freq="W-MON", year=2007, month=1, day=1)
|
| 267 |
+
|
| 268 |
+
ival_WSUN_to_D_start = Period(freq="D", year=2007, month=1, day=1)
|
| 269 |
+
ival_WSUN_to_D_end = Period(freq="D", year=2007, month=1, day=7)
|
| 270 |
+
ival_WSAT_to_D_start = Period(freq="D", year=2006, month=12, day=31)
|
| 271 |
+
ival_WSAT_to_D_end = Period(freq="D", year=2007, month=1, day=6)
|
| 272 |
+
ival_WFRI_to_D_start = Period(freq="D", year=2006, month=12, day=30)
|
| 273 |
+
ival_WFRI_to_D_end = Period(freq="D", year=2007, month=1, day=5)
|
| 274 |
+
ival_WTHU_to_D_start = Period(freq="D", year=2006, month=12, day=29)
|
| 275 |
+
ival_WTHU_to_D_end = Period(freq="D", year=2007, month=1, day=4)
|
| 276 |
+
ival_WWED_to_D_start = Period(freq="D", year=2006, month=12, day=28)
|
| 277 |
+
ival_WWED_to_D_end = Period(freq="D", year=2007, month=1, day=3)
|
| 278 |
+
ival_WTUE_to_D_start = Period(freq="D", year=2006, month=12, day=27)
|
| 279 |
+
ival_WTUE_to_D_end = Period(freq="D", year=2007, month=1, day=2)
|
| 280 |
+
ival_WMON_to_D_start = Period(freq="D", year=2006, month=12, day=26)
|
| 281 |
+
ival_WMON_to_D_end = Period(freq="D", year=2007, month=1, day=1)
|
| 282 |
+
|
| 283 |
+
ival_W_end_of_year = Period(freq="W", year=2007, month=12, day=31)
|
| 284 |
+
ival_W_end_of_quarter = Period(freq="W", year=2007, month=3, day=31)
|
| 285 |
+
ival_W_end_of_month = Period(freq="W", year=2007, month=1, day=31)
|
| 286 |
+
ival_W_to_A = Period(freq="Y", year=2007)
|
| 287 |
+
ival_W_to_Q = Period(freq="Q", year=2007, quarter=1)
|
| 288 |
+
ival_W_to_M = Period(freq="M", year=2007, month=1)
|
| 289 |
+
|
| 290 |
+
if Period(freq="D", year=2007, month=12, day=31).weekday == 6:
|
| 291 |
+
ival_W_to_A_end_of_year = Period(freq="Y", year=2007)
|
| 292 |
+
else:
|
| 293 |
+
ival_W_to_A_end_of_year = Period(freq="Y", year=2008)
|
| 294 |
+
|
| 295 |
+
if Period(freq="D", year=2007, month=3, day=31).weekday == 6:
|
| 296 |
+
ival_W_to_Q_end_of_quarter = Period(freq="Q", year=2007, quarter=1)
|
| 297 |
+
else:
|
| 298 |
+
ival_W_to_Q_end_of_quarter = Period(freq="Q", year=2007, quarter=2)
|
| 299 |
+
|
| 300 |
+
if Period(freq="D", year=2007, month=1, day=31).weekday == 6:
|
| 301 |
+
ival_W_to_M_end_of_month = Period(freq="M", year=2007, month=1)
|
| 302 |
+
else:
|
| 303 |
+
ival_W_to_M_end_of_month = Period(freq="M", year=2007, month=2)
|
| 304 |
+
|
| 305 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 306 |
+
ival_W_to_B_start = Period(freq="B", year=2007, month=1, day=1)
|
| 307 |
+
ival_W_to_B_end = Period(freq="B", year=2007, month=1, day=5)
|
| 308 |
+
ival_W_to_D_start = Period(freq="D", year=2007, month=1, day=1)
|
| 309 |
+
ival_W_to_D_end = Period(freq="D", year=2007, month=1, day=7)
|
| 310 |
+
ival_W_to_H_start = Period(freq="h", year=2007, month=1, day=1, hour=0)
|
| 311 |
+
ival_W_to_H_end = Period(freq="h", year=2007, month=1, day=7, hour=23)
|
| 312 |
+
ival_W_to_T_start = Period(
|
| 313 |
+
freq="Min", year=2007, month=1, day=1, hour=0, minute=0
|
| 314 |
+
)
|
| 315 |
+
ival_W_to_T_end = Period(
|
| 316 |
+
freq="Min", year=2007, month=1, day=7, hour=23, minute=59
|
| 317 |
+
)
|
| 318 |
+
ival_W_to_S_start = Period(
|
| 319 |
+
freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
| 320 |
+
)
|
| 321 |
+
ival_W_to_S_end = Period(
|
| 322 |
+
freq="s", year=2007, month=1, day=7, hour=23, minute=59, second=59
|
| 323 |
+
)
|
| 324 |
+
|
| 325 |
+
assert ival_W.asfreq("Y") == ival_W_to_A
|
| 326 |
+
assert ival_W_end_of_year.asfreq("Y") == ival_W_to_A_end_of_year
|
| 327 |
+
|
| 328 |
+
assert ival_W.asfreq("Q") == ival_W_to_Q
|
| 329 |
+
assert ival_W_end_of_quarter.asfreq("Q") == ival_W_to_Q_end_of_quarter
|
| 330 |
+
|
| 331 |
+
assert ival_W.asfreq("M") == ival_W_to_M
|
| 332 |
+
assert ival_W_end_of_month.asfreq("M") == ival_W_to_M_end_of_month
|
| 333 |
+
|
| 334 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 335 |
+
assert ival_W.asfreq("B", "s") == ival_W_to_B_start
|
| 336 |
+
assert ival_W.asfreq("B", "E") == ival_W_to_B_end
|
| 337 |
+
|
| 338 |
+
assert ival_W.asfreq("D", "s") == ival_W_to_D_start
|
| 339 |
+
assert ival_W.asfreq("D", "E") == ival_W_to_D_end
|
| 340 |
+
|
| 341 |
+
assert ival_WSUN.asfreq("D", "s") == ival_WSUN_to_D_start
|
| 342 |
+
assert ival_WSUN.asfreq("D", "E") == ival_WSUN_to_D_end
|
| 343 |
+
assert ival_WSAT.asfreq("D", "s") == ival_WSAT_to_D_start
|
| 344 |
+
assert ival_WSAT.asfreq("D", "E") == ival_WSAT_to_D_end
|
| 345 |
+
assert ival_WFRI.asfreq("D", "s") == ival_WFRI_to_D_start
|
| 346 |
+
assert ival_WFRI.asfreq("D", "E") == ival_WFRI_to_D_end
|
| 347 |
+
assert ival_WTHU.asfreq("D", "s") == ival_WTHU_to_D_start
|
| 348 |
+
assert ival_WTHU.asfreq("D", "E") == ival_WTHU_to_D_end
|
| 349 |
+
assert ival_WWED.asfreq("D", "s") == ival_WWED_to_D_start
|
| 350 |
+
assert ival_WWED.asfreq("D", "E") == ival_WWED_to_D_end
|
| 351 |
+
assert ival_WTUE.asfreq("D", "s") == ival_WTUE_to_D_start
|
| 352 |
+
assert ival_WTUE.asfreq("D", "E") == ival_WTUE_to_D_end
|
| 353 |
+
assert ival_WMON.asfreq("D", "s") == ival_WMON_to_D_start
|
| 354 |
+
assert ival_WMON.asfreq("D", "E") == ival_WMON_to_D_end
|
| 355 |
+
|
| 356 |
+
assert ival_W.asfreq("h", "s") == ival_W_to_H_start
|
| 357 |
+
assert ival_W.asfreq("h", "E") == ival_W_to_H_end
|
| 358 |
+
assert ival_W.asfreq("Min", "s") == ival_W_to_T_start
|
| 359 |
+
assert ival_W.asfreq("Min", "E") == ival_W_to_T_end
|
| 360 |
+
assert ival_W.asfreq("s", "s") == ival_W_to_S_start
|
| 361 |
+
assert ival_W.asfreq("s", "E") == ival_W_to_S_end
|
| 362 |
+
|
| 363 |
+
assert ival_W.asfreq("W") == ival_W
|
| 364 |
+
|
| 365 |
+
msg = INVALID_FREQ_ERR_MSG
|
| 366 |
+
with pytest.raises(ValueError, match=msg):
|
| 367 |
+
ival_W.asfreq("WK")
|
| 368 |
+
|
| 369 |
+
def test_conv_weekly_legacy(self):
|
| 370 |
+
# frequency conversion tests: from Weekly Frequency
|
| 371 |
+
msg = INVALID_FREQ_ERR_MSG
|
| 372 |
+
with pytest.raises(ValueError, match=msg):
|
| 373 |
+
Period(freq="WK", year=2007, month=1, day=1)
|
| 374 |
+
|
| 375 |
+
with pytest.raises(ValueError, match=msg):
|
| 376 |
+
Period(freq="WK-SAT", year=2007, month=1, day=6)
|
| 377 |
+
with pytest.raises(ValueError, match=msg):
|
| 378 |
+
Period(freq="WK-FRI", year=2007, month=1, day=5)
|
| 379 |
+
with pytest.raises(ValueError, match=msg):
|
| 380 |
+
Period(freq="WK-THU", year=2007, month=1, day=4)
|
| 381 |
+
with pytest.raises(ValueError, match=msg):
|
| 382 |
+
Period(freq="WK-WED", year=2007, month=1, day=3)
|
| 383 |
+
with pytest.raises(ValueError, match=msg):
|
| 384 |
+
Period(freq="WK-TUE", year=2007, month=1, day=2)
|
| 385 |
+
with pytest.raises(ValueError, match=msg):
|
| 386 |
+
Period(freq="WK-MON", year=2007, month=1, day=1)
|
| 387 |
+
|
| 388 |
+
def test_conv_business(self):
|
| 389 |
+
# frequency conversion tests: from Business Frequency"
|
| 390 |
+
|
| 391 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 392 |
+
ival_B = Period(freq="B", year=2007, month=1, day=1)
|
| 393 |
+
ival_B_end_of_year = Period(freq="B", year=2007, month=12, day=31)
|
| 394 |
+
ival_B_end_of_quarter = Period(freq="B", year=2007, month=3, day=30)
|
| 395 |
+
ival_B_end_of_month = Period(freq="B", year=2007, month=1, day=31)
|
| 396 |
+
ival_B_end_of_week = Period(freq="B", year=2007, month=1, day=5)
|
| 397 |
+
|
| 398 |
+
ival_B_to_A = Period(freq="Y", year=2007)
|
| 399 |
+
ival_B_to_Q = Period(freq="Q", year=2007, quarter=1)
|
| 400 |
+
ival_B_to_M = Period(freq="M", year=2007, month=1)
|
| 401 |
+
ival_B_to_W = Period(freq="W", year=2007, month=1, day=7)
|
| 402 |
+
ival_B_to_D = Period(freq="D", year=2007, month=1, day=1)
|
| 403 |
+
ival_B_to_H_start = Period(freq="h", year=2007, month=1, day=1, hour=0)
|
| 404 |
+
ival_B_to_H_end = Period(freq="h", year=2007, month=1, day=1, hour=23)
|
| 405 |
+
ival_B_to_T_start = Period(
|
| 406 |
+
freq="Min", year=2007, month=1, day=1, hour=0, minute=0
|
| 407 |
+
)
|
| 408 |
+
ival_B_to_T_end = Period(
|
| 409 |
+
freq="Min", year=2007, month=1, day=1, hour=23, minute=59
|
| 410 |
+
)
|
| 411 |
+
ival_B_to_S_start = Period(
|
| 412 |
+
freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
| 413 |
+
)
|
| 414 |
+
ival_B_to_S_end = Period(
|
| 415 |
+
freq="s", year=2007, month=1, day=1, hour=23, minute=59, second=59
|
| 416 |
+
)
|
| 417 |
+
|
| 418 |
+
assert ival_B.asfreq("Y") == ival_B_to_A
|
| 419 |
+
assert ival_B_end_of_year.asfreq("Y") == ival_B_to_A
|
| 420 |
+
assert ival_B.asfreq("Q") == ival_B_to_Q
|
| 421 |
+
assert ival_B_end_of_quarter.asfreq("Q") == ival_B_to_Q
|
| 422 |
+
assert ival_B.asfreq("M") == ival_B_to_M
|
| 423 |
+
assert ival_B_end_of_month.asfreq("M") == ival_B_to_M
|
| 424 |
+
assert ival_B.asfreq("W") == ival_B_to_W
|
| 425 |
+
assert ival_B_end_of_week.asfreq("W") == ival_B_to_W
|
| 426 |
+
|
| 427 |
+
assert ival_B.asfreq("D") == ival_B_to_D
|
| 428 |
+
|
| 429 |
+
assert ival_B.asfreq("h", "s") == ival_B_to_H_start
|
| 430 |
+
assert ival_B.asfreq("h", "E") == ival_B_to_H_end
|
| 431 |
+
assert ival_B.asfreq("Min", "s") == ival_B_to_T_start
|
| 432 |
+
assert ival_B.asfreq("Min", "E") == ival_B_to_T_end
|
| 433 |
+
assert ival_B.asfreq("s", "s") == ival_B_to_S_start
|
| 434 |
+
assert ival_B.asfreq("s", "E") == ival_B_to_S_end
|
| 435 |
+
|
| 436 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 437 |
+
assert ival_B.asfreq("B") == ival_B
|
| 438 |
+
|
| 439 |
+
def test_conv_daily(self):
|
| 440 |
+
# frequency conversion tests: from Business Frequency"
|
| 441 |
+
|
| 442 |
+
ival_D = Period(freq="D", year=2007, month=1, day=1)
|
| 443 |
+
ival_D_end_of_year = Period(freq="D", year=2007, month=12, day=31)
|
| 444 |
+
ival_D_end_of_quarter = Period(freq="D", year=2007, month=3, day=31)
|
| 445 |
+
ival_D_end_of_month = Period(freq="D", year=2007, month=1, day=31)
|
| 446 |
+
ival_D_end_of_week = Period(freq="D", year=2007, month=1, day=7)
|
| 447 |
+
|
| 448 |
+
ival_D_friday = Period(freq="D", year=2007, month=1, day=5)
|
| 449 |
+
ival_D_saturday = Period(freq="D", year=2007, month=1, day=6)
|
| 450 |
+
ival_D_sunday = Period(freq="D", year=2007, month=1, day=7)
|
| 451 |
+
|
| 452 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 453 |
+
ival_B_friday = Period(freq="B", year=2007, month=1, day=5)
|
| 454 |
+
ival_B_monday = Period(freq="B", year=2007, month=1, day=8)
|
| 455 |
+
|
| 456 |
+
ival_D_to_A = Period(freq="Y", year=2007)
|
| 457 |
+
|
| 458 |
+
ival_Deoq_to_AJAN = Period(freq="Y-JAN", year=2008)
|
| 459 |
+
ival_Deoq_to_AJUN = Period(freq="Y-JUN", year=2007)
|
| 460 |
+
ival_Deoq_to_ADEC = Period(freq="Y-DEC", year=2007)
|
| 461 |
+
|
| 462 |
+
ival_D_to_QEJAN = Period(freq="Q-JAN", year=2007, quarter=4)
|
| 463 |
+
ival_D_to_QEJUN = Period(freq="Q-JUN", year=2007, quarter=3)
|
| 464 |
+
ival_D_to_QEDEC = Period(freq="Q-DEC", year=2007, quarter=1)
|
| 465 |
+
|
| 466 |
+
ival_D_to_M = Period(freq="M", year=2007, month=1)
|
| 467 |
+
ival_D_to_W = Period(freq="W", year=2007, month=1, day=7)
|
| 468 |
+
|
| 469 |
+
ival_D_to_H_start = Period(freq="h", year=2007, month=1, day=1, hour=0)
|
| 470 |
+
ival_D_to_H_end = Period(freq="h", year=2007, month=1, day=1, hour=23)
|
| 471 |
+
ival_D_to_T_start = Period(
|
| 472 |
+
freq="Min", year=2007, month=1, day=1, hour=0, minute=0
|
| 473 |
+
)
|
| 474 |
+
ival_D_to_T_end = Period(
|
| 475 |
+
freq="Min", year=2007, month=1, day=1, hour=23, minute=59
|
| 476 |
+
)
|
| 477 |
+
ival_D_to_S_start = Period(
|
| 478 |
+
freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
| 479 |
+
)
|
| 480 |
+
ival_D_to_S_end = Period(
|
| 481 |
+
freq="s", year=2007, month=1, day=1, hour=23, minute=59, second=59
|
| 482 |
+
)
|
| 483 |
+
|
| 484 |
+
assert ival_D.asfreq("Y") == ival_D_to_A
|
| 485 |
+
|
| 486 |
+
assert ival_D_end_of_quarter.asfreq("Y-JAN") == ival_Deoq_to_AJAN
|
| 487 |
+
assert ival_D_end_of_quarter.asfreq("Y-JUN") == ival_Deoq_to_AJUN
|
| 488 |
+
assert ival_D_end_of_quarter.asfreq("Y-DEC") == ival_Deoq_to_ADEC
|
| 489 |
+
|
| 490 |
+
assert ival_D_end_of_year.asfreq("Y") == ival_D_to_A
|
| 491 |
+
assert ival_D_end_of_quarter.asfreq("Q") == ival_D_to_QEDEC
|
| 492 |
+
assert ival_D.asfreq("Q-JAN") == ival_D_to_QEJAN
|
| 493 |
+
assert ival_D.asfreq("Q-JUN") == ival_D_to_QEJUN
|
| 494 |
+
assert ival_D.asfreq("Q-DEC") == ival_D_to_QEDEC
|
| 495 |
+
assert ival_D.asfreq("M") == ival_D_to_M
|
| 496 |
+
assert ival_D_end_of_month.asfreq("M") == ival_D_to_M
|
| 497 |
+
assert ival_D.asfreq("W") == ival_D_to_W
|
| 498 |
+
assert ival_D_end_of_week.asfreq("W") == ival_D_to_W
|
| 499 |
+
|
| 500 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 501 |
+
assert ival_D_friday.asfreq("B") == ival_B_friday
|
| 502 |
+
assert ival_D_saturday.asfreq("B", "s") == ival_B_friday
|
| 503 |
+
assert ival_D_saturday.asfreq("B", "E") == ival_B_monday
|
| 504 |
+
assert ival_D_sunday.asfreq("B", "s") == ival_B_friday
|
| 505 |
+
assert ival_D_sunday.asfreq("B", "E") == ival_B_monday
|
| 506 |
+
|
| 507 |
+
assert ival_D.asfreq("h", "s") == ival_D_to_H_start
|
| 508 |
+
assert ival_D.asfreq("h", "E") == ival_D_to_H_end
|
| 509 |
+
assert ival_D.asfreq("Min", "s") == ival_D_to_T_start
|
| 510 |
+
assert ival_D.asfreq("Min", "E") == ival_D_to_T_end
|
| 511 |
+
assert ival_D.asfreq("s", "s") == ival_D_to_S_start
|
| 512 |
+
assert ival_D.asfreq("s", "E") == ival_D_to_S_end
|
| 513 |
+
|
| 514 |
+
assert ival_D.asfreq("D") == ival_D
|
| 515 |
+
|
| 516 |
+
def test_conv_hourly(self):
|
| 517 |
+
# frequency conversion tests: from Hourly Frequency"
|
| 518 |
+
|
| 519 |
+
ival_H = Period(freq="h", year=2007, month=1, day=1, hour=0)
|
| 520 |
+
ival_H_end_of_year = Period(freq="h", year=2007, month=12, day=31, hour=23)
|
| 521 |
+
ival_H_end_of_quarter = Period(freq="h", year=2007, month=3, day=31, hour=23)
|
| 522 |
+
ival_H_end_of_month = Period(freq="h", year=2007, month=1, day=31, hour=23)
|
| 523 |
+
ival_H_end_of_week = Period(freq="h", year=2007, month=1, day=7, hour=23)
|
| 524 |
+
ival_H_end_of_day = Period(freq="h", year=2007, month=1, day=1, hour=23)
|
| 525 |
+
ival_H_end_of_bus = Period(freq="h", year=2007, month=1, day=1, hour=23)
|
| 526 |
+
|
| 527 |
+
ival_H_to_A = Period(freq="Y", year=2007)
|
| 528 |
+
ival_H_to_Q = Period(freq="Q", year=2007, quarter=1)
|
| 529 |
+
ival_H_to_M = Period(freq="M", year=2007, month=1)
|
| 530 |
+
ival_H_to_W = Period(freq="W", year=2007, month=1, day=7)
|
| 531 |
+
ival_H_to_D = Period(freq="D", year=2007, month=1, day=1)
|
| 532 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 533 |
+
ival_H_to_B = Period(freq="B", year=2007, month=1, day=1)
|
| 534 |
+
|
| 535 |
+
ival_H_to_T_start = Period(
|
| 536 |
+
freq="Min", year=2007, month=1, day=1, hour=0, minute=0
|
| 537 |
+
)
|
| 538 |
+
ival_H_to_T_end = Period(
|
| 539 |
+
freq="Min", year=2007, month=1, day=1, hour=0, minute=59
|
| 540 |
+
)
|
| 541 |
+
ival_H_to_S_start = Period(
|
| 542 |
+
freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
| 543 |
+
)
|
| 544 |
+
ival_H_to_S_end = Period(
|
| 545 |
+
freq="s", year=2007, month=1, day=1, hour=0, minute=59, second=59
|
| 546 |
+
)
|
| 547 |
+
|
| 548 |
+
assert ival_H.asfreq("Y") == ival_H_to_A
|
| 549 |
+
assert ival_H_end_of_year.asfreq("Y") == ival_H_to_A
|
| 550 |
+
assert ival_H.asfreq("Q") == ival_H_to_Q
|
| 551 |
+
assert ival_H_end_of_quarter.asfreq("Q") == ival_H_to_Q
|
| 552 |
+
assert ival_H.asfreq("M") == ival_H_to_M
|
| 553 |
+
assert ival_H_end_of_month.asfreq("M") == ival_H_to_M
|
| 554 |
+
assert ival_H.asfreq("W") == ival_H_to_W
|
| 555 |
+
assert ival_H_end_of_week.asfreq("W") == ival_H_to_W
|
| 556 |
+
assert ival_H.asfreq("D") == ival_H_to_D
|
| 557 |
+
assert ival_H_end_of_day.asfreq("D") == ival_H_to_D
|
| 558 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 559 |
+
assert ival_H.asfreq("B") == ival_H_to_B
|
| 560 |
+
assert ival_H_end_of_bus.asfreq("B") == ival_H_to_B
|
| 561 |
+
|
| 562 |
+
assert ival_H.asfreq("Min", "s") == ival_H_to_T_start
|
| 563 |
+
assert ival_H.asfreq("Min", "E") == ival_H_to_T_end
|
| 564 |
+
assert ival_H.asfreq("s", "s") == ival_H_to_S_start
|
| 565 |
+
assert ival_H.asfreq("s", "E") == ival_H_to_S_end
|
| 566 |
+
|
| 567 |
+
assert ival_H.asfreq("h") == ival_H
|
| 568 |
+
|
| 569 |
+
def test_conv_minutely(self):
|
| 570 |
+
# frequency conversion tests: from Minutely Frequency"
|
| 571 |
+
|
| 572 |
+
ival_T = Period(freq="Min", year=2007, month=1, day=1, hour=0, minute=0)
|
| 573 |
+
ival_T_end_of_year = Period(
|
| 574 |
+
freq="Min", year=2007, month=12, day=31, hour=23, minute=59
|
| 575 |
+
)
|
| 576 |
+
ival_T_end_of_quarter = Period(
|
| 577 |
+
freq="Min", year=2007, month=3, day=31, hour=23, minute=59
|
| 578 |
+
)
|
| 579 |
+
ival_T_end_of_month = Period(
|
| 580 |
+
freq="Min", year=2007, month=1, day=31, hour=23, minute=59
|
| 581 |
+
)
|
| 582 |
+
ival_T_end_of_week = Period(
|
| 583 |
+
freq="Min", year=2007, month=1, day=7, hour=23, minute=59
|
| 584 |
+
)
|
| 585 |
+
ival_T_end_of_day = Period(
|
| 586 |
+
freq="Min", year=2007, month=1, day=1, hour=23, minute=59
|
| 587 |
+
)
|
| 588 |
+
ival_T_end_of_bus = Period(
|
| 589 |
+
freq="Min", year=2007, month=1, day=1, hour=23, minute=59
|
| 590 |
+
)
|
| 591 |
+
ival_T_end_of_hour = Period(
|
| 592 |
+
freq="Min", year=2007, month=1, day=1, hour=0, minute=59
|
| 593 |
+
)
|
| 594 |
+
|
| 595 |
+
ival_T_to_A = Period(freq="Y", year=2007)
|
| 596 |
+
ival_T_to_Q = Period(freq="Q", year=2007, quarter=1)
|
| 597 |
+
ival_T_to_M = Period(freq="M", year=2007, month=1)
|
| 598 |
+
ival_T_to_W = Period(freq="W", year=2007, month=1, day=7)
|
| 599 |
+
ival_T_to_D = Period(freq="D", year=2007, month=1, day=1)
|
| 600 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 601 |
+
ival_T_to_B = Period(freq="B", year=2007, month=1, day=1)
|
| 602 |
+
ival_T_to_H = Period(freq="h", year=2007, month=1, day=1, hour=0)
|
| 603 |
+
|
| 604 |
+
ival_T_to_S_start = Period(
|
| 605 |
+
freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
| 606 |
+
)
|
| 607 |
+
ival_T_to_S_end = Period(
|
| 608 |
+
freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=59
|
| 609 |
+
)
|
| 610 |
+
|
| 611 |
+
assert ival_T.asfreq("Y") == ival_T_to_A
|
| 612 |
+
assert ival_T_end_of_year.asfreq("Y") == ival_T_to_A
|
| 613 |
+
assert ival_T.asfreq("Q") == ival_T_to_Q
|
| 614 |
+
assert ival_T_end_of_quarter.asfreq("Q") == ival_T_to_Q
|
| 615 |
+
assert ival_T.asfreq("M") == ival_T_to_M
|
| 616 |
+
assert ival_T_end_of_month.asfreq("M") == ival_T_to_M
|
| 617 |
+
assert ival_T.asfreq("W") == ival_T_to_W
|
| 618 |
+
assert ival_T_end_of_week.asfreq("W") == ival_T_to_W
|
| 619 |
+
assert ival_T.asfreq("D") == ival_T_to_D
|
| 620 |
+
assert ival_T_end_of_day.asfreq("D") == ival_T_to_D
|
| 621 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 622 |
+
assert ival_T.asfreq("B") == ival_T_to_B
|
| 623 |
+
assert ival_T_end_of_bus.asfreq("B") == ival_T_to_B
|
| 624 |
+
assert ival_T.asfreq("h") == ival_T_to_H
|
| 625 |
+
assert ival_T_end_of_hour.asfreq("h") == ival_T_to_H
|
| 626 |
+
|
| 627 |
+
assert ival_T.asfreq("s", "s") == ival_T_to_S_start
|
| 628 |
+
assert ival_T.asfreq("s", "E") == ival_T_to_S_end
|
| 629 |
+
|
| 630 |
+
assert ival_T.asfreq("Min") == ival_T
|
| 631 |
+
|
| 632 |
+
def test_conv_secondly(self):
|
| 633 |
+
# frequency conversion tests: from Secondly Frequency"
|
| 634 |
+
|
| 635 |
+
ival_S = Period(freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0)
|
| 636 |
+
ival_S_end_of_year = Period(
|
| 637 |
+
freq="s", year=2007, month=12, day=31, hour=23, minute=59, second=59
|
| 638 |
+
)
|
| 639 |
+
ival_S_end_of_quarter = Period(
|
| 640 |
+
freq="s", year=2007, month=3, day=31, hour=23, minute=59, second=59
|
| 641 |
+
)
|
| 642 |
+
ival_S_end_of_month = Period(
|
| 643 |
+
freq="s", year=2007, month=1, day=31, hour=23, minute=59, second=59
|
| 644 |
+
)
|
| 645 |
+
ival_S_end_of_week = Period(
|
| 646 |
+
freq="s", year=2007, month=1, day=7, hour=23, minute=59, second=59
|
| 647 |
+
)
|
| 648 |
+
ival_S_end_of_day = Period(
|
| 649 |
+
freq="s", year=2007, month=1, day=1, hour=23, minute=59, second=59
|
| 650 |
+
)
|
| 651 |
+
ival_S_end_of_bus = Period(
|
| 652 |
+
freq="s", year=2007, month=1, day=1, hour=23, minute=59, second=59
|
| 653 |
+
)
|
| 654 |
+
ival_S_end_of_hour = Period(
|
| 655 |
+
freq="s", year=2007, month=1, day=1, hour=0, minute=59, second=59
|
| 656 |
+
)
|
| 657 |
+
ival_S_end_of_minute = Period(
|
| 658 |
+
freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=59
|
| 659 |
+
)
|
| 660 |
+
|
| 661 |
+
ival_S_to_A = Period(freq="Y", year=2007)
|
| 662 |
+
ival_S_to_Q = Period(freq="Q", year=2007, quarter=1)
|
| 663 |
+
ival_S_to_M = Period(freq="M", year=2007, month=1)
|
| 664 |
+
ival_S_to_W = Period(freq="W", year=2007, month=1, day=7)
|
| 665 |
+
ival_S_to_D = Period(freq="D", year=2007, month=1, day=1)
|
| 666 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 667 |
+
ival_S_to_B = Period(freq="B", year=2007, month=1, day=1)
|
| 668 |
+
ival_S_to_H = Period(freq="h", year=2007, month=1, day=1, hour=0)
|
| 669 |
+
ival_S_to_T = Period(freq="Min", year=2007, month=1, day=1, hour=0, minute=0)
|
| 670 |
+
|
| 671 |
+
assert ival_S.asfreq("Y") == ival_S_to_A
|
| 672 |
+
assert ival_S_end_of_year.asfreq("Y") == ival_S_to_A
|
| 673 |
+
assert ival_S.asfreq("Q") == ival_S_to_Q
|
| 674 |
+
assert ival_S_end_of_quarter.asfreq("Q") == ival_S_to_Q
|
| 675 |
+
assert ival_S.asfreq("M") == ival_S_to_M
|
| 676 |
+
assert ival_S_end_of_month.asfreq("M") == ival_S_to_M
|
| 677 |
+
assert ival_S.asfreq("W") == ival_S_to_W
|
| 678 |
+
assert ival_S_end_of_week.asfreq("W") == ival_S_to_W
|
| 679 |
+
assert ival_S.asfreq("D") == ival_S_to_D
|
| 680 |
+
assert ival_S_end_of_day.asfreq("D") == ival_S_to_D
|
| 681 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 682 |
+
assert ival_S.asfreq("B") == ival_S_to_B
|
| 683 |
+
assert ival_S_end_of_bus.asfreq("B") == ival_S_to_B
|
| 684 |
+
assert ival_S.asfreq("h") == ival_S_to_H
|
| 685 |
+
assert ival_S_end_of_hour.asfreq("h") == ival_S_to_H
|
| 686 |
+
assert ival_S.asfreq("Min") == ival_S_to_T
|
| 687 |
+
assert ival_S_end_of_minute.asfreq("Min") == ival_S_to_T
|
| 688 |
+
|
| 689 |
+
assert ival_S.asfreq("s") == ival_S
|
| 690 |
+
|
| 691 |
+
def test_conv_microsecond(self):
|
| 692 |
+
# GH#31475 Avoid floating point errors dropping the start_time to
|
| 693 |
+
# before the beginning of the Period
|
| 694 |
+
per = Period("2020-01-30 15:57:27.576166", freq="us")
|
| 695 |
+
assert per.ordinal == 1580399847576166
|
| 696 |
+
|
| 697 |
+
start = per.start_time
|
| 698 |
+
expected = Timestamp("2020-01-30 15:57:27.576166")
|
| 699 |
+
assert start == expected
|
| 700 |
+
assert start._value == per.ordinal * 1000
|
| 701 |
+
|
| 702 |
+
per2 = Period("2300-01-01", "us")
|
| 703 |
+
msg = "2300-01-01"
|
| 704 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 705 |
+
per2.start_time
|
| 706 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 707 |
+
per2.end_time
|
| 708 |
+
|
| 709 |
+
def test_asfreq_mult(self):
|
| 710 |
+
# normal freq to mult freq
|
| 711 |
+
p = Period(freq="Y", year=2007)
|
| 712 |
+
# ordinal will not change
|
| 713 |
+
for freq in ["3Y", offsets.YearEnd(3)]:
|
| 714 |
+
result = p.asfreq(freq)
|
| 715 |
+
expected = Period("2007", freq="3Y")
|
| 716 |
+
|
| 717 |
+
assert result == expected
|
| 718 |
+
assert result.ordinal == expected.ordinal
|
| 719 |
+
assert result.freq == expected.freq
|
| 720 |
+
# ordinal will not change
|
| 721 |
+
for freq in ["3Y", offsets.YearEnd(3)]:
|
| 722 |
+
result = p.asfreq(freq, how="S")
|
| 723 |
+
expected = Period("2007", freq="3Y")
|
| 724 |
+
|
| 725 |
+
assert result == expected
|
| 726 |
+
assert result.ordinal == expected.ordinal
|
| 727 |
+
assert result.freq == expected.freq
|
| 728 |
+
|
| 729 |
+
# mult freq to normal freq
|
| 730 |
+
p = Period(freq="3Y", year=2007)
|
| 731 |
+
# ordinal will change because how=E is the default
|
| 732 |
+
for freq in ["Y", offsets.YearEnd()]:
|
| 733 |
+
result = p.asfreq(freq)
|
| 734 |
+
expected = Period("2009", freq="Y")
|
| 735 |
+
|
| 736 |
+
assert result == expected
|
| 737 |
+
assert result.ordinal == expected.ordinal
|
| 738 |
+
assert result.freq == expected.freq
|
| 739 |
+
# ordinal will not change
|
| 740 |
+
for freq in ["Y", offsets.YearEnd()]:
|
| 741 |
+
result = p.asfreq(freq, how="s")
|
| 742 |
+
expected = Period("2007", freq="Y")
|
| 743 |
+
|
| 744 |
+
assert result == expected
|
| 745 |
+
assert result.ordinal == expected.ordinal
|
| 746 |
+
assert result.freq == expected.freq
|
| 747 |
+
|
| 748 |
+
p = Period(freq="Y", year=2007)
|
| 749 |
+
for freq in ["2M", offsets.MonthEnd(2)]:
|
| 750 |
+
result = p.asfreq(freq)
|
| 751 |
+
expected = Period("2007-12", freq="2M")
|
| 752 |
+
|
| 753 |
+
assert result == expected
|
| 754 |
+
assert result.ordinal == expected.ordinal
|
| 755 |
+
assert result.freq == expected.freq
|
| 756 |
+
for freq in ["2M", offsets.MonthEnd(2)]:
|
| 757 |
+
result = p.asfreq(freq, how="s")
|
| 758 |
+
expected = Period("2007-01", freq="2M")
|
| 759 |
+
|
| 760 |
+
assert result == expected
|
| 761 |
+
assert result.ordinal == expected.ordinal
|
| 762 |
+
assert result.freq == expected.freq
|
| 763 |
+
|
| 764 |
+
p = Period(freq="3Y", year=2007)
|
| 765 |
+
for freq in ["2M", offsets.MonthEnd(2)]:
|
| 766 |
+
result = p.asfreq(freq)
|
| 767 |
+
expected = Period("2009-12", freq="2M")
|
| 768 |
+
|
| 769 |
+
assert result == expected
|
| 770 |
+
assert result.ordinal == expected.ordinal
|
| 771 |
+
assert result.freq == expected.freq
|
| 772 |
+
for freq in ["2M", offsets.MonthEnd(2)]:
|
| 773 |
+
result = p.asfreq(freq, how="s")
|
| 774 |
+
expected = Period("2007-01", freq="2M")
|
| 775 |
+
|
| 776 |
+
assert result == expected
|
| 777 |
+
assert result.ordinal == expected.ordinal
|
| 778 |
+
assert result.freq == expected.freq
|
| 779 |
+
|
| 780 |
+
def test_asfreq_combined(self):
|
| 781 |
+
# normal freq to combined freq
|
| 782 |
+
p = Period("2007", freq="h")
|
| 783 |
+
|
| 784 |
+
# ordinal will not change
|
| 785 |
+
expected = Period("2007", freq="25h")
|
| 786 |
+
for freq, how in zip(["1D1h", "1h1D"], ["E", "S"]):
|
| 787 |
+
result = p.asfreq(freq, how=how)
|
| 788 |
+
assert result == expected
|
| 789 |
+
assert result.ordinal == expected.ordinal
|
| 790 |
+
assert result.freq == expected.freq
|
| 791 |
+
|
| 792 |
+
# combined freq to normal freq
|
| 793 |
+
p1 = Period(freq="1D1h", year=2007)
|
| 794 |
+
p2 = Period(freq="1h1D", year=2007)
|
| 795 |
+
|
| 796 |
+
# ordinal will change because how=E is the default
|
| 797 |
+
result1 = p1.asfreq("h")
|
| 798 |
+
result2 = p2.asfreq("h")
|
| 799 |
+
expected = Period("2007-01-02", freq="h")
|
| 800 |
+
assert result1 == expected
|
| 801 |
+
assert result1.ordinal == expected.ordinal
|
| 802 |
+
assert result1.freq == expected.freq
|
| 803 |
+
assert result2 == expected
|
| 804 |
+
assert result2.ordinal == expected.ordinal
|
| 805 |
+
assert result2.freq == expected.freq
|
| 806 |
+
|
| 807 |
+
# ordinal will not change
|
| 808 |
+
result1 = p1.asfreq("h", how="S")
|
| 809 |
+
result2 = p2.asfreq("h", how="S")
|
| 810 |
+
expected = Period("2007-01-01", freq="h")
|
| 811 |
+
assert result1 == expected
|
| 812 |
+
assert result1.ordinal == expected.ordinal
|
| 813 |
+
assert result1.freq == expected.freq
|
| 814 |
+
assert result2 == expected
|
| 815 |
+
assert result2.ordinal == expected.ordinal
|
| 816 |
+
assert result2.freq == expected.freq
|
| 817 |
+
|
| 818 |
+
def test_asfreq_MS(self):
|
| 819 |
+
initial = Period("2013")
|
| 820 |
+
|
| 821 |
+
assert initial.asfreq(freq="M", how="S") == Period("2013-01", "M")
|
| 822 |
+
|
| 823 |
+
msg = "MS is not supported as period frequency"
|
| 824 |
+
with pytest.raises(ValueError, match=msg):
|
| 825 |
+
initial.asfreq(freq="MS", how="S")
|
| 826 |
+
|
| 827 |
+
with pytest.raises(ValueError, match=msg):
|
| 828 |
+
Period("2013-01", "MS")
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/period/test_period.py
ADDED
|
@@ -0,0 +1,1158 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import (
|
| 2 |
+
date,
|
| 3 |
+
datetime,
|
| 4 |
+
timedelta,
|
| 5 |
+
)
|
| 6 |
+
import re
|
| 7 |
+
|
| 8 |
+
import numpy as np
|
| 9 |
+
import pytest
|
| 10 |
+
|
| 11 |
+
from pandas._libs.tslibs import iNaT
|
| 12 |
+
from pandas._libs.tslibs.ccalendar import (
|
| 13 |
+
DAYS,
|
| 14 |
+
MONTHS,
|
| 15 |
+
)
|
| 16 |
+
from pandas._libs.tslibs.np_datetime import OutOfBoundsDatetime
|
| 17 |
+
from pandas._libs.tslibs.parsing import DateParseError
|
| 18 |
+
from pandas._libs.tslibs.period import INVALID_FREQ_ERR_MSG
|
| 19 |
+
from pandas.compat import PY314
|
| 20 |
+
|
| 21 |
+
from pandas import (
|
| 22 |
+
NaT,
|
| 23 |
+
Period,
|
| 24 |
+
Timedelta,
|
| 25 |
+
Timestamp,
|
| 26 |
+
offsets,
|
| 27 |
+
)
|
| 28 |
+
import pandas._testing as tm
|
| 29 |
+
|
| 30 |
+
bday_msg = "Period with BDay freq is deprecated"
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class TestPeriodDisallowedFreqs:
|
| 34 |
+
@pytest.mark.parametrize(
|
| 35 |
+
"freq, freq_msg",
|
| 36 |
+
[
|
| 37 |
+
(offsets.BYearBegin(), "BYearBegin"),
|
| 38 |
+
(offsets.YearBegin(2), "YearBegin"),
|
| 39 |
+
(offsets.QuarterBegin(startingMonth=12), "QuarterBegin"),
|
| 40 |
+
(offsets.BusinessMonthEnd(2), "BusinessMonthEnd"),
|
| 41 |
+
],
|
| 42 |
+
)
|
| 43 |
+
def test_offsets_not_supported(self, freq, freq_msg):
|
| 44 |
+
# GH#55785
|
| 45 |
+
msg = re.escape(f"{freq} is not supported as period frequency")
|
| 46 |
+
with pytest.raises(ValueError, match=msg):
|
| 47 |
+
Period(year=2014, freq=freq)
|
| 48 |
+
|
| 49 |
+
def test_custom_business_day_freq_raises(self):
|
| 50 |
+
# GH#52534
|
| 51 |
+
msg = "C is not supported as period frequency"
|
| 52 |
+
with pytest.raises(ValueError, match=msg):
|
| 53 |
+
Period("2023-04-10", freq="C")
|
| 54 |
+
msg = f"{offsets.CustomBusinessDay().base} is not supported as period frequency"
|
| 55 |
+
with pytest.raises(ValueError, match=msg):
|
| 56 |
+
Period("2023-04-10", freq=offsets.CustomBusinessDay())
|
| 57 |
+
|
| 58 |
+
def test_invalid_frequency_error_message(self):
|
| 59 |
+
msg = "WOM-1MON is not supported as period frequency"
|
| 60 |
+
with pytest.raises(ValueError, match=msg):
|
| 61 |
+
Period("2012-01-02", freq="WOM-1MON")
|
| 62 |
+
|
| 63 |
+
def test_invalid_frequency_period_error_message(self):
|
| 64 |
+
msg = "for Period, please use 'M' instead of 'ME'"
|
| 65 |
+
with pytest.raises(ValueError, match=msg):
|
| 66 |
+
Period("2012-01-02", freq="ME")
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
class TestPeriodConstruction:
|
| 70 |
+
def test_from_td64nat_raises(self):
|
| 71 |
+
# GH#44507
|
| 72 |
+
td = NaT.to_numpy("m8[ns]")
|
| 73 |
+
|
| 74 |
+
msg = "Value must be Period, string, integer, or datetime"
|
| 75 |
+
with pytest.raises(ValueError, match=msg):
|
| 76 |
+
Period(td)
|
| 77 |
+
|
| 78 |
+
with pytest.raises(ValueError, match=msg):
|
| 79 |
+
Period(td, freq="D")
|
| 80 |
+
|
| 81 |
+
def test_construction(self):
|
| 82 |
+
i1 = Period("1/1/2005", freq="M")
|
| 83 |
+
i2 = Period("Jan 2005")
|
| 84 |
+
|
| 85 |
+
assert i1 == i2
|
| 86 |
+
|
| 87 |
+
# GH#54105 - Period can be confusingly instantiated with lowercase freq
|
| 88 |
+
# TODO: raise in the future an error when passing lowercase freq
|
| 89 |
+
i1 = Period("2005", freq="Y")
|
| 90 |
+
i2 = Period("2005")
|
| 91 |
+
|
| 92 |
+
assert i1 == i2
|
| 93 |
+
|
| 94 |
+
i4 = Period("2005", freq="M")
|
| 95 |
+
assert i1 != i4
|
| 96 |
+
|
| 97 |
+
i1 = Period.now(freq="Q")
|
| 98 |
+
i2 = Period(datetime.now(), freq="Q")
|
| 99 |
+
|
| 100 |
+
assert i1 == i2
|
| 101 |
+
|
| 102 |
+
# Pass in freq as a keyword argument sometimes as a test for
|
| 103 |
+
# https://github.com/pandas-dev/pandas/issues/53369
|
| 104 |
+
i1 = Period.now(freq="D")
|
| 105 |
+
i2 = Period(datetime.now(), freq="D")
|
| 106 |
+
i3 = Period.now(offsets.Day())
|
| 107 |
+
|
| 108 |
+
assert i1 == i2
|
| 109 |
+
assert i1 == i3
|
| 110 |
+
|
| 111 |
+
i1 = Period("1982", freq="min")
|
| 112 |
+
msg = "'MIN' is deprecated and will be removed in a future version."
|
| 113 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 114 |
+
i2 = Period("1982", freq="MIN")
|
| 115 |
+
assert i1 == i2
|
| 116 |
+
|
| 117 |
+
i1 = Period(year=2005, month=3, day=1, freq="D")
|
| 118 |
+
i2 = Period("3/1/2005", freq="D")
|
| 119 |
+
assert i1 == i2
|
| 120 |
+
|
| 121 |
+
i3 = Period(year=2005, month=3, day=1, freq="d")
|
| 122 |
+
assert i1 == i3
|
| 123 |
+
|
| 124 |
+
i1 = Period("2007-01-01 09:00:00.001")
|
| 125 |
+
expected = Period(datetime(2007, 1, 1, 9, 0, 0, 1000), freq="ms")
|
| 126 |
+
assert i1 == expected
|
| 127 |
+
|
| 128 |
+
expected = Period("2007-01-01 09:00:00.001", freq="ms")
|
| 129 |
+
assert i1 == expected
|
| 130 |
+
|
| 131 |
+
i1 = Period("2007-01-01 09:00:00.00101")
|
| 132 |
+
expected = Period(datetime(2007, 1, 1, 9, 0, 0, 1010), freq="us")
|
| 133 |
+
assert i1 == expected
|
| 134 |
+
|
| 135 |
+
expected = Period("2007-01-01 09:00:00.00101", freq="us")
|
| 136 |
+
assert i1 == expected
|
| 137 |
+
|
| 138 |
+
msg = "Must supply freq for ordinal value"
|
| 139 |
+
with pytest.raises(ValueError, match=msg):
|
| 140 |
+
Period(ordinal=200701)
|
| 141 |
+
|
| 142 |
+
msg = "Invalid frequency: X"
|
| 143 |
+
with pytest.raises(ValueError, match=msg):
|
| 144 |
+
Period("2007-1-1", freq="X")
|
| 145 |
+
|
| 146 |
+
def test_tuple_freq_disallowed(self):
|
| 147 |
+
# GH#34703 tuple freq disallowed
|
| 148 |
+
with pytest.raises(TypeError, match="pass as a string instead"):
|
| 149 |
+
Period("1982", freq=("Min", 1))
|
| 150 |
+
|
| 151 |
+
with pytest.raises(TypeError, match="pass as a string instead"):
|
| 152 |
+
Period("2006-12-31", ("w", 1))
|
| 153 |
+
|
| 154 |
+
def test_construction_from_timestamp_nanos(self):
|
| 155 |
+
# GH#46811 don't drop nanos from Timestamp
|
| 156 |
+
ts = Timestamp("2022-04-20 09:23:24.123456789")
|
| 157 |
+
per = Period(ts, freq="ns")
|
| 158 |
+
|
| 159 |
+
# should losslessly round-trip, not lose the 789
|
| 160 |
+
rt = per.to_timestamp()
|
| 161 |
+
assert rt == ts
|
| 162 |
+
|
| 163 |
+
# same thing but from a datetime64 object
|
| 164 |
+
dt64 = ts.asm8
|
| 165 |
+
per2 = Period(dt64, freq="ns")
|
| 166 |
+
rt2 = per2.to_timestamp()
|
| 167 |
+
assert rt2.asm8 == dt64
|
| 168 |
+
|
| 169 |
+
def test_construction_bday(self):
|
| 170 |
+
# Biz day construction, roll forward if non-weekday
|
| 171 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 172 |
+
i1 = Period("3/10/12", freq="B")
|
| 173 |
+
i2 = Period("3/10/12", freq="D")
|
| 174 |
+
assert i1 == i2.asfreq("B")
|
| 175 |
+
i2 = Period("3/11/12", freq="D")
|
| 176 |
+
assert i1 == i2.asfreq("B")
|
| 177 |
+
i2 = Period("3/12/12", freq="D")
|
| 178 |
+
assert i1 == i2.asfreq("B")
|
| 179 |
+
|
| 180 |
+
i3 = Period("3/10/12", freq="b")
|
| 181 |
+
assert i1 == i3
|
| 182 |
+
|
| 183 |
+
i1 = Period(year=2012, month=3, day=10, freq="B")
|
| 184 |
+
i2 = Period("3/12/12", freq="B")
|
| 185 |
+
assert i1 == i2
|
| 186 |
+
|
| 187 |
+
def test_construction_quarter(self):
|
| 188 |
+
i1 = Period(year=2005, quarter=1, freq="Q")
|
| 189 |
+
i2 = Period("1/1/2005", freq="Q")
|
| 190 |
+
assert i1 == i2
|
| 191 |
+
|
| 192 |
+
i1 = Period(year=2005, quarter=3, freq="Q")
|
| 193 |
+
i2 = Period("9/1/2005", freq="Q")
|
| 194 |
+
assert i1 == i2
|
| 195 |
+
|
| 196 |
+
i1 = Period("2005Q1")
|
| 197 |
+
i2 = Period(year=2005, quarter=1, freq="Q")
|
| 198 |
+
i3 = Period("2005q1")
|
| 199 |
+
assert i1 == i2
|
| 200 |
+
assert i1 == i3
|
| 201 |
+
|
| 202 |
+
i1 = Period("05Q1")
|
| 203 |
+
assert i1 == i2
|
| 204 |
+
lower = Period("05q1")
|
| 205 |
+
assert i1 == lower
|
| 206 |
+
|
| 207 |
+
i1 = Period("1Q2005")
|
| 208 |
+
assert i1 == i2
|
| 209 |
+
lower = Period("1q2005")
|
| 210 |
+
assert i1 == lower
|
| 211 |
+
|
| 212 |
+
i1 = Period("1Q05")
|
| 213 |
+
assert i1 == i2
|
| 214 |
+
lower = Period("1q05")
|
| 215 |
+
assert i1 == lower
|
| 216 |
+
|
| 217 |
+
i1 = Period("4Q1984")
|
| 218 |
+
assert i1.year == 1984
|
| 219 |
+
lower = Period("4q1984")
|
| 220 |
+
assert i1 == lower
|
| 221 |
+
|
| 222 |
+
def test_construction_month(self):
|
| 223 |
+
expected = Period("2007-01", freq="M")
|
| 224 |
+
i1 = Period("200701", freq="M")
|
| 225 |
+
assert i1 == expected
|
| 226 |
+
|
| 227 |
+
i1 = Period("200701", freq="M")
|
| 228 |
+
assert i1 == expected
|
| 229 |
+
|
| 230 |
+
i1 = Period(200701, freq="M")
|
| 231 |
+
assert i1 == expected
|
| 232 |
+
|
| 233 |
+
i1 = Period(ordinal=200701, freq="M")
|
| 234 |
+
assert i1.year == 18695
|
| 235 |
+
|
| 236 |
+
i1 = Period(datetime(2007, 1, 1), freq="M")
|
| 237 |
+
i2 = Period("200701", freq="M")
|
| 238 |
+
assert i1 == i2
|
| 239 |
+
|
| 240 |
+
i1 = Period(date(2007, 1, 1), freq="M")
|
| 241 |
+
i2 = Period(datetime(2007, 1, 1), freq="M")
|
| 242 |
+
i3 = Period(np.datetime64("2007-01-01"), freq="M")
|
| 243 |
+
i4 = Period("2007-01-01 00:00:00", freq="M")
|
| 244 |
+
i5 = Period("2007-01-01 00:00:00.000", freq="M")
|
| 245 |
+
assert i1 == i2
|
| 246 |
+
assert i1 == i3
|
| 247 |
+
assert i1 == i4
|
| 248 |
+
assert i1 == i5
|
| 249 |
+
|
| 250 |
+
def test_period_constructor_offsets(self):
|
| 251 |
+
assert Period("1/1/2005", freq=offsets.MonthEnd()) == Period(
|
| 252 |
+
"1/1/2005", freq="M"
|
| 253 |
+
)
|
| 254 |
+
assert Period("2005", freq=offsets.YearEnd()) == Period("2005", freq="Y")
|
| 255 |
+
assert Period("2005", freq=offsets.MonthEnd()) == Period("2005", freq="M")
|
| 256 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 257 |
+
assert Period("3/10/12", freq=offsets.BusinessDay()) == Period(
|
| 258 |
+
"3/10/12", freq="B"
|
| 259 |
+
)
|
| 260 |
+
assert Period("3/10/12", freq=offsets.Day()) == Period("3/10/12", freq="D")
|
| 261 |
+
|
| 262 |
+
assert Period(
|
| 263 |
+
year=2005, quarter=1, freq=offsets.QuarterEnd(startingMonth=12)
|
| 264 |
+
) == Period(year=2005, quarter=1, freq="Q")
|
| 265 |
+
assert Period(
|
| 266 |
+
year=2005, quarter=2, freq=offsets.QuarterEnd(startingMonth=12)
|
| 267 |
+
) == Period(year=2005, quarter=2, freq="Q")
|
| 268 |
+
|
| 269 |
+
assert Period(year=2005, month=3, day=1, freq=offsets.Day()) == Period(
|
| 270 |
+
year=2005, month=3, day=1, freq="D"
|
| 271 |
+
)
|
| 272 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 273 |
+
assert Period(year=2012, month=3, day=10, freq=offsets.BDay()) == Period(
|
| 274 |
+
year=2012, month=3, day=10, freq="B"
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
expected = Period("2005-03-01", freq="3D")
|
| 278 |
+
assert Period(year=2005, month=3, day=1, freq=offsets.Day(3)) == expected
|
| 279 |
+
assert Period(year=2005, month=3, day=1, freq="3D") == expected
|
| 280 |
+
|
| 281 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 282 |
+
assert Period(year=2012, month=3, day=10, freq=offsets.BDay(3)) == Period(
|
| 283 |
+
year=2012, month=3, day=10, freq="3B"
|
| 284 |
+
)
|
| 285 |
+
|
| 286 |
+
assert Period(200701, freq=offsets.MonthEnd()) == Period(200701, freq="M")
|
| 287 |
+
|
| 288 |
+
i1 = Period(ordinal=200701, freq=offsets.MonthEnd())
|
| 289 |
+
i2 = Period(ordinal=200701, freq="M")
|
| 290 |
+
assert i1 == i2
|
| 291 |
+
assert i1.year == 18695
|
| 292 |
+
assert i2.year == 18695
|
| 293 |
+
|
| 294 |
+
i1 = Period(datetime(2007, 1, 1), freq="M")
|
| 295 |
+
i2 = Period("200701", freq="M")
|
| 296 |
+
assert i1 == i2
|
| 297 |
+
|
| 298 |
+
i1 = Period(date(2007, 1, 1), freq="M")
|
| 299 |
+
i2 = Period(datetime(2007, 1, 1), freq="M")
|
| 300 |
+
i3 = Period(np.datetime64("2007-01-01"), freq="M")
|
| 301 |
+
i4 = Period("2007-01-01 00:00:00", freq="M")
|
| 302 |
+
i5 = Period("2007-01-01 00:00:00.000", freq="M")
|
| 303 |
+
assert i1 == i2
|
| 304 |
+
assert i1 == i3
|
| 305 |
+
assert i1 == i4
|
| 306 |
+
assert i1 == i5
|
| 307 |
+
|
| 308 |
+
i1 = Period("2007-01-01 09:00:00.001")
|
| 309 |
+
expected = Period(datetime(2007, 1, 1, 9, 0, 0, 1000), freq="ms")
|
| 310 |
+
assert i1 == expected
|
| 311 |
+
|
| 312 |
+
expected = Period("2007-01-01 09:00:00.001", freq="ms")
|
| 313 |
+
assert i1 == expected
|
| 314 |
+
|
| 315 |
+
i1 = Period("2007-01-01 09:00:00.00101")
|
| 316 |
+
expected = Period(datetime(2007, 1, 1, 9, 0, 0, 1010), freq="us")
|
| 317 |
+
assert i1 == expected
|
| 318 |
+
|
| 319 |
+
expected = Period("2007-01-01 09:00:00.00101", freq="us")
|
| 320 |
+
assert i1 == expected
|
| 321 |
+
|
| 322 |
+
def test_invalid_arguments(self):
|
| 323 |
+
msg = "Must supply freq for datetime value"
|
| 324 |
+
with pytest.raises(ValueError, match=msg):
|
| 325 |
+
Period(datetime.now())
|
| 326 |
+
with pytest.raises(ValueError, match=msg):
|
| 327 |
+
Period(datetime.now().date())
|
| 328 |
+
|
| 329 |
+
msg = "Value must be Period, string, integer, or datetime"
|
| 330 |
+
with pytest.raises(ValueError, match=msg):
|
| 331 |
+
Period(1.6, freq="D")
|
| 332 |
+
msg = "Ordinal must be an integer"
|
| 333 |
+
with pytest.raises(ValueError, match=msg):
|
| 334 |
+
Period(ordinal=1.6, freq="D")
|
| 335 |
+
msg = "Only value or ordinal but not both should be given but not both"
|
| 336 |
+
with pytest.raises(ValueError, match=msg):
|
| 337 |
+
Period(ordinal=2, value=1, freq="D")
|
| 338 |
+
|
| 339 |
+
msg = "If value is None, freq cannot be None"
|
| 340 |
+
with pytest.raises(ValueError, match=msg):
|
| 341 |
+
Period(month=1)
|
| 342 |
+
|
| 343 |
+
msg = '^Given date string "-2000" not likely a datetime$'
|
| 344 |
+
with pytest.raises(ValueError, match=msg):
|
| 345 |
+
Period("-2000", "Y")
|
| 346 |
+
if PY314:
|
| 347 |
+
msg = "day 0 must be in range 1..31 for month 1 in year 1: 0"
|
| 348 |
+
else:
|
| 349 |
+
msg = "day is out of range for month"
|
| 350 |
+
with pytest.raises(DateParseError, match=msg):
|
| 351 |
+
Period("0", "Y")
|
| 352 |
+
msg = "Unknown datetime string format, unable to parse"
|
| 353 |
+
with pytest.raises(DateParseError, match=msg):
|
| 354 |
+
Period("1/1/-2000", "Y")
|
| 355 |
+
|
| 356 |
+
def test_constructor_corner(self):
|
| 357 |
+
expected = Period("2007-01", freq="2M")
|
| 358 |
+
assert Period(year=2007, month=1, freq="2M") == expected
|
| 359 |
+
|
| 360 |
+
assert Period(None) is NaT
|
| 361 |
+
|
| 362 |
+
p = Period("2007-01-01", freq="D")
|
| 363 |
+
|
| 364 |
+
result = Period(p, freq="Y")
|
| 365 |
+
exp = Period("2007", freq="Y")
|
| 366 |
+
assert result == exp
|
| 367 |
+
|
| 368 |
+
def test_constructor_infer_freq(self):
|
| 369 |
+
p = Period("2007-01-01")
|
| 370 |
+
assert p.freq == "D"
|
| 371 |
+
|
| 372 |
+
p = Period("2007-01-01 07")
|
| 373 |
+
assert p.freq == "h"
|
| 374 |
+
|
| 375 |
+
p = Period("2007-01-01 07:10")
|
| 376 |
+
assert p.freq == "min"
|
| 377 |
+
|
| 378 |
+
p = Period("2007-01-01 07:10:15")
|
| 379 |
+
assert p.freq == "s"
|
| 380 |
+
|
| 381 |
+
p = Period("2007-01-01 07:10:15.123")
|
| 382 |
+
assert p.freq == "ms"
|
| 383 |
+
|
| 384 |
+
# We see that there are 6 digits after the decimal, so get microsecond
|
| 385 |
+
# even though they are all zeros.
|
| 386 |
+
p = Period("2007-01-01 07:10:15.123000")
|
| 387 |
+
assert p.freq == "us"
|
| 388 |
+
|
| 389 |
+
p = Period("2007-01-01 07:10:15.123400")
|
| 390 |
+
assert p.freq == "us"
|
| 391 |
+
|
| 392 |
+
def test_multiples(self):
|
| 393 |
+
result1 = Period("1989", freq="2Y")
|
| 394 |
+
result2 = Period("1989", freq="Y")
|
| 395 |
+
assert result1.ordinal == result2.ordinal
|
| 396 |
+
assert result1.freqstr == "2Y-DEC"
|
| 397 |
+
assert result2.freqstr == "Y-DEC"
|
| 398 |
+
assert result1.freq == offsets.YearEnd(2)
|
| 399 |
+
assert result2.freq == offsets.YearEnd()
|
| 400 |
+
|
| 401 |
+
assert (result1 + 1).ordinal == result1.ordinal + 2
|
| 402 |
+
assert (1 + result1).ordinal == result1.ordinal + 2
|
| 403 |
+
assert (result1 - 1).ordinal == result2.ordinal - 2
|
| 404 |
+
assert (-1 + result1).ordinal == result2.ordinal - 2
|
| 405 |
+
|
| 406 |
+
@pytest.mark.parametrize("month", MONTHS)
|
| 407 |
+
def test_period_cons_quarterly(self, month):
|
| 408 |
+
# bugs in scikits.timeseries
|
| 409 |
+
freq = f"Q-{month}"
|
| 410 |
+
exp = Period("1989Q3", freq=freq)
|
| 411 |
+
assert "1989Q3" in str(exp)
|
| 412 |
+
stamp = exp.to_timestamp("D", how="end")
|
| 413 |
+
p = Period(stamp, freq=freq)
|
| 414 |
+
assert p == exp
|
| 415 |
+
|
| 416 |
+
stamp = exp.to_timestamp("3D", how="end")
|
| 417 |
+
p = Period(stamp, freq=freq)
|
| 418 |
+
assert p == exp
|
| 419 |
+
|
| 420 |
+
@pytest.mark.parametrize("month", MONTHS)
|
| 421 |
+
def test_period_cons_annual(self, month):
|
| 422 |
+
# bugs in scikits.timeseries
|
| 423 |
+
freq = f"Y-{month}"
|
| 424 |
+
exp = Period("1989", freq=freq)
|
| 425 |
+
stamp = exp.to_timestamp("D", how="end") + timedelta(days=30)
|
| 426 |
+
p = Period(stamp, freq=freq)
|
| 427 |
+
|
| 428 |
+
assert p == exp + 1
|
| 429 |
+
assert isinstance(p, Period)
|
| 430 |
+
|
| 431 |
+
@pytest.mark.parametrize("day", DAYS)
|
| 432 |
+
@pytest.mark.parametrize("num", range(10, 17))
|
| 433 |
+
def test_period_cons_weekly(self, num, day):
|
| 434 |
+
daystr = f"2011-02-{num}"
|
| 435 |
+
freq = f"W-{day}"
|
| 436 |
+
|
| 437 |
+
result = Period(daystr, freq=freq)
|
| 438 |
+
expected = Period(daystr, freq="D").asfreq(freq)
|
| 439 |
+
assert result == expected
|
| 440 |
+
assert isinstance(result, Period)
|
| 441 |
+
|
| 442 |
+
def test_parse_week_str_roundstrip(self):
|
| 443 |
+
# GH#50803
|
| 444 |
+
per = Period("2017-01-23/2017-01-29")
|
| 445 |
+
assert per.freq.freqstr == "W-SUN"
|
| 446 |
+
|
| 447 |
+
per = Period("2017-01-24/2017-01-30")
|
| 448 |
+
assert per.freq.freqstr == "W-MON"
|
| 449 |
+
|
| 450 |
+
msg = "Could not parse as weekly-freq Period"
|
| 451 |
+
with pytest.raises(ValueError, match=msg):
|
| 452 |
+
# not 6 days apart
|
| 453 |
+
Period("2016-01-23/2017-01-29")
|
| 454 |
+
|
| 455 |
+
def test_period_from_ordinal(self):
|
| 456 |
+
p = Period("2011-01", freq="M")
|
| 457 |
+
res = Period._from_ordinal(p.ordinal, freq=p.freq)
|
| 458 |
+
assert p == res
|
| 459 |
+
assert isinstance(res, Period)
|
| 460 |
+
|
| 461 |
+
@pytest.mark.parametrize("freq", ["Y", "M", "D", "h"])
|
| 462 |
+
def test_construct_from_nat_string_and_freq(self, freq):
|
| 463 |
+
per = Period("NaT", freq=freq)
|
| 464 |
+
assert per is NaT
|
| 465 |
+
|
| 466 |
+
per = Period("NaT", freq="2" + freq)
|
| 467 |
+
assert per is NaT
|
| 468 |
+
|
| 469 |
+
per = Period("NaT", freq="3" + freq)
|
| 470 |
+
assert per is NaT
|
| 471 |
+
|
| 472 |
+
def test_period_cons_nat(self):
|
| 473 |
+
p = Period("nat", freq="W-SUN")
|
| 474 |
+
assert p is NaT
|
| 475 |
+
|
| 476 |
+
p = Period(iNaT, freq="D")
|
| 477 |
+
assert p is NaT
|
| 478 |
+
|
| 479 |
+
p = Period(iNaT, freq="3D")
|
| 480 |
+
assert p is NaT
|
| 481 |
+
|
| 482 |
+
p = Period(iNaT, freq="1D1h")
|
| 483 |
+
assert p is NaT
|
| 484 |
+
|
| 485 |
+
p = Period("NaT")
|
| 486 |
+
assert p is NaT
|
| 487 |
+
|
| 488 |
+
p = Period(iNaT)
|
| 489 |
+
assert p is NaT
|
| 490 |
+
|
| 491 |
+
def test_period_cons_mult(self):
|
| 492 |
+
p1 = Period("2011-01", freq="3M")
|
| 493 |
+
p2 = Period("2011-01", freq="M")
|
| 494 |
+
assert p1.ordinal == p2.ordinal
|
| 495 |
+
|
| 496 |
+
assert p1.freq == offsets.MonthEnd(3)
|
| 497 |
+
assert p1.freqstr == "3M"
|
| 498 |
+
|
| 499 |
+
assert p2.freq == offsets.MonthEnd()
|
| 500 |
+
assert p2.freqstr == "M"
|
| 501 |
+
|
| 502 |
+
result = p1 + 1
|
| 503 |
+
assert result.ordinal == (p2 + 3).ordinal
|
| 504 |
+
|
| 505 |
+
assert result.freq == p1.freq
|
| 506 |
+
assert result.freqstr == "3M"
|
| 507 |
+
|
| 508 |
+
result = p1 - 1
|
| 509 |
+
assert result.ordinal == (p2 - 3).ordinal
|
| 510 |
+
assert result.freq == p1.freq
|
| 511 |
+
assert result.freqstr == "3M"
|
| 512 |
+
|
| 513 |
+
msg = "Frequency must be positive, because it represents span: -3M"
|
| 514 |
+
with pytest.raises(ValueError, match=msg):
|
| 515 |
+
Period("2011-01", freq="-3M")
|
| 516 |
+
|
| 517 |
+
msg = "Frequency must be positive, because it represents span: 0M"
|
| 518 |
+
with pytest.raises(ValueError, match=msg):
|
| 519 |
+
Period("2011-01", freq="0M")
|
| 520 |
+
|
| 521 |
+
def test_period_cons_combined(self):
|
| 522 |
+
p = [
|
| 523 |
+
(
|
| 524 |
+
Period("2011-01", freq="1D1h"),
|
| 525 |
+
Period("2011-01", freq="1h1D"),
|
| 526 |
+
Period("2011-01", freq="h"),
|
| 527 |
+
),
|
| 528 |
+
(
|
| 529 |
+
Period(ordinal=1, freq="1D1h"),
|
| 530 |
+
Period(ordinal=1, freq="1h1D"),
|
| 531 |
+
Period(ordinal=1, freq="h"),
|
| 532 |
+
),
|
| 533 |
+
]
|
| 534 |
+
|
| 535 |
+
for p1, p2, p3 in p:
|
| 536 |
+
assert p1.ordinal == p3.ordinal
|
| 537 |
+
assert p2.ordinal == p3.ordinal
|
| 538 |
+
|
| 539 |
+
assert p1.freq == offsets.Hour(25)
|
| 540 |
+
assert p1.freqstr == "25h"
|
| 541 |
+
|
| 542 |
+
assert p2.freq == offsets.Hour(25)
|
| 543 |
+
assert p2.freqstr == "25h"
|
| 544 |
+
|
| 545 |
+
assert p3.freq == offsets.Hour()
|
| 546 |
+
assert p3.freqstr == "h"
|
| 547 |
+
|
| 548 |
+
result = p1 + 1
|
| 549 |
+
assert result.ordinal == (p3 + 25).ordinal
|
| 550 |
+
assert result.freq == p1.freq
|
| 551 |
+
assert result.freqstr == "25h"
|
| 552 |
+
|
| 553 |
+
result = p2 + 1
|
| 554 |
+
assert result.ordinal == (p3 + 25).ordinal
|
| 555 |
+
assert result.freq == p2.freq
|
| 556 |
+
assert result.freqstr == "25h"
|
| 557 |
+
|
| 558 |
+
result = p1 - 1
|
| 559 |
+
assert result.ordinal == (p3 - 25).ordinal
|
| 560 |
+
assert result.freq == p1.freq
|
| 561 |
+
assert result.freqstr == "25h"
|
| 562 |
+
|
| 563 |
+
result = p2 - 1
|
| 564 |
+
assert result.ordinal == (p3 - 25).ordinal
|
| 565 |
+
assert result.freq == p2.freq
|
| 566 |
+
assert result.freqstr == "25h"
|
| 567 |
+
|
| 568 |
+
msg = "Frequency must be positive, because it represents span: -25h"
|
| 569 |
+
with pytest.raises(ValueError, match=msg):
|
| 570 |
+
Period("2011-01", freq="-1D1h")
|
| 571 |
+
with pytest.raises(ValueError, match=msg):
|
| 572 |
+
Period("2011-01", freq="-1h1D")
|
| 573 |
+
with pytest.raises(ValueError, match=msg):
|
| 574 |
+
Period(ordinal=1, freq="-1D1h")
|
| 575 |
+
with pytest.raises(ValueError, match=msg):
|
| 576 |
+
Period(ordinal=1, freq="-1h1D")
|
| 577 |
+
|
| 578 |
+
msg = "Frequency must be positive, because it represents span: 0D"
|
| 579 |
+
with pytest.raises(ValueError, match=msg):
|
| 580 |
+
Period("2011-01", freq="0D0h")
|
| 581 |
+
with pytest.raises(ValueError, match=msg):
|
| 582 |
+
Period(ordinal=1, freq="0D0h")
|
| 583 |
+
|
| 584 |
+
# You can only combine together day and intraday offsets
|
| 585 |
+
msg = "Invalid frequency: 1W1D"
|
| 586 |
+
with pytest.raises(ValueError, match=msg):
|
| 587 |
+
Period("2011-01", freq="1W1D")
|
| 588 |
+
msg = "Invalid frequency: 1D1W"
|
| 589 |
+
with pytest.raises(ValueError, match=msg):
|
| 590 |
+
Period("2011-01", freq="1D1W")
|
| 591 |
+
|
| 592 |
+
@pytest.mark.parametrize("day", ["1970/01/01 ", "2020-12-31 ", "1981/09/13 "])
|
| 593 |
+
@pytest.mark.parametrize("hour", ["00:00:00", "00:00:01", "23:59:59", "12:00:59"])
|
| 594 |
+
@pytest.mark.parametrize(
|
| 595 |
+
"sec_float, expected",
|
| 596 |
+
[
|
| 597 |
+
(".000000001", 1),
|
| 598 |
+
(".000000999", 999),
|
| 599 |
+
(".123456789", 789),
|
| 600 |
+
(".999999999", 999),
|
| 601 |
+
(".999999000", 0),
|
| 602 |
+
# Test femtoseconds, attoseconds, picoseconds are dropped like Timestamp
|
| 603 |
+
(".999999001123", 1),
|
| 604 |
+
(".999999001123456", 1),
|
| 605 |
+
(".999999001123456789", 1),
|
| 606 |
+
],
|
| 607 |
+
)
|
| 608 |
+
def test_period_constructor_nanosecond(self, day, hour, sec_float, expected):
|
| 609 |
+
# GH 34621
|
| 610 |
+
|
| 611 |
+
assert Period(day + hour + sec_float).start_time.nanosecond == expected
|
| 612 |
+
|
| 613 |
+
@pytest.mark.parametrize("hour", range(24))
|
| 614 |
+
def test_period_large_ordinal(self, hour):
|
| 615 |
+
# Issue #36430
|
| 616 |
+
# Integer overflow for Period over the maximum timestamp
|
| 617 |
+
p = Period(ordinal=2562048 + hour, freq="1h")
|
| 618 |
+
assert p.hour == hour
|
| 619 |
+
|
| 620 |
+
|
| 621 |
+
class TestPeriodMethods:
|
| 622 |
+
def test_round_trip(self):
|
| 623 |
+
p = Period("2000Q1")
|
| 624 |
+
new_p = tm.round_trip_pickle(p)
|
| 625 |
+
assert new_p == p
|
| 626 |
+
|
| 627 |
+
def test_hash(self):
|
| 628 |
+
assert hash(Period("2011-01", freq="M")) == hash(Period("2011-01", freq="M"))
|
| 629 |
+
|
| 630 |
+
assert hash(Period("2011-01-01", freq="D")) != hash(Period("2011-01", freq="M"))
|
| 631 |
+
|
| 632 |
+
assert hash(Period("2011-01", freq="3M")) != hash(Period("2011-01", freq="2M"))
|
| 633 |
+
|
| 634 |
+
assert hash(Period("2011-01", freq="M")) != hash(Period("2011-02", freq="M"))
|
| 635 |
+
|
| 636 |
+
# --------------------------------------------------------------
|
| 637 |
+
# to_timestamp
|
| 638 |
+
|
| 639 |
+
def test_to_timestamp_mult(self):
|
| 640 |
+
p = Period("2011-01", freq="M")
|
| 641 |
+
assert p.to_timestamp(how="S") == Timestamp("2011-01-01")
|
| 642 |
+
expected = Timestamp("2011-02-01") - Timedelta(1, "ns")
|
| 643 |
+
assert p.to_timestamp(how="E") == expected
|
| 644 |
+
|
| 645 |
+
p = Period("2011-01", freq="3M")
|
| 646 |
+
assert p.to_timestamp(how="S") == Timestamp("2011-01-01")
|
| 647 |
+
expected = Timestamp("2011-04-01") - Timedelta(1, "ns")
|
| 648 |
+
assert p.to_timestamp(how="E") == expected
|
| 649 |
+
|
| 650 |
+
@pytest.mark.filterwarnings(
|
| 651 |
+
"ignore:Period with BDay freq is deprecated:FutureWarning"
|
| 652 |
+
)
|
| 653 |
+
def test_to_timestamp(self):
|
| 654 |
+
p = Period("1982", freq="Y")
|
| 655 |
+
start_ts = p.to_timestamp(how="S")
|
| 656 |
+
aliases = ["s", "StarT", "BEGIn"]
|
| 657 |
+
for a in aliases:
|
| 658 |
+
assert start_ts == p.to_timestamp("D", how=a)
|
| 659 |
+
# freq with mult should not affect to the result
|
| 660 |
+
assert start_ts == p.to_timestamp("3D", how=a)
|
| 661 |
+
|
| 662 |
+
end_ts = p.to_timestamp(how="E")
|
| 663 |
+
aliases = ["e", "end", "FINIsH"]
|
| 664 |
+
for a in aliases:
|
| 665 |
+
assert end_ts == p.to_timestamp("D", how=a)
|
| 666 |
+
assert end_ts == p.to_timestamp("3D", how=a)
|
| 667 |
+
|
| 668 |
+
from_lst = ["Y", "Q", "M", "W", "B", "D", "h", "Min", "s"]
|
| 669 |
+
|
| 670 |
+
def _ex(p):
|
| 671 |
+
if p.freq == "B":
|
| 672 |
+
return p.start_time + Timedelta(days=1, nanoseconds=-1)
|
| 673 |
+
return Timestamp((p + p.freq).start_time._value - 1)
|
| 674 |
+
|
| 675 |
+
for fcode in from_lst:
|
| 676 |
+
p = Period("1982", freq=fcode)
|
| 677 |
+
result = p.to_timestamp().to_period(fcode)
|
| 678 |
+
assert result == p
|
| 679 |
+
|
| 680 |
+
assert p.start_time == p.to_timestamp(how="S")
|
| 681 |
+
|
| 682 |
+
assert p.end_time == _ex(p)
|
| 683 |
+
|
| 684 |
+
# Frequency other than daily
|
| 685 |
+
|
| 686 |
+
p = Period("1985", freq="Y")
|
| 687 |
+
|
| 688 |
+
result = p.to_timestamp("h", how="end")
|
| 689 |
+
expected = Timestamp(1986, 1, 1) - Timedelta(1, "ns")
|
| 690 |
+
assert result == expected
|
| 691 |
+
result = p.to_timestamp("3h", how="end")
|
| 692 |
+
assert result == expected
|
| 693 |
+
|
| 694 |
+
result = p.to_timestamp("min", how="end")
|
| 695 |
+
expected = Timestamp(1986, 1, 1) - Timedelta(1, "ns")
|
| 696 |
+
assert result == expected
|
| 697 |
+
result = p.to_timestamp("2min", how="end")
|
| 698 |
+
assert result == expected
|
| 699 |
+
|
| 700 |
+
result = p.to_timestamp(how="end")
|
| 701 |
+
expected = Timestamp(1986, 1, 1) - Timedelta(1, "ns")
|
| 702 |
+
assert result == expected
|
| 703 |
+
|
| 704 |
+
expected = datetime(1985, 1, 1)
|
| 705 |
+
result = p.to_timestamp("h", how="start")
|
| 706 |
+
assert result == expected
|
| 707 |
+
result = p.to_timestamp("min", how="start")
|
| 708 |
+
assert result == expected
|
| 709 |
+
result = p.to_timestamp("s", how="start")
|
| 710 |
+
assert result == expected
|
| 711 |
+
result = p.to_timestamp("3h", how="start")
|
| 712 |
+
assert result == expected
|
| 713 |
+
result = p.to_timestamp("5s", how="start")
|
| 714 |
+
assert result == expected
|
| 715 |
+
|
| 716 |
+
def test_to_timestamp_business_end(self):
|
| 717 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 718 |
+
per = Period("1990-01-05", "B") # Friday
|
| 719 |
+
result = per.to_timestamp("B", how="E")
|
| 720 |
+
|
| 721 |
+
expected = Timestamp("1990-01-06") - Timedelta(nanoseconds=1)
|
| 722 |
+
assert result == expected
|
| 723 |
+
|
| 724 |
+
@pytest.mark.parametrize(
|
| 725 |
+
"ts, expected",
|
| 726 |
+
[
|
| 727 |
+
("1970-01-01 00:00:00", 0),
|
| 728 |
+
("1970-01-01 00:00:00.000001", 1),
|
| 729 |
+
("1970-01-01 00:00:00.00001", 10),
|
| 730 |
+
("1970-01-01 00:00:00.499", 499000),
|
| 731 |
+
("1999-12-31 23:59:59.999", 999000),
|
| 732 |
+
("1999-12-31 23:59:59.999999", 999999),
|
| 733 |
+
("2050-12-31 23:59:59.5", 500000),
|
| 734 |
+
("2050-12-31 23:59:59.500001", 500001),
|
| 735 |
+
("2050-12-31 23:59:59.123456", 123456),
|
| 736 |
+
],
|
| 737 |
+
)
|
| 738 |
+
@pytest.mark.parametrize("freq", [None, "us", "ns"])
|
| 739 |
+
def test_to_timestamp_microsecond(self, ts, expected, freq):
|
| 740 |
+
# GH 24444
|
| 741 |
+
result = Period(ts).to_timestamp(freq=freq).microsecond
|
| 742 |
+
assert result == expected
|
| 743 |
+
|
| 744 |
+
# --------------------------------------------------------------
|
| 745 |
+
# Rendering: __repr__, strftime, etc
|
| 746 |
+
|
| 747 |
+
@pytest.mark.parametrize(
|
| 748 |
+
"str_ts,freq,str_res,str_freq",
|
| 749 |
+
(
|
| 750 |
+
("Jan-2000", None, "2000-01", "M"),
|
| 751 |
+
("2000-12-15", None, "2000-12-15", "D"),
|
| 752 |
+
(
|
| 753 |
+
"2000-12-15 13:45:26.123456789",
|
| 754 |
+
"ns",
|
| 755 |
+
"2000-12-15 13:45:26.123456789",
|
| 756 |
+
"ns",
|
| 757 |
+
),
|
| 758 |
+
("2000-12-15 13:45:26.123456789", "us", "2000-12-15 13:45:26.123456", "us"),
|
| 759 |
+
("2000-12-15 13:45:26.123456", None, "2000-12-15 13:45:26.123456", "us"),
|
| 760 |
+
("2000-12-15 13:45:26.123456789", "ms", "2000-12-15 13:45:26.123", "ms"),
|
| 761 |
+
("2000-12-15 13:45:26.123", None, "2000-12-15 13:45:26.123", "ms"),
|
| 762 |
+
("2000-12-15 13:45:26", "s", "2000-12-15 13:45:26", "s"),
|
| 763 |
+
("2000-12-15 13:45:26", "min", "2000-12-15 13:45", "min"),
|
| 764 |
+
("2000-12-15 13:45:26", "h", "2000-12-15 13:00", "h"),
|
| 765 |
+
("2000-12-15", "Y", "2000", "Y-DEC"),
|
| 766 |
+
("2000-12-15", "Q", "2000Q4", "Q-DEC"),
|
| 767 |
+
("2000-12-15", "M", "2000-12", "M"),
|
| 768 |
+
("2000-12-15", "W", "2000-12-11/2000-12-17", "W-SUN"),
|
| 769 |
+
("2000-12-15", "D", "2000-12-15", "D"),
|
| 770 |
+
("2000-12-15", "B", "2000-12-15", "B"),
|
| 771 |
+
),
|
| 772 |
+
)
|
| 773 |
+
@pytest.mark.filterwarnings(
|
| 774 |
+
"ignore:Period with BDay freq is deprecated:FutureWarning"
|
| 775 |
+
)
|
| 776 |
+
def test_repr(self, str_ts, freq, str_res, str_freq):
|
| 777 |
+
p = Period(str_ts, freq=freq)
|
| 778 |
+
assert str(p) == str_res
|
| 779 |
+
assert repr(p) == f"Period('{str_res}', '{str_freq}')"
|
| 780 |
+
|
| 781 |
+
def test_repr_nat(self):
|
| 782 |
+
p = Period("nat", freq="M")
|
| 783 |
+
assert repr(NaT) in repr(p)
|
| 784 |
+
|
| 785 |
+
def test_strftime(self):
|
| 786 |
+
# GH#3363
|
| 787 |
+
p = Period("2000-1-1 12:34:12", freq="s")
|
| 788 |
+
res = p.strftime("%Y-%m-%d %H:%M:%S")
|
| 789 |
+
assert res == "2000-01-01 12:34:12"
|
| 790 |
+
assert isinstance(res, str)
|
| 791 |
+
|
| 792 |
+
|
| 793 |
+
class TestPeriodProperties:
|
| 794 |
+
"""Test properties such as year, month, weekday, etc...."""
|
| 795 |
+
|
| 796 |
+
@pytest.mark.parametrize("freq", ["Y", "M", "D", "h"])
|
| 797 |
+
def test_is_leap_year(self, freq):
|
| 798 |
+
# GH 13727
|
| 799 |
+
p = Period("2000-01-01 00:00:00", freq=freq)
|
| 800 |
+
assert p.is_leap_year
|
| 801 |
+
assert isinstance(p.is_leap_year, bool)
|
| 802 |
+
|
| 803 |
+
p = Period("1999-01-01 00:00:00", freq=freq)
|
| 804 |
+
assert not p.is_leap_year
|
| 805 |
+
|
| 806 |
+
p = Period("2004-01-01 00:00:00", freq=freq)
|
| 807 |
+
assert p.is_leap_year
|
| 808 |
+
|
| 809 |
+
p = Period("2100-01-01 00:00:00", freq=freq)
|
| 810 |
+
assert not p.is_leap_year
|
| 811 |
+
|
| 812 |
+
def test_quarterly_negative_ordinals(self):
|
| 813 |
+
p = Period(ordinal=-1, freq="Q-DEC")
|
| 814 |
+
assert p.year == 1969
|
| 815 |
+
assert p.quarter == 4
|
| 816 |
+
assert isinstance(p, Period)
|
| 817 |
+
|
| 818 |
+
p = Period(ordinal=-2, freq="Q-DEC")
|
| 819 |
+
assert p.year == 1969
|
| 820 |
+
assert p.quarter == 3
|
| 821 |
+
assert isinstance(p, Period)
|
| 822 |
+
|
| 823 |
+
p = Period(ordinal=-2, freq="M")
|
| 824 |
+
assert p.year == 1969
|
| 825 |
+
assert p.month == 11
|
| 826 |
+
assert isinstance(p, Period)
|
| 827 |
+
|
| 828 |
+
def test_freq_str(self):
|
| 829 |
+
i1 = Period("1982", freq="Min")
|
| 830 |
+
assert i1.freq == offsets.Minute()
|
| 831 |
+
assert i1.freqstr == "min"
|
| 832 |
+
|
| 833 |
+
@pytest.mark.filterwarnings(
|
| 834 |
+
"ignore:Period with BDay freq is deprecated:FutureWarning"
|
| 835 |
+
)
|
| 836 |
+
def test_period_deprecated_freq(self):
|
| 837 |
+
cases = {
|
| 838 |
+
"M": ["MTH", "MONTH", "MONTHLY", "Mth", "month", "monthly"],
|
| 839 |
+
"B": ["BUS", "BUSINESS", "BUSINESSLY", "WEEKDAY", "bus"],
|
| 840 |
+
"D": ["DAY", "DLY", "DAILY", "Day", "Dly", "Daily"],
|
| 841 |
+
"h": ["HR", "HOUR", "HRLY", "HOURLY", "hr", "Hour", "HRly"],
|
| 842 |
+
"min": ["minute", "MINUTE", "MINUTELY", "minutely"],
|
| 843 |
+
"s": ["sec", "SEC", "SECOND", "SECONDLY", "second"],
|
| 844 |
+
"ms": ["MILLISECOND", "MILLISECONDLY", "millisecond"],
|
| 845 |
+
"us": ["MICROSECOND", "MICROSECONDLY", "microsecond"],
|
| 846 |
+
"ns": ["NANOSECOND", "NANOSECONDLY", "nanosecond"],
|
| 847 |
+
}
|
| 848 |
+
|
| 849 |
+
msg = INVALID_FREQ_ERR_MSG
|
| 850 |
+
for exp, freqs in cases.items():
|
| 851 |
+
for freq in freqs:
|
| 852 |
+
with pytest.raises(ValueError, match=msg):
|
| 853 |
+
Period("2016-03-01 09:00", freq=freq)
|
| 854 |
+
with pytest.raises(ValueError, match=msg):
|
| 855 |
+
Period(ordinal=1, freq=freq)
|
| 856 |
+
|
| 857 |
+
# check supported freq-aliases still works
|
| 858 |
+
p1 = Period("2016-03-01 09:00", freq=exp)
|
| 859 |
+
p2 = Period(ordinal=1, freq=exp)
|
| 860 |
+
assert isinstance(p1, Period)
|
| 861 |
+
assert isinstance(p2, Period)
|
| 862 |
+
|
| 863 |
+
@staticmethod
|
| 864 |
+
def _period_constructor(bound, offset):
|
| 865 |
+
return Period(
|
| 866 |
+
year=bound.year,
|
| 867 |
+
month=bound.month,
|
| 868 |
+
day=bound.day,
|
| 869 |
+
hour=bound.hour,
|
| 870 |
+
minute=bound.minute,
|
| 871 |
+
second=bound.second + offset,
|
| 872 |
+
freq="us",
|
| 873 |
+
)
|
| 874 |
+
|
| 875 |
+
@pytest.mark.parametrize("bound, offset", [(Timestamp.min, -1), (Timestamp.max, 1)])
|
| 876 |
+
@pytest.mark.parametrize("period_property", ["start_time", "end_time"])
|
| 877 |
+
def test_outer_bounds_start_and_end_time(self, bound, offset, period_property):
|
| 878 |
+
# GH #13346
|
| 879 |
+
period = TestPeriodProperties._period_constructor(bound, offset)
|
| 880 |
+
with pytest.raises(OutOfBoundsDatetime, match="Out of bounds nanosecond"):
|
| 881 |
+
getattr(period, period_property)
|
| 882 |
+
|
| 883 |
+
@pytest.mark.parametrize("bound, offset", [(Timestamp.min, -1), (Timestamp.max, 1)])
|
| 884 |
+
@pytest.mark.parametrize("period_property", ["start_time", "end_time"])
|
| 885 |
+
def test_inner_bounds_start_and_end_time(self, bound, offset, period_property):
|
| 886 |
+
# GH #13346
|
| 887 |
+
period = TestPeriodProperties._period_constructor(bound, -offset)
|
| 888 |
+
expected = period.to_timestamp().round(freq="s")
|
| 889 |
+
assert getattr(period, period_property).round(freq="s") == expected
|
| 890 |
+
expected = (bound - offset * Timedelta(1, unit="s")).floor("s")
|
| 891 |
+
assert getattr(period, period_property).floor("s") == expected
|
| 892 |
+
|
| 893 |
+
def test_start_time(self):
|
| 894 |
+
freq_lst = ["Y", "Q", "M", "D", "h", "min", "s"]
|
| 895 |
+
xp = datetime(2012, 1, 1)
|
| 896 |
+
for f in freq_lst:
|
| 897 |
+
p = Period("2012", freq=f)
|
| 898 |
+
assert p.start_time == xp
|
| 899 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 900 |
+
assert Period("2012", freq="B").start_time == datetime(2012, 1, 2)
|
| 901 |
+
assert Period("2012", freq="W").start_time == datetime(2011, 12, 26)
|
| 902 |
+
|
| 903 |
+
def test_end_time(self):
|
| 904 |
+
p = Period("2012", freq="Y")
|
| 905 |
+
|
| 906 |
+
def _ex(*args):
|
| 907 |
+
return Timestamp(Timestamp(datetime(*args)).as_unit("ns")._value - 1)
|
| 908 |
+
|
| 909 |
+
xp = _ex(2013, 1, 1)
|
| 910 |
+
assert xp == p.end_time
|
| 911 |
+
|
| 912 |
+
p = Period("2012", freq="Q")
|
| 913 |
+
xp = _ex(2012, 4, 1)
|
| 914 |
+
assert xp == p.end_time
|
| 915 |
+
|
| 916 |
+
p = Period("2012", freq="M")
|
| 917 |
+
xp = _ex(2012, 2, 1)
|
| 918 |
+
assert xp == p.end_time
|
| 919 |
+
|
| 920 |
+
p = Period("2012", freq="D")
|
| 921 |
+
xp = _ex(2012, 1, 2)
|
| 922 |
+
assert xp == p.end_time
|
| 923 |
+
|
| 924 |
+
p = Period("2012", freq="h")
|
| 925 |
+
xp = _ex(2012, 1, 1, 1)
|
| 926 |
+
assert xp == p.end_time
|
| 927 |
+
|
| 928 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 929 |
+
p = Period("2012", freq="B")
|
| 930 |
+
xp = _ex(2012, 1, 3)
|
| 931 |
+
assert xp == p.end_time
|
| 932 |
+
|
| 933 |
+
p = Period("2012", freq="W")
|
| 934 |
+
xp = _ex(2012, 1, 2)
|
| 935 |
+
assert xp == p.end_time
|
| 936 |
+
|
| 937 |
+
# Test for GH 11738
|
| 938 |
+
p = Period("2012", freq="15D")
|
| 939 |
+
xp = _ex(2012, 1, 16)
|
| 940 |
+
assert xp == p.end_time
|
| 941 |
+
|
| 942 |
+
p = Period("2012", freq="1D1h")
|
| 943 |
+
xp = _ex(2012, 1, 2, 1)
|
| 944 |
+
assert xp == p.end_time
|
| 945 |
+
|
| 946 |
+
p = Period("2012", freq="1h1D")
|
| 947 |
+
xp = _ex(2012, 1, 2, 1)
|
| 948 |
+
assert xp == p.end_time
|
| 949 |
+
|
| 950 |
+
def test_end_time_business_friday(self):
|
| 951 |
+
# GH#34449
|
| 952 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 953 |
+
per = Period("1990-01-05", "B")
|
| 954 |
+
result = per.end_time
|
| 955 |
+
|
| 956 |
+
expected = Timestamp("1990-01-06") - Timedelta(nanoseconds=1)
|
| 957 |
+
assert result == expected
|
| 958 |
+
|
| 959 |
+
def test_anchor_week_end_time(self):
|
| 960 |
+
def _ex(*args):
|
| 961 |
+
return Timestamp(Timestamp(datetime(*args)).as_unit("ns")._value - 1)
|
| 962 |
+
|
| 963 |
+
p = Period("2013-1-1", "W-SAT")
|
| 964 |
+
xp = _ex(2013, 1, 6)
|
| 965 |
+
assert p.end_time == xp
|
| 966 |
+
|
| 967 |
+
def test_properties_annually(self):
|
| 968 |
+
# Test properties on Periods with annually frequency.
|
| 969 |
+
a_date = Period(freq="Y", year=2007)
|
| 970 |
+
assert a_date.year == 2007
|
| 971 |
+
|
| 972 |
+
def test_properties_quarterly(self):
|
| 973 |
+
# Test properties on Periods with daily frequency.
|
| 974 |
+
qedec_date = Period(freq="Q-DEC", year=2007, quarter=1)
|
| 975 |
+
qejan_date = Period(freq="Q-JAN", year=2007, quarter=1)
|
| 976 |
+
qejun_date = Period(freq="Q-JUN", year=2007, quarter=1)
|
| 977 |
+
#
|
| 978 |
+
for x in range(3):
|
| 979 |
+
for qd in (qedec_date, qejan_date, qejun_date):
|
| 980 |
+
assert (qd + x).qyear == 2007
|
| 981 |
+
assert (qd + x).quarter == x + 1
|
| 982 |
+
|
| 983 |
+
def test_properties_monthly(self):
|
| 984 |
+
# Test properties on Periods with daily frequency.
|
| 985 |
+
m_date = Period(freq="M", year=2007, month=1)
|
| 986 |
+
for x in range(11):
|
| 987 |
+
m_ival_x = m_date + x
|
| 988 |
+
assert m_ival_x.year == 2007
|
| 989 |
+
if 1 <= x + 1 <= 3:
|
| 990 |
+
assert m_ival_x.quarter == 1
|
| 991 |
+
elif 4 <= x + 1 <= 6:
|
| 992 |
+
assert m_ival_x.quarter == 2
|
| 993 |
+
elif 7 <= x + 1 <= 9:
|
| 994 |
+
assert m_ival_x.quarter == 3
|
| 995 |
+
elif 10 <= x + 1 <= 12:
|
| 996 |
+
assert m_ival_x.quarter == 4
|
| 997 |
+
assert m_ival_x.month == x + 1
|
| 998 |
+
|
| 999 |
+
def test_properties_weekly(self):
|
| 1000 |
+
# Test properties on Periods with daily frequency.
|
| 1001 |
+
w_date = Period(freq="W", year=2007, month=1, day=7)
|
| 1002 |
+
#
|
| 1003 |
+
assert w_date.year == 2007
|
| 1004 |
+
assert w_date.quarter == 1
|
| 1005 |
+
assert w_date.month == 1
|
| 1006 |
+
assert w_date.week == 1
|
| 1007 |
+
assert (w_date - 1).week == 52
|
| 1008 |
+
assert w_date.days_in_month == 31
|
| 1009 |
+
assert Period(freq="W", year=2012, month=2, day=1).days_in_month == 29
|
| 1010 |
+
|
| 1011 |
+
def test_properties_weekly_legacy(self):
|
| 1012 |
+
# Test properties on Periods with daily frequency.
|
| 1013 |
+
w_date = Period(freq="W", year=2007, month=1, day=7)
|
| 1014 |
+
assert w_date.year == 2007
|
| 1015 |
+
assert w_date.quarter == 1
|
| 1016 |
+
assert w_date.month == 1
|
| 1017 |
+
assert w_date.week == 1
|
| 1018 |
+
assert (w_date - 1).week == 52
|
| 1019 |
+
assert w_date.days_in_month == 31
|
| 1020 |
+
|
| 1021 |
+
exp = Period(freq="W", year=2012, month=2, day=1)
|
| 1022 |
+
assert exp.days_in_month == 29
|
| 1023 |
+
|
| 1024 |
+
msg = INVALID_FREQ_ERR_MSG
|
| 1025 |
+
with pytest.raises(ValueError, match=msg):
|
| 1026 |
+
Period(freq="WK", year=2007, month=1, day=7)
|
| 1027 |
+
|
| 1028 |
+
def test_properties_daily(self):
|
| 1029 |
+
# Test properties on Periods with daily frequency.
|
| 1030 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 1031 |
+
b_date = Period(freq="B", year=2007, month=1, day=1)
|
| 1032 |
+
#
|
| 1033 |
+
assert b_date.year == 2007
|
| 1034 |
+
assert b_date.quarter == 1
|
| 1035 |
+
assert b_date.month == 1
|
| 1036 |
+
assert b_date.day == 1
|
| 1037 |
+
assert b_date.weekday == 0
|
| 1038 |
+
assert b_date.dayofyear == 1
|
| 1039 |
+
assert b_date.days_in_month == 31
|
| 1040 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 1041 |
+
assert Period(freq="B", year=2012, month=2, day=1).days_in_month == 29
|
| 1042 |
+
|
| 1043 |
+
d_date = Period(freq="D", year=2007, month=1, day=1)
|
| 1044 |
+
|
| 1045 |
+
assert d_date.year == 2007
|
| 1046 |
+
assert d_date.quarter == 1
|
| 1047 |
+
assert d_date.month == 1
|
| 1048 |
+
assert d_date.day == 1
|
| 1049 |
+
assert d_date.weekday == 0
|
| 1050 |
+
assert d_date.dayofyear == 1
|
| 1051 |
+
assert d_date.days_in_month == 31
|
| 1052 |
+
assert Period(freq="D", year=2012, month=2, day=1).days_in_month == 29
|
| 1053 |
+
|
| 1054 |
+
def test_properties_hourly(self):
|
| 1055 |
+
# Test properties on Periods with hourly frequency.
|
| 1056 |
+
h_date1 = Period(freq="h", year=2007, month=1, day=1, hour=0)
|
| 1057 |
+
h_date2 = Period(freq="2h", year=2007, month=1, day=1, hour=0)
|
| 1058 |
+
|
| 1059 |
+
for h_date in [h_date1, h_date2]:
|
| 1060 |
+
assert h_date.year == 2007
|
| 1061 |
+
assert h_date.quarter == 1
|
| 1062 |
+
assert h_date.month == 1
|
| 1063 |
+
assert h_date.day == 1
|
| 1064 |
+
assert h_date.weekday == 0
|
| 1065 |
+
assert h_date.dayofyear == 1
|
| 1066 |
+
assert h_date.hour == 0
|
| 1067 |
+
assert h_date.days_in_month == 31
|
| 1068 |
+
assert (
|
| 1069 |
+
Period(freq="h", year=2012, month=2, day=1, hour=0).days_in_month == 29
|
| 1070 |
+
)
|
| 1071 |
+
|
| 1072 |
+
def test_properties_minutely(self):
|
| 1073 |
+
# Test properties on Periods with minutely frequency.
|
| 1074 |
+
t_date = Period(freq="Min", year=2007, month=1, day=1, hour=0, minute=0)
|
| 1075 |
+
#
|
| 1076 |
+
assert t_date.quarter == 1
|
| 1077 |
+
assert t_date.month == 1
|
| 1078 |
+
assert t_date.day == 1
|
| 1079 |
+
assert t_date.weekday == 0
|
| 1080 |
+
assert t_date.dayofyear == 1
|
| 1081 |
+
assert t_date.hour == 0
|
| 1082 |
+
assert t_date.minute == 0
|
| 1083 |
+
assert t_date.days_in_month == 31
|
| 1084 |
+
assert (
|
| 1085 |
+
Period(freq="D", year=2012, month=2, day=1, hour=0, minute=0).days_in_month
|
| 1086 |
+
== 29
|
| 1087 |
+
)
|
| 1088 |
+
|
| 1089 |
+
def test_properties_secondly(self):
|
| 1090 |
+
# Test properties on Periods with secondly frequency.
|
| 1091 |
+
s_date = Period(
|
| 1092 |
+
freq="Min", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
| 1093 |
+
)
|
| 1094 |
+
#
|
| 1095 |
+
assert s_date.year == 2007
|
| 1096 |
+
assert s_date.quarter == 1
|
| 1097 |
+
assert s_date.month == 1
|
| 1098 |
+
assert s_date.day == 1
|
| 1099 |
+
assert s_date.weekday == 0
|
| 1100 |
+
assert s_date.dayofyear == 1
|
| 1101 |
+
assert s_date.hour == 0
|
| 1102 |
+
assert s_date.minute == 0
|
| 1103 |
+
assert s_date.second == 0
|
| 1104 |
+
assert s_date.days_in_month == 31
|
| 1105 |
+
assert (
|
| 1106 |
+
Period(
|
| 1107 |
+
freq="Min", year=2012, month=2, day=1, hour=0, minute=0, second=0
|
| 1108 |
+
).days_in_month
|
| 1109 |
+
== 29
|
| 1110 |
+
)
|
| 1111 |
+
|
| 1112 |
+
|
| 1113 |
+
class TestPeriodComparisons:
|
| 1114 |
+
def test_sort_periods(self):
|
| 1115 |
+
jan = Period("2000-01", "M")
|
| 1116 |
+
feb = Period("2000-02", "M")
|
| 1117 |
+
mar = Period("2000-03", "M")
|
| 1118 |
+
periods = [mar, jan, feb]
|
| 1119 |
+
correctPeriods = [jan, feb, mar]
|
| 1120 |
+
assert sorted(periods) == correctPeriods
|
| 1121 |
+
|
| 1122 |
+
|
| 1123 |
+
def test_period_immutable():
|
| 1124 |
+
# see gh-17116
|
| 1125 |
+
msg = "not writable"
|
| 1126 |
+
|
| 1127 |
+
per = Period("2014Q1")
|
| 1128 |
+
with pytest.raises(AttributeError, match=msg):
|
| 1129 |
+
per.ordinal = 14
|
| 1130 |
+
|
| 1131 |
+
freq = per.freq
|
| 1132 |
+
with pytest.raises(AttributeError, match=msg):
|
| 1133 |
+
per.freq = 2 * freq
|
| 1134 |
+
|
| 1135 |
+
|
| 1136 |
+
def test_small_year_parsing():
|
| 1137 |
+
per1 = Period("0001-01-07", "D")
|
| 1138 |
+
assert per1.year == 1
|
| 1139 |
+
assert per1.day == 7
|
| 1140 |
+
|
| 1141 |
+
|
| 1142 |
+
def test_negone_ordinals():
|
| 1143 |
+
freqs = ["Y", "M", "Q", "D", "h", "min", "s"]
|
| 1144 |
+
|
| 1145 |
+
period = Period(ordinal=-1, freq="D")
|
| 1146 |
+
for freq in freqs:
|
| 1147 |
+
repr(period.asfreq(freq))
|
| 1148 |
+
|
| 1149 |
+
for freq in freqs:
|
| 1150 |
+
period = Period(ordinal=-1, freq=freq)
|
| 1151 |
+
repr(period)
|
| 1152 |
+
assert period.year == 1969
|
| 1153 |
+
|
| 1154 |
+
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
| 1155 |
+
period = Period(ordinal=-1, freq="B")
|
| 1156 |
+
repr(period)
|
| 1157 |
+
period = Period(ordinal=-1, freq="W")
|
| 1158 |
+
repr(period)
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/__init__.py
ADDED
|
File without changes
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/methods/__init__.py
ADDED
|
File without changes
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/methods/test_as_unit.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
|
| 3 |
+
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
| 4 |
+
from pandas.errors import OutOfBoundsTimedelta
|
| 5 |
+
|
| 6 |
+
from pandas import Timedelta
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class TestAsUnit:
|
| 10 |
+
def test_as_unit(self):
|
| 11 |
+
td = Timedelta(days=1)
|
| 12 |
+
|
| 13 |
+
assert td.as_unit("ns") is td
|
| 14 |
+
|
| 15 |
+
res = td.as_unit("us")
|
| 16 |
+
assert res._value == td._value // 1000
|
| 17 |
+
assert res._creso == NpyDatetimeUnit.NPY_FR_us.value
|
| 18 |
+
|
| 19 |
+
rt = res.as_unit("ns")
|
| 20 |
+
assert rt._value == td._value
|
| 21 |
+
assert rt._creso == td._creso
|
| 22 |
+
|
| 23 |
+
res = td.as_unit("ms")
|
| 24 |
+
assert res._value == td._value // 1_000_000
|
| 25 |
+
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
| 26 |
+
|
| 27 |
+
rt = res.as_unit("ns")
|
| 28 |
+
assert rt._value == td._value
|
| 29 |
+
assert rt._creso == td._creso
|
| 30 |
+
|
| 31 |
+
res = td.as_unit("s")
|
| 32 |
+
assert res._value == td._value // 1_000_000_000
|
| 33 |
+
assert res._creso == NpyDatetimeUnit.NPY_FR_s.value
|
| 34 |
+
|
| 35 |
+
rt = res.as_unit("ns")
|
| 36 |
+
assert rt._value == td._value
|
| 37 |
+
assert rt._creso == td._creso
|
| 38 |
+
|
| 39 |
+
def test_as_unit_overflows(self):
|
| 40 |
+
# microsecond that would be just out of bounds for nano
|
| 41 |
+
us = 9223372800000000
|
| 42 |
+
td = Timedelta._from_value_and_reso(us, NpyDatetimeUnit.NPY_FR_us.value)
|
| 43 |
+
|
| 44 |
+
msg = "Cannot cast 106752 days 00:00:00 to unit='ns' without overflow"
|
| 45 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 46 |
+
td.as_unit("ns")
|
| 47 |
+
|
| 48 |
+
res = td.as_unit("ms")
|
| 49 |
+
assert res._value == us // 1000
|
| 50 |
+
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
| 51 |
+
|
| 52 |
+
def test_as_unit_rounding(self):
|
| 53 |
+
td = Timedelta(microseconds=1500)
|
| 54 |
+
res = td.as_unit("ms")
|
| 55 |
+
|
| 56 |
+
expected = Timedelta(milliseconds=1)
|
| 57 |
+
assert res == expected
|
| 58 |
+
|
| 59 |
+
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
| 60 |
+
assert res._value == 1
|
| 61 |
+
|
| 62 |
+
with pytest.raises(ValueError, match="Cannot losslessly convert units"):
|
| 63 |
+
td.as_unit("ms", round_ok=False)
|
| 64 |
+
|
| 65 |
+
def test_as_unit_non_nano(self):
|
| 66 |
+
# case where we are going neither to nor from nano
|
| 67 |
+
td = Timedelta(days=1).as_unit("ms")
|
| 68 |
+
assert td.days == 1
|
| 69 |
+
assert td._value == 86_400_000
|
| 70 |
+
assert td.components.days == 1
|
| 71 |
+
assert td._d == 1
|
| 72 |
+
assert td.total_seconds() == 86400
|
| 73 |
+
|
| 74 |
+
res = td.as_unit("us")
|
| 75 |
+
assert res._value == 86_400_000_000
|
| 76 |
+
assert res.components.days == 1
|
| 77 |
+
assert res.components.hours == 0
|
| 78 |
+
assert res._d == 1
|
| 79 |
+
assert res._h == 0
|
| 80 |
+
assert res.total_seconds() == 86400
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/methods/test_round.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from hypothesis import (
|
| 2 |
+
given,
|
| 3 |
+
strategies as st,
|
| 4 |
+
)
|
| 5 |
+
import numpy as np
|
| 6 |
+
import pytest
|
| 7 |
+
|
| 8 |
+
from pandas._libs import lib
|
| 9 |
+
from pandas._libs.tslibs import iNaT
|
| 10 |
+
from pandas.errors import OutOfBoundsTimedelta
|
| 11 |
+
|
| 12 |
+
from pandas import Timedelta
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class TestTimedeltaRound:
|
| 16 |
+
@pytest.mark.parametrize(
|
| 17 |
+
"freq,s1,s2",
|
| 18 |
+
[
|
| 19 |
+
# This first case has s1, s2 being the same as t1,t2 below
|
| 20 |
+
(
|
| 21 |
+
"ns",
|
| 22 |
+
Timedelta("1 days 02:34:56.789123456"),
|
| 23 |
+
Timedelta("-1 days 02:34:56.789123456"),
|
| 24 |
+
),
|
| 25 |
+
(
|
| 26 |
+
"us",
|
| 27 |
+
Timedelta("1 days 02:34:56.789123000"),
|
| 28 |
+
Timedelta("-1 days 02:34:56.789123000"),
|
| 29 |
+
),
|
| 30 |
+
(
|
| 31 |
+
"ms",
|
| 32 |
+
Timedelta("1 days 02:34:56.789000000"),
|
| 33 |
+
Timedelta("-1 days 02:34:56.789000000"),
|
| 34 |
+
),
|
| 35 |
+
("s", Timedelta("1 days 02:34:57"), Timedelta("-1 days 02:34:57")),
|
| 36 |
+
("2s", Timedelta("1 days 02:34:56"), Timedelta("-1 days 02:34:56")),
|
| 37 |
+
("5s", Timedelta("1 days 02:34:55"), Timedelta("-1 days 02:34:55")),
|
| 38 |
+
("min", Timedelta("1 days 02:35:00"), Timedelta("-1 days 02:35:00")),
|
| 39 |
+
("12min", Timedelta("1 days 02:36:00"), Timedelta("-1 days 02:36:00")),
|
| 40 |
+
("h", Timedelta("1 days 03:00:00"), Timedelta("-1 days 03:00:00")),
|
| 41 |
+
("d", Timedelta("1 days"), Timedelta("-1 days")),
|
| 42 |
+
],
|
| 43 |
+
)
|
| 44 |
+
def test_round(self, freq, s1, s2):
|
| 45 |
+
t1 = Timedelta("1 days 02:34:56.789123456")
|
| 46 |
+
t2 = Timedelta("-1 days 02:34:56.789123456")
|
| 47 |
+
|
| 48 |
+
r1 = t1.round(freq)
|
| 49 |
+
assert r1 == s1
|
| 50 |
+
r2 = t2.round(freq)
|
| 51 |
+
assert r2 == s2
|
| 52 |
+
|
| 53 |
+
def test_round_invalid(self):
|
| 54 |
+
t1 = Timedelta("1 days 02:34:56.789123456")
|
| 55 |
+
|
| 56 |
+
for freq, msg in [
|
| 57 |
+
("YE", "<YearEnd: month=12> is a non-fixed frequency"),
|
| 58 |
+
("ME", "<MonthEnd> is a non-fixed frequency"),
|
| 59 |
+
("foobar", "Invalid frequency: foobar"),
|
| 60 |
+
]:
|
| 61 |
+
with pytest.raises(ValueError, match=msg):
|
| 62 |
+
t1.round(freq)
|
| 63 |
+
|
| 64 |
+
@pytest.mark.skip_ubsan
|
| 65 |
+
def test_round_implementation_bounds(self):
|
| 66 |
+
# See also: analogous test for Timestamp
|
| 67 |
+
# GH#38964
|
| 68 |
+
result = Timedelta.min.ceil("s")
|
| 69 |
+
expected = Timedelta.min + Timedelta(seconds=1) - Timedelta(145224193)
|
| 70 |
+
assert result == expected
|
| 71 |
+
|
| 72 |
+
result = Timedelta.max.floor("s")
|
| 73 |
+
expected = Timedelta.max - Timedelta(854775807)
|
| 74 |
+
assert result == expected
|
| 75 |
+
|
| 76 |
+
msg = (
|
| 77 |
+
r"Cannot round -106752 days \+00:12:43.145224193 to freq=s without overflow"
|
| 78 |
+
)
|
| 79 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 80 |
+
Timedelta.min.floor("s")
|
| 81 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 82 |
+
Timedelta.min.round("s")
|
| 83 |
+
|
| 84 |
+
msg = "Cannot round 106751 days 23:47:16.854775807 to freq=s without overflow"
|
| 85 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 86 |
+
Timedelta.max.ceil("s")
|
| 87 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 88 |
+
Timedelta.max.round("s")
|
| 89 |
+
|
| 90 |
+
@pytest.mark.skip_ubsan
|
| 91 |
+
@given(val=st.integers(min_value=iNaT + 1, max_value=lib.i8max))
|
| 92 |
+
@pytest.mark.parametrize(
|
| 93 |
+
"method", [Timedelta.round, Timedelta.floor, Timedelta.ceil]
|
| 94 |
+
)
|
| 95 |
+
def test_round_sanity(self, val, method):
|
| 96 |
+
cls = Timedelta
|
| 97 |
+
err_cls = OutOfBoundsTimedelta
|
| 98 |
+
|
| 99 |
+
val = np.int64(val)
|
| 100 |
+
td = cls(val)
|
| 101 |
+
|
| 102 |
+
def checker(ts, nanos, unit):
|
| 103 |
+
# First check that we do raise in cases where we should
|
| 104 |
+
if nanos == 1:
|
| 105 |
+
pass
|
| 106 |
+
else:
|
| 107 |
+
div, mod = divmod(ts._value, nanos)
|
| 108 |
+
diff = int(nanos - mod)
|
| 109 |
+
lb = ts._value - mod
|
| 110 |
+
assert lb <= ts._value # i.e. no overflows with python ints
|
| 111 |
+
ub = ts._value + diff
|
| 112 |
+
assert ub > ts._value # i.e. no overflows with python ints
|
| 113 |
+
|
| 114 |
+
msg = "without overflow"
|
| 115 |
+
if mod == 0:
|
| 116 |
+
# We should never be raising in this
|
| 117 |
+
pass
|
| 118 |
+
elif method is cls.ceil:
|
| 119 |
+
if ub > cls.max._value:
|
| 120 |
+
with pytest.raises(err_cls, match=msg):
|
| 121 |
+
method(ts, unit)
|
| 122 |
+
return
|
| 123 |
+
elif method is cls.floor:
|
| 124 |
+
if lb < cls.min._value:
|
| 125 |
+
with pytest.raises(err_cls, match=msg):
|
| 126 |
+
method(ts, unit)
|
| 127 |
+
return
|
| 128 |
+
elif mod >= diff:
|
| 129 |
+
if ub > cls.max._value:
|
| 130 |
+
with pytest.raises(err_cls, match=msg):
|
| 131 |
+
method(ts, unit)
|
| 132 |
+
return
|
| 133 |
+
elif lb < cls.min._value:
|
| 134 |
+
with pytest.raises(err_cls, match=msg):
|
| 135 |
+
method(ts, unit)
|
| 136 |
+
return
|
| 137 |
+
|
| 138 |
+
res = method(ts, unit)
|
| 139 |
+
|
| 140 |
+
td = res - ts
|
| 141 |
+
diff = abs(td._value)
|
| 142 |
+
assert diff < nanos
|
| 143 |
+
assert res._value % nanos == 0
|
| 144 |
+
|
| 145 |
+
if method is cls.round:
|
| 146 |
+
assert diff <= nanos / 2
|
| 147 |
+
elif method is cls.floor:
|
| 148 |
+
assert res <= ts
|
| 149 |
+
elif method is cls.ceil:
|
| 150 |
+
assert res >= ts
|
| 151 |
+
|
| 152 |
+
nanos = 1
|
| 153 |
+
checker(td, nanos, "ns")
|
| 154 |
+
|
| 155 |
+
nanos = 1000
|
| 156 |
+
checker(td, nanos, "us")
|
| 157 |
+
|
| 158 |
+
nanos = 1_000_000
|
| 159 |
+
checker(td, nanos, "ms")
|
| 160 |
+
|
| 161 |
+
nanos = 1_000_000_000
|
| 162 |
+
checker(td, nanos, "s")
|
| 163 |
+
|
| 164 |
+
nanos = 60 * 1_000_000_000
|
| 165 |
+
checker(td, nanos, "min")
|
| 166 |
+
|
| 167 |
+
nanos = 60 * 60 * 1_000_000_000
|
| 168 |
+
checker(td, nanos, "h")
|
| 169 |
+
|
| 170 |
+
nanos = 24 * 60 * 60 * 1_000_000_000
|
| 171 |
+
checker(td, nanos, "D")
|
| 172 |
+
|
| 173 |
+
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
| 174 |
+
def test_round_non_nano(self, unit):
|
| 175 |
+
td = Timedelta("1 days 02:34:57").as_unit(unit)
|
| 176 |
+
|
| 177 |
+
res = td.round("min")
|
| 178 |
+
assert res == Timedelta("1 days 02:35:00")
|
| 179 |
+
assert res._creso == td._creso
|
| 180 |
+
|
| 181 |
+
res = td.floor("min")
|
| 182 |
+
assert res == Timedelta("1 days 02:34:00")
|
| 183 |
+
assert res._creso == td._creso
|
| 184 |
+
|
| 185 |
+
res = td.ceil("min")
|
| 186 |
+
assert res == Timedelta("1 days 02:35:00")
|
| 187 |
+
assert res._creso == td._creso
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/test_arithmetic.py
ADDED
|
@@ -0,0 +1,1183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Tests for scalar Timedelta arithmetic ops
|
| 3 |
+
"""
|
| 4 |
+
from datetime import (
|
| 5 |
+
datetime,
|
| 6 |
+
timedelta,
|
| 7 |
+
)
|
| 8 |
+
import operator
|
| 9 |
+
|
| 10 |
+
import numpy as np
|
| 11 |
+
import pytest
|
| 12 |
+
|
| 13 |
+
from pandas.errors import OutOfBoundsTimedelta
|
| 14 |
+
|
| 15 |
+
import pandas as pd
|
| 16 |
+
from pandas import (
|
| 17 |
+
NaT,
|
| 18 |
+
Timedelta,
|
| 19 |
+
Timestamp,
|
| 20 |
+
offsets,
|
| 21 |
+
)
|
| 22 |
+
import pandas._testing as tm
|
| 23 |
+
from pandas.core import ops
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class TestTimedeltaAdditionSubtraction:
|
| 27 |
+
"""
|
| 28 |
+
Tests for Timedelta methods:
|
| 29 |
+
|
| 30 |
+
__add__, __radd__,
|
| 31 |
+
__sub__, __rsub__
|
| 32 |
+
"""
|
| 33 |
+
|
| 34 |
+
@pytest.mark.parametrize(
|
| 35 |
+
"ten_seconds",
|
| 36 |
+
[
|
| 37 |
+
Timedelta(10, unit="s"),
|
| 38 |
+
timedelta(seconds=10),
|
| 39 |
+
np.timedelta64(10, "s"),
|
| 40 |
+
np.timedelta64(10000000000, "ns"),
|
| 41 |
+
offsets.Second(10),
|
| 42 |
+
],
|
| 43 |
+
)
|
| 44 |
+
def test_td_add_sub_ten_seconds(self, ten_seconds):
|
| 45 |
+
# GH#6808
|
| 46 |
+
base = Timestamp("20130101 09:01:12.123456")
|
| 47 |
+
expected_add = Timestamp("20130101 09:01:22.123456")
|
| 48 |
+
expected_sub = Timestamp("20130101 09:01:02.123456")
|
| 49 |
+
|
| 50 |
+
result = base + ten_seconds
|
| 51 |
+
assert result == expected_add
|
| 52 |
+
|
| 53 |
+
result = base - ten_seconds
|
| 54 |
+
assert result == expected_sub
|
| 55 |
+
|
| 56 |
+
@pytest.mark.parametrize(
|
| 57 |
+
"one_day_ten_secs",
|
| 58 |
+
[
|
| 59 |
+
Timedelta("1 day, 00:00:10"),
|
| 60 |
+
Timedelta("1 days, 00:00:10"),
|
| 61 |
+
timedelta(days=1, seconds=10),
|
| 62 |
+
np.timedelta64(1, "D") + np.timedelta64(10, "s"),
|
| 63 |
+
offsets.Day() + offsets.Second(10),
|
| 64 |
+
],
|
| 65 |
+
)
|
| 66 |
+
def test_td_add_sub_one_day_ten_seconds(self, one_day_ten_secs):
|
| 67 |
+
# GH#6808
|
| 68 |
+
base = Timestamp("20130102 09:01:12.123456")
|
| 69 |
+
expected_add = Timestamp("20130103 09:01:22.123456")
|
| 70 |
+
expected_sub = Timestamp("20130101 09:01:02.123456")
|
| 71 |
+
|
| 72 |
+
result = base + one_day_ten_secs
|
| 73 |
+
assert result == expected_add
|
| 74 |
+
|
| 75 |
+
result = base - one_day_ten_secs
|
| 76 |
+
assert result == expected_sub
|
| 77 |
+
|
| 78 |
+
@pytest.mark.parametrize("op", [operator.add, ops.radd])
|
| 79 |
+
def test_td_add_datetimelike_scalar(self, op):
|
| 80 |
+
# GH#19738
|
| 81 |
+
td = Timedelta(10, unit="d")
|
| 82 |
+
|
| 83 |
+
result = op(td, datetime(2016, 1, 1))
|
| 84 |
+
if op is operator.add:
|
| 85 |
+
# datetime + Timedelta does _not_ call Timedelta.__radd__,
|
| 86 |
+
# so we get a datetime back instead of a Timestamp
|
| 87 |
+
assert isinstance(result, Timestamp)
|
| 88 |
+
assert result == Timestamp(2016, 1, 11)
|
| 89 |
+
|
| 90 |
+
result = op(td, Timestamp("2018-01-12 18:09"))
|
| 91 |
+
assert isinstance(result, Timestamp)
|
| 92 |
+
assert result == Timestamp("2018-01-22 18:09")
|
| 93 |
+
|
| 94 |
+
result = op(td, np.datetime64("2018-01-12"))
|
| 95 |
+
assert isinstance(result, Timestamp)
|
| 96 |
+
assert result == Timestamp("2018-01-22")
|
| 97 |
+
|
| 98 |
+
result = op(td, NaT)
|
| 99 |
+
assert result is NaT
|
| 100 |
+
|
| 101 |
+
def test_td_add_timestamp_overflow(self):
|
| 102 |
+
ts = Timestamp("1700-01-01").as_unit("ns")
|
| 103 |
+
msg = "Cannot cast 259987 from D to 'ns' without overflow."
|
| 104 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 105 |
+
ts + Timedelta(13 * 19999, unit="D")
|
| 106 |
+
|
| 107 |
+
msg = "Cannot cast 259987 days 00:00:00 to unit='ns' without overflow"
|
| 108 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 109 |
+
ts + timedelta(days=13 * 19999)
|
| 110 |
+
|
| 111 |
+
@pytest.mark.parametrize("op", [operator.add, ops.radd])
|
| 112 |
+
def test_td_add_td(self, op):
|
| 113 |
+
td = Timedelta(10, unit="d")
|
| 114 |
+
|
| 115 |
+
result = op(td, Timedelta(days=10))
|
| 116 |
+
assert isinstance(result, Timedelta)
|
| 117 |
+
assert result == Timedelta(days=20)
|
| 118 |
+
|
| 119 |
+
@pytest.mark.parametrize("op", [operator.add, ops.radd])
|
| 120 |
+
def test_td_add_pytimedelta(self, op):
|
| 121 |
+
td = Timedelta(10, unit="d")
|
| 122 |
+
result = op(td, timedelta(days=9))
|
| 123 |
+
assert isinstance(result, Timedelta)
|
| 124 |
+
assert result == Timedelta(days=19)
|
| 125 |
+
|
| 126 |
+
@pytest.mark.parametrize("op", [operator.add, ops.radd])
|
| 127 |
+
def test_td_add_timedelta64(self, op):
|
| 128 |
+
td = Timedelta(10, unit="d")
|
| 129 |
+
result = op(td, np.timedelta64(-4, "D"))
|
| 130 |
+
assert isinstance(result, Timedelta)
|
| 131 |
+
assert result == Timedelta(days=6)
|
| 132 |
+
|
| 133 |
+
@pytest.mark.parametrize("op", [operator.add, ops.radd])
|
| 134 |
+
def test_td_add_offset(self, op):
|
| 135 |
+
td = Timedelta(10, unit="d")
|
| 136 |
+
|
| 137 |
+
result = op(td, offsets.Hour(6))
|
| 138 |
+
assert isinstance(result, Timedelta)
|
| 139 |
+
assert result == Timedelta(days=10, hours=6)
|
| 140 |
+
|
| 141 |
+
def test_td_sub_td(self):
|
| 142 |
+
td = Timedelta(10, unit="d")
|
| 143 |
+
expected = Timedelta(0, unit="ns")
|
| 144 |
+
result = td - td
|
| 145 |
+
assert isinstance(result, Timedelta)
|
| 146 |
+
assert result == expected
|
| 147 |
+
|
| 148 |
+
def test_td_sub_pytimedelta(self):
|
| 149 |
+
td = Timedelta(10, unit="d")
|
| 150 |
+
expected = Timedelta(0, unit="ns")
|
| 151 |
+
|
| 152 |
+
result = td - td.to_pytimedelta()
|
| 153 |
+
assert isinstance(result, Timedelta)
|
| 154 |
+
assert result == expected
|
| 155 |
+
|
| 156 |
+
result = td.to_pytimedelta() - td
|
| 157 |
+
assert isinstance(result, Timedelta)
|
| 158 |
+
assert result == expected
|
| 159 |
+
|
| 160 |
+
def test_td_sub_timedelta64(self):
|
| 161 |
+
td = Timedelta(10, unit="d")
|
| 162 |
+
expected = Timedelta(0, unit="ns")
|
| 163 |
+
|
| 164 |
+
result = td - td.to_timedelta64()
|
| 165 |
+
assert isinstance(result, Timedelta)
|
| 166 |
+
assert result == expected
|
| 167 |
+
|
| 168 |
+
result = td.to_timedelta64() - td
|
| 169 |
+
assert isinstance(result, Timedelta)
|
| 170 |
+
assert result == expected
|
| 171 |
+
|
| 172 |
+
def test_td_sub_nat(self):
|
| 173 |
+
# In this context pd.NaT is treated as timedelta-like
|
| 174 |
+
td = Timedelta(10, unit="d")
|
| 175 |
+
result = td - NaT
|
| 176 |
+
assert result is NaT
|
| 177 |
+
|
| 178 |
+
def test_td_sub_td64_nat(self):
|
| 179 |
+
td = Timedelta(10, unit="d")
|
| 180 |
+
td_nat = np.timedelta64("NaT")
|
| 181 |
+
|
| 182 |
+
result = td - td_nat
|
| 183 |
+
assert result is NaT
|
| 184 |
+
|
| 185 |
+
result = td_nat - td
|
| 186 |
+
assert result is NaT
|
| 187 |
+
|
| 188 |
+
def test_td_sub_offset(self):
|
| 189 |
+
td = Timedelta(10, unit="d")
|
| 190 |
+
result = td - offsets.Hour(1)
|
| 191 |
+
assert isinstance(result, Timedelta)
|
| 192 |
+
assert result == Timedelta(239, unit="h")
|
| 193 |
+
|
| 194 |
+
def test_td_add_sub_numeric_raises(self):
|
| 195 |
+
td = Timedelta(10, unit="d")
|
| 196 |
+
msg = "unsupported operand type"
|
| 197 |
+
for other in [2, 2.0, np.int64(2), np.float64(2)]:
|
| 198 |
+
with pytest.raises(TypeError, match=msg):
|
| 199 |
+
td + other
|
| 200 |
+
with pytest.raises(TypeError, match=msg):
|
| 201 |
+
other + td
|
| 202 |
+
with pytest.raises(TypeError, match=msg):
|
| 203 |
+
td - other
|
| 204 |
+
with pytest.raises(TypeError, match=msg):
|
| 205 |
+
other - td
|
| 206 |
+
|
| 207 |
+
def test_td_add_sub_int_ndarray(self):
|
| 208 |
+
td = Timedelta("1 day")
|
| 209 |
+
other = np.array([1])
|
| 210 |
+
|
| 211 |
+
msg = r"unsupported operand type\(s\) for \+: 'Timedelta' and 'int'"
|
| 212 |
+
with pytest.raises(TypeError, match=msg):
|
| 213 |
+
td + np.array([1])
|
| 214 |
+
|
| 215 |
+
msg = "|".join(
|
| 216 |
+
[
|
| 217 |
+
(
|
| 218 |
+
r"unsupported operand type\(s\) for \+: 'numpy.ndarray' "
|
| 219 |
+
"and 'Timedelta'"
|
| 220 |
+
),
|
| 221 |
+
# This message goes on to say "Please do not rely on this error;
|
| 222 |
+
# it may not be given on all Python implementations"
|
| 223 |
+
"Concatenation operation is not implemented for NumPy arrays",
|
| 224 |
+
]
|
| 225 |
+
)
|
| 226 |
+
with pytest.raises(TypeError, match=msg):
|
| 227 |
+
other + td
|
| 228 |
+
msg = r"unsupported operand type\(s\) for -: 'Timedelta' and 'int'"
|
| 229 |
+
with pytest.raises(TypeError, match=msg):
|
| 230 |
+
td - other
|
| 231 |
+
msg = r"unsupported operand type\(s\) for -: 'numpy.ndarray' and 'Timedelta'"
|
| 232 |
+
with pytest.raises(TypeError, match=msg):
|
| 233 |
+
other - td
|
| 234 |
+
|
| 235 |
+
def test_td_rsub_nat(self):
|
| 236 |
+
td = Timedelta(10, unit="d")
|
| 237 |
+
result = NaT - td
|
| 238 |
+
assert result is NaT
|
| 239 |
+
|
| 240 |
+
result = np.datetime64("NaT") - td
|
| 241 |
+
assert result is NaT
|
| 242 |
+
|
| 243 |
+
def test_td_rsub_offset(self):
|
| 244 |
+
result = offsets.Hour(1) - Timedelta(10, unit="d")
|
| 245 |
+
assert isinstance(result, Timedelta)
|
| 246 |
+
assert result == Timedelta(-239, unit="h")
|
| 247 |
+
|
| 248 |
+
def test_td_sub_timedeltalike_object_dtype_array(self):
|
| 249 |
+
# GH#21980
|
| 250 |
+
arr = np.array([Timestamp("20130101 9:01"), Timestamp("20121230 9:02")])
|
| 251 |
+
exp = np.array([Timestamp("20121231 9:01"), Timestamp("20121229 9:02")])
|
| 252 |
+
res = arr - Timedelta("1D")
|
| 253 |
+
tm.assert_numpy_array_equal(res, exp)
|
| 254 |
+
|
| 255 |
+
def test_td_sub_mixed_most_timedeltalike_object_dtype_array(self):
|
| 256 |
+
# GH#21980
|
| 257 |
+
now = Timestamp("2021-11-09 09:54:00")
|
| 258 |
+
arr = np.array([now, Timedelta("1D"), np.timedelta64(2, "h")])
|
| 259 |
+
exp = np.array(
|
| 260 |
+
[
|
| 261 |
+
now - Timedelta("1D"),
|
| 262 |
+
Timedelta("0D"),
|
| 263 |
+
np.timedelta64(2, "h") - Timedelta("1D"),
|
| 264 |
+
]
|
| 265 |
+
)
|
| 266 |
+
res = arr - Timedelta("1D")
|
| 267 |
+
tm.assert_numpy_array_equal(res, exp)
|
| 268 |
+
|
| 269 |
+
def test_td_rsub_mixed_most_timedeltalike_object_dtype_array(self):
|
| 270 |
+
# GH#21980
|
| 271 |
+
now = Timestamp("2021-11-09 09:54:00")
|
| 272 |
+
arr = np.array([now, Timedelta("1D"), np.timedelta64(2, "h")])
|
| 273 |
+
msg = r"unsupported operand type\(s\) for \-: 'Timedelta' and 'Timestamp'"
|
| 274 |
+
with pytest.raises(TypeError, match=msg):
|
| 275 |
+
Timedelta("1D") - arr
|
| 276 |
+
|
| 277 |
+
@pytest.mark.parametrize("op", [operator.add, ops.radd])
|
| 278 |
+
def test_td_add_timedeltalike_object_dtype_array(self, op):
|
| 279 |
+
# GH#21980
|
| 280 |
+
arr = np.array([Timestamp("20130101 9:01"), Timestamp("20121230 9:02")])
|
| 281 |
+
exp = np.array([Timestamp("20130102 9:01"), Timestamp("20121231 9:02")])
|
| 282 |
+
res = op(arr, Timedelta("1D"))
|
| 283 |
+
tm.assert_numpy_array_equal(res, exp)
|
| 284 |
+
|
| 285 |
+
@pytest.mark.parametrize("op", [operator.add, ops.radd])
|
| 286 |
+
def test_td_add_mixed_timedeltalike_object_dtype_array(self, op):
|
| 287 |
+
# GH#21980
|
| 288 |
+
now = Timestamp("2021-11-09 09:54:00")
|
| 289 |
+
arr = np.array([now, Timedelta("1D")])
|
| 290 |
+
exp = np.array([now + Timedelta("1D"), Timedelta("2D")])
|
| 291 |
+
res = op(arr, Timedelta("1D"))
|
| 292 |
+
tm.assert_numpy_array_equal(res, exp)
|
| 293 |
+
|
| 294 |
+
def test_td_add_sub_td64_ndarray(self):
|
| 295 |
+
td = Timedelta("1 day")
|
| 296 |
+
|
| 297 |
+
other = np.array([td.to_timedelta64()])
|
| 298 |
+
expected = np.array([Timedelta("2 Days").to_timedelta64()])
|
| 299 |
+
|
| 300 |
+
result = td + other
|
| 301 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 302 |
+
result = other + td
|
| 303 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 304 |
+
|
| 305 |
+
result = td - other
|
| 306 |
+
tm.assert_numpy_array_equal(result, expected * 0)
|
| 307 |
+
result = other - td
|
| 308 |
+
tm.assert_numpy_array_equal(result, expected * 0)
|
| 309 |
+
|
| 310 |
+
def test_td_add_sub_dt64_ndarray(self):
|
| 311 |
+
td = Timedelta("1 day")
|
| 312 |
+
other = np.array(["2000-01-01"], dtype="M8[ns]")
|
| 313 |
+
|
| 314 |
+
expected = np.array(["2000-01-02"], dtype="M8[ns]")
|
| 315 |
+
tm.assert_numpy_array_equal(td + other, expected)
|
| 316 |
+
tm.assert_numpy_array_equal(other + td, expected)
|
| 317 |
+
|
| 318 |
+
expected = np.array(["1999-12-31"], dtype="M8[ns]")
|
| 319 |
+
tm.assert_numpy_array_equal(-td + other, expected)
|
| 320 |
+
tm.assert_numpy_array_equal(other - td, expected)
|
| 321 |
+
|
| 322 |
+
def test_td_add_sub_ndarray_0d(self):
|
| 323 |
+
td = Timedelta("1 day")
|
| 324 |
+
other = np.array(td.asm8)
|
| 325 |
+
|
| 326 |
+
result = td + other
|
| 327 |
+
assert isinstance(result, Timedelta)
|
| 328 |
+
assert result == 2 * td
|
| 329 |
+
|
| 330 |
+
result = other + td
|
| 331 |
+
assert isinstance(result, Timedelta)
|
| 332 |
+
assert result == 2 * td
|
| 333 |
+
|
| 334 |
+
result = other - td
|
| 335 |
+
assert isinstance(result, Timedelta)
|
| 336 |
+
assert result == 0 * td
|
| 337 |
+
|
| 338 |
+
result = td - other
|
| 339 |
+
assert isinstance(result, Timedelta)
|
| 340 |
+
assert result == 0 * td
|
| 341 |
+
|
| 342 |
+
|
| 343 |
+
class TestTimedeltaMultiplicationDivision:
|
| 344 |
+
"""
|
| 345 |
+
Tests for Timedelta methods:
|
| 346 |
+
|
| 347 |
+
__mul__, __rmul__,
|
| 348 |
+
__div__, __rdiv__,
|
| 349 |
+
__truediv__, __rtruediv__,
|
| 350 |
+
__floordiv__, __rfloordiv__,
|
| 351 |
+
__mod__, __rmod__,
|
| 352 |
+
__divmod__, __rdivmod__
|
| 353 |
+
"""
|
| 354 |
+
|
| 355 |
+
# ---------------------------------------------------------------
|
| 356 |
+
# Timedelta.__mul__, __rmul__
|
| 357 |
+
|
| 358 |
+
@pytest.mark.parametrize(
|
| 359 |
+
"td_nat", [NaT, np.timedelta64("NaT", "ns"), np.timedelta64("NaT")]
|
| 360 |
+
)
|
| 361 |
+
@pytest.mark.parametrize("op", [operator.mul, ops.rmul])
|
| 362 |
+
def test_td_mul_nat(self, op, td_nat):
|
| 363 |
+
# GH#19819
|
| 364 |
+
td = Timedelta(10, unit="d")
|
| 365 |
+
typs = "|".join(["numpy.timedelta64", "NaTType", "Timedelta"])
|
| 366 |
+
msg = "|".join(
|
| 367 |
+
[
|
| 368 |
+
rf"unsupported operand type\(s\) for \*: '{typs}' and '{typs}'",
|
| 369 |
+
r"ufunc '?multiply'? cannot use operands with types",
|
| 370 |
+
]
|
| 371 |
+
)
|
| 372 |
+
with pytest.raises(TypeError, match=msg):
|
| 373 |
+
op(td, td_nat)
|
| 374 |
+
|
| 375 |
+
@pytest.mark.parametrize("nan", [np.nan, np.float64("NaN"), float("nan")])
|
| 376 |
+
@pytest.mark.parametrize("op", [operator.mul, ops.rmul])
|
| 377 |
+
def test_td_mul_nan(self, op, nan):
|
| 378 |
+
# np.float64('NaN') has a 'dtype' attr, avoid treating as array
|
| 379 |
+
td = Timedelta(10, unit="d")
|
| 380 |
+
result = op(td, nan)
|
| 381 |
+
assert result is NaT
|
| 382 |
+
|
| 383 |
+
@pytest.mark.parametrize("op", [operator.mul, ops.rmul])
|
| 384 |
+
def test_td_mul_scalar(self, op):
|
| 385 |
+
# GH#19738
|
| 386 |
+
td = Timedelta(minutes=3)
|
| 387 |
+
|
| 388 |
+
result = op(td, 2)
|
| 389 |
+
assert result == Timedelta(minutes=6)
|
| 390 |
+
|
| 391 |
+
result = op(td, 1.5)
|
| 392 |
+
assert result == Timedelta(minutes=4, seconds=30)
|
| 393 |
+
|
| 394 |
+
assert op(td, np.nan) is NaT
|
| 395 |
+
|
| 396 |
+
assert op(-1, td)._value == -1 * td._value
|
| 397 |
+
assert op(-1.0, td)._value == -1.0 * td._value
|
| 398 |
+
|
| 399 |
+
msg = "unsupported operand type"
|
| 400 |
+
with pytest.raises(TypeError, match=msg):
|
| 401 |
+
# timedelta * datetime is gibberish
|
| 402 |
+
op(td, Timestamp(2016, 1, 2))
|
| 403 |
+
|
| 404 |
+
with pytest.raises(TypeError, match=msg):
|
| 405 |
+
# invalid multiply with another timedelta
|
| 406 |
+
op(td, td)
|
| 407 |
+
|
| 408 |
+
def test_td_mul_numeric_ndarray(self):
|
| 409 |
+
td = Timedelta("1 day")
|
| 410 |
+
other = np.array([2])
|
| 411 |
+
expected = np.array([Timedelta("2 Days").to_timedelta64()])
|
| 412 |
+
|
| 413 |
+
result = td * other
|
| 414 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 415 |
+
|
| 416 |
+
result = other * td
|
| 417 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 418 |
+
|
| 419 |
+
def test_td_mul_numeric_ndarray_0d(self):
|
| 420 |
+
td = Timedelta("1 day")
|
| 421 |
+
other = np.array(2, dtype=np.int64)
|
| 422 |
+
assert other.ndim == 0
|
| 423 |
+
expected = Timedelta("2 days")
|
| 424 |
+
|
| 425 |
+
res = td * other
|
| 426 |
+
assert type(res) is Timedelta
|
| 427 |
+
assert res == expected
|
| 428 |
+
|
| 429 |
+
res = other * td
|
| 430 |
+
assert type(res) is Timedelta
|
| 431 |
+
assert res == expected
|
| 432 |
+
|
| 433 |
+
def test_td_mul_td64_ndarray_invalid(self):
|
| 434 |
+
td = Timedelta("1 day")
|
| 435 |
+
other = np.array([Timedelta("2 Days").to_timedelta64()])
|
| 436 |
+
|
| 437 |
+
msg = (
|
| 438 |
+
"ufunc '?multiply'? cannot use operands with types "
|
| 439 |
+
rf"dtype\('{tm.ENDIAN}m8\[ns\]'\) and dtype\('{tm.ENDIAN}m8\[ns\]'\)"
|
| 440 |
+
)
|
| 441 |
+
with pytest.raises(TypeError, match=msg):
|
| 442 |
+
td * other
|
| 443 |
+
with pytest.raises(TypeError, match=msg):
|
| 444 |
+
other * td
|
| 445 |
+
|
| 446 |
+
# ---------------------------------------------------------------
|
| 447 |
+
# Timedelta.__div__, __truediv__
|
| 448 |
+
|
| 449 |
+
def test_td_div_timedeltalike_scalar(self):
|
| 450 |
+
# GH#19738
|
| 451 |
+
td = Timedelta(10, unit="d")
|
| 452 |
+
|
| 453 |
+
result = td / offsets.Hour(1)
|
| 454 |
+
assert result == 240
|
| 455 |
+
|
| 456 |
+
assert td / td == 1
|
| 457 |
+
assert td / np.timedelta64(60, "h") == 4
|
| 458 |
+
|
| 459 |
+
assert np.isnan(td / NaT)
|
| 460 |
+
|
| 461 |
+
def test_td_div_td64_non_nano(self):
|
| 462 |
+
# truediv
|
| 463 |
+
td = Timedelta("1 days 2 hours 3 ns")
|
| 464 |
+
result = td / np.timedelta64(1, "D")
|
| 465 |
+
assert result == td._value / (86400 * 10**9)
|
| 466 |
+
result = td / np.timedelta64(1, "s")
|
| 467 |
+
assert result == td._value / 10**9
|
| 468 |
+
result = td / np.timedelta64(1, "ns")
|
| 469 |
+
assert result == td._value
|
| 470 |
+
|
| 471 |
+
# floordiv
|
| 472 |
+
td = Timedelta("1 days 2 hours 3 ns")
|
| 473 |
+
result = td // np.timedelta64(1, "D")
|
| 474 |
+
assert result == 1
|
| 475 |
+
result = td // np.timedelta64(1, "s")
|
| 476 |
+
assert result == 93600
|
| 477 |
+
result = td // np.timedelta64(1, "ns")
|
| 478 |
+
assert result == td._value
|
| 479 |
+
|
| 480 |
+
def test_td_div_numeric_scalar(self):
|
| 481 |
+
# GH#19738
|
| 482 |
+
td = Timedelta(10, unit="d")
|
| 483 |
+
|
| 484 |
+
result = td / 2
|
| 485 |
+
assert isinstance(result, Timedelta)
|
| 486 |
+
assert result == Timedelta(days=5)
|
| 487 |
+
|
| 488 |
+
result = td / 5
|
| 489 |
+
assert isinstance(result, Timedelta)
|
| 490 |
+
assert result == Timedelta(days=2)
|
| 491 |
+
|
| 492 |
+
@pytest.mark.parametrize(
|
| 493 |
+
"nan",
|
| 494 |
+
[
|
| 495 |
+
np.nan,
|
| 496 |
+
np.float64("NaN"),
|
| 497 |
+
float("nan"),
|
| 498 |
+
],
|
| 499 |
+
)
|
| 500 |
+
def test_td_div_nan(self, nan):
|
| 501 |
+
# np.float64('NaN') has a 'dtype' attr, avoid treating as array
|
| 502 |
+
td = Timedelta(10, unit="d")
|
| 503 |
+
result = td / nan
|
| 504 |
+
assert result is NaT
|
| 505 |
+
|
| 506 |
+
result = td // nan
|
| 507 |
+
assert result is NaT
|
| 508 |
+
|
| 509 |
+
def test_td_div_td64_ndarray(self):
|
| 510 |
+
td = Timedelta("1 day")
|
| 511 |
+
|
| 512 |
+
other = np.array([Timedelta("2 Days").to_timedelta64()])
|
| 513 |
+
expected = np.array([0.5])
|
| 514 |
+
|
| 515 |
+
result = td / other
|
| 516 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 517 |
+
|
| 518 |
+
result = other / td
|
| 519 |
+
tm.assert_numpy_array_equal(result, expected * 4)
|
| 520 |
+
|
| 521 |
+
def test_td_div_ndarray_0d(self):
|
| 522 |
+
td = Timedelta("1 day")
|
| 523 |
+
|
| 524 |
+
other = np.array(1)
|
| 525 |
+
res = td / other
|
| 526 |
+
assert isinstance(res, Timedelta)
|
| 527 |
+
assert res == td
|
| 528 |
+
|
| 529 |
+
# ---------------------------------------------------------------
|
| 530 |
+
# Timedelta.__rdiv__
|
| 531 |
+
|
| 532 |
+
def test_td_rdiv_timedeltalike_scalar(self):
|
| 533 |
+
# GH#19738
|
| 534 |
+
td = Timedelta(10, unit="d")
|
| 535 |
+
result = offsets.Hour(1) / td
|
| 536 |
+
assert result == 1 / 240.0
|
| 537 |
+
|
| 538 |
+
assert np.timedelta64(60, "h") / td == 0.25
|
| 539 |
+
|
| 540 |
+
def test_td_rdiv_na_scalar(self):
|
| 541 |
+
# GH#31869 None gets cast to NaT
|
| 542 |
+
td = Timedelta(10, unit="d")
|
| 543 |
+
|
| 544 |
+
result = NaT / td
|
| 545 |
+
assert np.isnan(result)
|
| 546 |
+
|
| 547 |
+
result = None / td
|
| 548 |
+
assert np.isnan(result)
|
| 549 |
+
|
| 550 |
+
result = np.timedelta64("NaT") / td
|
| 551 |
+
assert np.isnan(result)
|
| 552 |
+
|
| 553 |
+
msg = r"unsupported operand type\(s\) for /: 'numpy.datetime64' and 'Timedelta'"
|
| 554 |
+
with pytest.raises(TypeError, match=msg):
|
| 555 |
+
np.datetime64("NaT") / td
|
| 556 |
+
|
| 557 |
+
msg = r"unsupported operand type\(s\) for /: 'float' and 'Timedelta'"
|
| 558 |
+
with pytest.raises(TypeError, match=msg):
|
| 559 |
+
np.nan / td
|
| 560 |
+
|
| 561 |
+
def test_td_rdiv_ndarray(self):
|
| 562 |
+
td = Timedelta(10, unit="d")
|
| 563 |
+
|
| 564 |
+
arr = np.array([td], dtype=object)
|
| 565 |
+
result = arr / td
|
| 566 |
+
expected = np.array([1], dtype=np.float64)
|
| 567 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 568 |
+
|
| 569 |
+
arr = np.array([None])
|
| 570 |
+
result = arr / td
|
| 571 |
+
expected = np.array([np.nan])
|
| 572 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 573 |
+
|
| 574 |
+
arr = np.array([np.nan], dtype=object)
|
| 575 |
+
msg = r"unsupported operand type\(s\) for /: 'float' and 'Timedelta'"
|
| 576 |
+
with pytest.raises(TypeError, match=msg):
|
| 577 |
+
arr / td
|
| 578 |
+
|
| 579 |
+
arr = np.array([np.nan], dtype=np.float64)
|
| 580 |
+
msg = "cannot use operands with types dtype"
|
| 581 |
+
with pytest.raises(TypeError, match=msg):
|
| 582 |
+
arr / td
|
| 583 |
+
|
| 584 |
+
def test_td_rdiv_ndarray_0d(self):
|
| 585 |
+
td = Timedelta(10, unit="d")
|
| 586 |
+
|
| 587 |
+
arr = np.array(td.asm8)
|
| 588 |
+
|
| 589 |
+
assert arr / td == 1
|
| 590 |
+
|
| 591 |
+
# ---------------------------------------------------------------
|
| 592 |
+
# Timedelta.__floordiv__
|
| 593 |
+
|
| 594 |
+
def test_td_floordiv_timedeltalike_scalar(self):
|
| 595 |
+
# GH#18846
|
| 596 |
+
td = Timedelta(hours=3, minutes=4)
|
| 597 |
+
scalar = Timedelta(hours=3, minutes=3)
|
| 598 |
+
|
| 599 |
+
assert td // scalar == 1
|
| 600 |
+
assert -td // scalar.to_pytimedelta() == -2
|
| 601 |
+
assert (2 * td) // scalar.to_timedelta64() == 2
|
| 602 |
+
|
| 603 |
+
def test_td_floordiv_null_scalar(self):
|
| 604 |
+
# GH#18846
|
| 605 |
+
td = Timedelta(hours=3, minutes=4)
|
| 606 |
+
|
| 607 |
+
assert td // np.nan is NaT
|
| 608 |
+
assert np.isnan(td // NaT)
|
| 609 |
+
assert np.isnan(td // np.timedelta64("NaT"))
|
| 610 |
+
|
| 611 |
+
def test_td_floordiv_offsets(self):
|
| 612 |
+
# GH#19738
|
| 613 |
+
td = Timedelta(hours=3, minutes=4)
|
| 614 |
+
assert td // offsets.Hour(1) == 3
|
| 615 |
+
assert td // offsets.Minute(2) == 92
|
| 616 |
+
|
| 617 |
+
def test_td_floordiv_invalid_scalar(self):
|
| 618 |
+
# GH#18846
|
| 619 |
+
td = Timedelta(hours=3, minutes=4)
|
| 620 |
+
|
| 621 |
+
msg = "|".join(
|
| 622 |
+
[
|
| 623 |
+
r"Invalid dtype datetime64\[D\] for __floordiv__",
|
| 624 |
+
"'dtype' is an invalid keyword argument for this function",
|
| 625 |
+
"this function got an unexpected keyword argument 'dtype'",
|
| 626 |
+
r"ufunc '?floor_divide'? cannot use operands with types",
|
| 627 |
+
]
|
| 628 |
+
)
|
| 629 |
+
with pytest.raises(TypeError, match=msg):
|
| 630 |
+
td // np.datetime64("2016-01-01", dtype="datetime64[us]")
|
| 631 |
+
|
| 632 |
+
def test_td_floordiv_numeric_scalar(self):
|
| 633 |
+
# GH#18846
|
| 634 |
+
td = Timedelta(hours=3, minutes=4)
|
| 635 |
+
|
| 636 |
+
expected = Timedelta(hours=1, minutes=32)
|
| 637 |
+
assert td // 2 == expected
|
| 638 |
+
assert td // 2.0 == expected
|
| 639 |
+
assert td // np.float64(2.0) == expected
|
| 640 |
+
assert td // np.int32(2.0) == expected
|
| 641 |
+
assert td // np.uint8(2.0) == expected
|
| 642 |
+
|
| 643 |
+
def test_td_floordiv_timedeltalike_array(self):
|
| 644 |
+
# GH#18846
|
| 645 |
+
td = Timedelta(hours=3, minutes=4)
|
| 646 |
+
scalar = Timedelta(hours=3, minutes=3)
|
| 647 |
+
|
| 648 |
+
# Array-like others
|
| 649 |
+
assert td // np.array(scalar.to_timedelta64()) == 1
|
| 650 |
+
|
| 651 |
+
res = (3 * td) // np.array([scalar.to_timedelta64()])
|
| 652 |
+
expected = np.array([3], dtype=np.int64)
|
| 653 |
+
tm.assert_numpy_array_equal(res, expected)
|
| 654 |
+
|
| 655 |
+
res = (10 * td) // np.array([scalar.to_timedelta64(), np.timedelta64("NaT")])
|
| 656 |
+
expected = np.array([10, np.nan])
|
| 657 |
+
tm.assert_numpy_array_equal(res, expected)
|
| 658 |
+
|
| 659 |
+
def test_td_floordiv_numeric_series(self):
|
| 660 |
+
# GH#18846
|
| 661 |
+
td = Timedelta(hours=3, minutes=4)
|
| 662 |
+
ser = pd.Series([1], dtype=np.int64)
|
| 663 |
+
res = td // ser
|
| 664 |
+
assert res.dtype.kind == "m"
|
| 665 |
+
|
| 666 |
+
# ---------------------------------------------------------------
|
| 667 |
+
# Timedelta.__rfloordiv__
|
| 668 |
+
|
| 669 |
+
def test_td_rfloordiv_timedeltalike_scalar(self):
|
| 670 |
+
# GH#18846
|
| 671 |
+
td = Timedelta(hours=3, minutes=3)
|
| 672 |
+
scalar = Timedelta(hours=3, minutes=4)
|
| 673 |
+
|
| 674 |
+
# scalar others
|
| 675 |
+
# x // Timedelta is defined only for timedelta-like x. int-like,
|
| 676 |
+
# float-like, and date-like, in particular, should all either
|
| 677 |
+
# a) raise TypeError directly or
|
| 678 |
+
# b) return NotImplemented, following which the reversed
|
| 679 |
+
# operation will raise TypeError.
|
| 680 |
+
assert td.__rfloordiv__(scalar) == 1
|
| 681 |
+
assert (-td).__rfloordiv__(scalar.to_pytimedelta()) == -2
|
| 682 |
+
assert (2 * td).__rfloordiv__(scalar.to_timedelta64()) == 0
|
| 683 |
+
|
| 684 |
+
def test_td_rfloordiv_null_scalar(self):
|
| 685 |
+
# GH#18846
|
| 686 |
+
td = Timedelta(hours=3, minutes=3)
|
| 687 |
+
|
| 688 |
+
assert np.isnan(td.__rfloordiv__(NaT))
|
| 689 |
+
assert np.isnan(td.__rfloordiv__(np.timedelta64("NaT")))
|
| 690 |
+
|
| 691 |
+
def test_td_rfloordiv_offsets(self):
|
| 692 |
+
# GH#19738
|
| 693 |
+
assert offsets.Hour(1) // Timedelta(minutes=25) == 2
|
| 694 |
+
|
| 695 |
+
def test_td_rfloordiv_invalid_scalar(self):
|
| 696 |
+
# GH#18846
|
| 697 |
+
td = Timedelta(hours=3, minutes=3)
|
| 698 |
+
|
| 699 |
+
dt64 = np.datetime64("2016-01-01", "us")
|
| 700 |
+
|
| 701 |
+
assert td.__rfloordiv__(dt64) is NotImplemented
|
| 702 |
+
|
| 703 |
+
msg = (
|
| 704 |
+
r"unsupported operand type\(s\) for //: 'numpy.datetime64' and 'Timedelta'"
|
| 705 |
+
)
|
| 706 |
+
with pytest.raises(TypeError, match=msg):
|
| 707 |
+
dt64 // td
|
| 708 |
+
|
| 709 |
+
def test_td_rfloordiv_numeric_scalar(self):
|
| 710 |
+
# GH#18846
|
| 711 |
+
td = Timedelta(hours=3, minutes=3)
|
| 712 |
+
|
| 713 |
+
assert td.__rfloordiv__(np.nan) is NotImplemented
|
| 714 |
+
assert td.__rfloordiv__(3.5) is NotImplemented
|
| 715 |
+
assert td.__rfloordiv__(2) is NotImplemented
|
| 716 |
+
assert td.__rfloordiv__(np.float64(2.0)) is NotImplemented
|
| 717 |
+
assert td.__rfloordiv__(np.uint8(9)) is NotImplemented
|
| 718 |
+
assert td.__rfloordiv__(np.int32(2.0)) is NotImplemented
|
| 719 |
+
|
| 720 |
+
msg = r"unsupported operand type\(s\) for //: '.*' and 'Timedelta"
|
| 721 |
+
with pytest.raises(TypeError, match=msg):
|
| 722 |
+
np.float64(2.0) // td
|
| 723 |
+
with pytest.raises(TypeError, match=msg):
|
| 724 |
+
np.uint8(9) // td
|
| 725 |
+
with pytest.raises(TypeError, match=msg):
|
| 726 |
+
# deprecated GH#19761, enforced GH#29797
|
| 727 |
+
np.int32(2.0) // td
|
| 728 |
+
|
| 729 |
+
def test_td_rfloordiv_timedeltalike_array(self):
|
| 730 |
+
# GH#18846
|
| 731 |
+
td = Timedelta(hours=3, minutes=3)
|
| 732 |
+
scalar = Timedelta(hours=3, minutes=4)
|
| 733 |
+
|
| 734 |
+
# Array-like others
|
| 735 |
+
assert td.__rfloordiv__(np.array(scalar.to_timedelta64())) == 1
|
| 736 |
+
|
| 737 |
+
res = td.__rfloordiv__(np.array([(3 * scalar).to_timedelta64()]))
|
| 738 |
+
expected = np.array([3], dtype=np.int64)
|
| 739 |
+
tm.assert_numpy_array_equal(res, expected)
|
| 740 |
+
|
| 741 |
+
arr = np.array([(10 * scalar).to_timedelta64(), np.timedelta64("NaT")])
|
| 742 |
+
res = td.__rfloordiv__(arr)
|
| 743 |
+
expected = np.array([10, np.nan])
|
| 744 |
+
tm.assert_numpy_array_equal(res, expected)
|
| 745 |
+
|
| 746 |
+
def test_td_rfloordiv_intarray(self):
|
| 747 |
+
# deprecated GH#19761, enforced GH#29797
|
| 748 |
+
ints = np.array([1349654400, 1349740800, 1349827200, 1349913600]) * 10**9
|
| 749 |
+
|
| 750 |
+
msg = "Invalid dtype"
|
| 751 |
+
with pytest.raises(TypeError, match=msg):
|
| 752 |
+
ints // Timedelta(1, unit="s")
|
| 753 |
+
|
| 754 |
+
def test_td_rfloordiv_numeric_series(self):
|
| 755 |
+
# GH#18846
|
| 756 |
+
td = Timedelta(hours=3, minutes=3)
|
| 757 |
+
ser = pd.Series([1], dtype=np.int64)
|
| 758 |
+
res = td.__rfloordiv__(ser)
|
| 759 |
+
assert res is NotImplemented
|
| 760 |
+
|
| 761 |
+
msg = "Invalid dtype"
|
| 762 |
+
with pytest.raises(TypeError, match=msg):
|
| 763 |
+
# Deprecated GH#19761, enforced GH#29797
|
| 764 |
+
ser // td
|
| 765 |
+
|
| 766 |
+
# ----------------------------------------------------------------
|
| 767 |
+
# Timedelta.__mod__, __rmod__
|
| 768 |
+
|
| 769 |
+
def test_mod_timedeltalike(self):
|
| 770 |
+
# GH#19365
|
| 771 |
+
td = Timedelta(hours=37)
|
| 772 |
+
|
| 773 |
+
# Timedelta-like others
|
| 774 |
+
result = td % Timedelta(hours=6)
|
| 775 |
+
assert isinstance(result, Timedelta)
|
| 776 |
+
assert result == Timedelta(hours=1)
|
| 777 |
+
|
| 778 |
+
result = td % timedelta(minutes=60)
|
| 779 |
+
assert isinstance(result, Timedelta)
|
| 780 |
+
assert result == Timedelta(0)
|
| 781 |
+
|
| 782 |
+
result = td % NaT
|
| 783 |
+
assert result is NaT
|
| 784 |
+
|
| 785 |
+
def test_mod_timedelta64_nat(self):
|
| 786 |
+
# GH#19365
|
| 787 |
+
td = Timedelta(hours=37)
|
| 788 |
+
|
| 789 |
+
result = td % np.timedelta64("NaT", "ns")
|
| 790 |
+
assert result is NaT
|
| 791 |
+
|
| 792 |
+
def test_mod_timedelta64(self):
|
| 793 |
+
# GH#19365
|
| 794 |
+
td = Timedelta(hours=37)
|
| 795 |
+
|
| 796 |
+
result = td % np.timedelta64(2, "h")
|
| 797 |
+
assert isinstance(result, Timedelta)
|
| 798 |
+
assert result == Timedelta(hours=1)
|
| 799 |
+
|
| 800 |
+
def test_mod_offset(self):
|
| 801 |
+
# GH#19365
|
| 802 |
+
td = Timedelta(hours=37)
|
| 803 |
+
|
| 804 |
+
result = td % offsets.Hour(5)
|
| 805 |
+
assert isinstance(result, Timedelta)
|
| 806 |
+
assert result == Timedelta(hours=2)
|
| 807 |
+
|
| 808 |
+
def test_mod_numeric(self):
|
| 809 |
+
# GH#19365
|
| 810 |
+
td = Timedelta(hours=37)
|
| 811 |
+
|
| 812 |
+
# Numeric Others
|
| 813 |
+
result = td % 2
|
| 814 |
+
assert isinstance(result, Timedelta)
|
| 815 |
+
assert result == Timedelta(0)
|
| 816 |
+
|
| 817 |
+
result = td % 1e12
|
| 818 |
+
assert isinstance(result, Timedelta)
|
| 819 |
+
assert result == Timedelta(minutes=3, seconds=20)
|
| 820 |
+
|
| 821 |
+
result = td % int(1e12)
|
| 822 |
+
assert isinstance(result, Timedelta)
|
| 823 |
+
assert result == Timedelta(minutes=3, seconds=20)
|
| 824 |
+
|
| 825 |
+
def test_mod_invalid(self):
|
| 826 |
+
# GH#19365
|
| 827 |
+
td = Timedelta(hours=37)
|
| 828 |
+
msg = "unsupported operand type"
|
| 829 |
+
with pytest.raises(TypeError, match=msg):
|
| 830 |
+
td % Timestamp("2018-01-22")
|
| 831 |
+
|
| 832 |
+
with pytest.raises(TypeError, match=msg):
|
| 833 |
+
td % []
|
| 834 |
+
|
| 835 |
+
def test_rmod_pytimedelta(self):
|
| 836 |
+
# GH#19365
|
| 837 |
+
td = Timedelta(minutes=3)
|
| 838 |
+
|
| 839 |
+
result = timedelta(minutes=4) % td
|
| 840 |
+
assert isinstance(result, Timedelta)
|
| 841 |
+
assert result == Timedelta(minutes=1)
|
| 842 |
+
|
| 843 |
+
def test_rmod_timedelta64(self):
|
| 844 |
+
# GH#19365
|
| 845 |
+
td = Timedelta(minutes=3)
|
| 846 |
+
result = np.timedelta64(5, "m") % td
|
| 847 |
+
assert isinstance(result, Timedelta)
|
| 848 |
+
assert result == Timedelta(minutes=2)
|
| 849 |
+
|
| 850 |
+
def test_rmod_invalid(self):
|
| 851 |
+
# GH#19365
|
| 852 |
+
td = Timedelta(minutes=3)
|
| 853 |
+
|
| 854 |
+
msg = "unsupported operand"
|
| 855 |
+
with pytest.raises(TypeError, match=msg):
|
| 856 |
+
Timestamp("2018-01-22") % td
|
| 857 |
+
|
| 858 |
+
with pytest.raises(TypeError, match=msg):
|
| 859 |
+
15 % td
|
| 860 |
+
|
| 861 |
+
with pytest.raises(TypeError, match=msg):
|
| 862 |
+
16.0 % td
|
| 863 |
+
|
| 864 |
+
msg = "Invalid dtype int"
|
| 865 |
+
with pytest.raises(TypeError, match=msg):
|
| 866 |
+
np.array([22, 24]) % td
|
| 867 |
+
|
| 868 |
+
# ----------------------------------------------------------------
|
| 869 |
+
# Timedelta.__divmod__, __rdivmod__
|
| 870 |
+
|
| 871 |
+
def test_divmod_numeric(self):
|
| 872 |
+
# GH#19365
|
| 873 |
+
td = Timedelta(days=2, hours=6)
|
| 874 |
+
|
| 875 |
+
result = divmod(td, 53 * 3600 * 1e9)
|
| 876 |
+
assert result[0] == Timedelta(1, unit="ns")
|
| 877 |
+
assert isinstance(result[1], Timedelta)
|
| 878 |
+
assert result[1] == Timedelta(hours=1)
|
| 879 |
+
|
| 880 |
+
assert result
|
| 881 |
+
result = divmod(td, np.nan)
|
| 882 |
+
assert result[0] is NaT
|
| 883 |
+
assert result[1] is NaT
|
| 884 |
+
|
| 885 |
+
def test_divmod(self):
|
| 886 |
+
# GH#19365
|
| 887 |
+
td = Timedelta(days=2, hours=6)
|
| 888 |
+
|
| 889 |
+
result = divmod(td, timedelta(days=1))
|
| 890 |
+
assert result[0] == 2
|
| 891 |
+
assert isinstance(result[1], Timedelta)
|
| 892 |
+
assert result[1] == Timedelta(hours=6)
|
| 893 |
+
|
| 894 |
+
result = divmod(td, 54)
|
| 895 |
+
assert result[0] == Timedelta(hours=1)
|
| 896 |
+
assert isinstance(result[1], Timedelta)
|
| 897 |
+
assert result[1] == Timedelta(0)
|
| 898 |
+
|
| 899 |
+
result = divmod(td, NaT)
|
| 900 |
+
assert np.isnan(result[0])
|
| 901 |
+
assert result[1] is NaT
|
| 902 |
+
|
| 903 |
+
def test_divmod_offset(self):
|
| 904 |
+
# GH#19365
|
| 905 |
+
td = Timedelta(days=2, hours=6)
|
| 906 |
+
|
| 907 |
+
result = divmod(td, offsets.Hour(-4))
|
| 908 |
+
assert result[0] == -14
|
| 909 |
+
assert isinstance(result[1], Timedelta)
|
| 910 |
+
assert result[1] == Timedelta(hours=-2)
|
| 911 |
+
|
| 912 |
+
def test_divmod_invalid(self):
|
| 913 |
+
# GH#19365
|
| 914 |
+
td = Timedelta(days=2, hours=6)
|
| 915 |
+
|
| 916 |
+
msg = r"unsupported operand type\(s\) for //: 'Timedelta' and 'Timestamp'"
|
| 917 |
+
with pytest.raises(TypeError, match=msg):
|
| 918 |
+
divmod(td, Timestamp("2018-01-22"))
|
| 919 |
+
|
| 920 |
+
def test_rdivmod_pytimedelta(self):
|
| 921 |
+
# GH#19365
|
| 922 |
+
result = divmod(timedelta(days=2, hours=6), Timedelta(days=1))
|
| 923 |
+
assert result[0] == 2
|
| 924 |
+
assert isinstance(result[1], Timedelta)
|
| 925 |
+
assert result[1] == Timedelta(hours=6)
|
| 926 |
+
|
| 927 |
+
def test_rdivmod_offset(self):
|
| 928 |
+
result = divmod(offsets.Hour(54), Timedelta(hours=-4))
|
| 929 |
+
assert result[0] == -14
|
| 930 |
+
assert isinstance(result[1], Timedelta)
|
| 931 |
+
assert result[1] == Timedelta(hours=-2)
|
| 932 |
+
|
| 933 |
+
def test_rdivmod_invalid(self):
|
| 934 |
+
# GH#19365
|
| 935 |
+
td = Timedelta(minutes=3)
|
| 936 |
+
msg = "unsupported operand type"
|
| 937 |
+
|
| 938 |
+
with pytest.raises(TypeError, match=msg):
|
| 939 |
+
divmod(Timestamp("2018-01-22"), td)
|
| 940 |
+
|
| 941 |
+
with pytest.raises(TypeError, match=msg):
|
| 942 |
+
divmod(15, td)
|
| 943 |
+
|
| 944 |
+
with pytest.raises(TypeError, match=msg):
|
| 945 |
+
divmod(16.0, td)
|
| 946 |
+
|
| 947 |
+
msg = "Invalid dtype int"
|
| 948 |
+
with pytest.raises(TypeError, match=msg):
|
| 949 |
+
divmod(np.array([22, 24]), td)
|
| 950 |
+
|
| 951 |
+
# ----------------------------------------------------------------
|
| 952 |
+
|
| 953 |
+
@pytest.mark.parametrize(
|
| 954 |
+
"op", [operator.mul, ops.rmul, operator.truediv, ops.rdiv, ops.rsub]
|
| 955 |
+
)
|
| 956 |
+
@pytest.mark.parametrize(
|
| 957 |
+
"arr",
|
| 958 |
+
[
|
| 959 |
+
np.array([Timestamp("20130101 9:01"), Timestamp("20121230 9:02")]),
|
| 960 |
+
np.array([Timestamp("2021-11-09 09:54:00"), Timedelta("1D")]),
|
| 961 |
+
],
|
| 962 |
+
)
|
| 963 |
+
def test_td_op_timedelta_timedeltalike_array(self, op, arr):
|
| 964 |
+
msg = "unsupported operand type|cannot use operands with types"
|
| 965 |
+
with pytest.raises(TypeError, match=msg):
|
| 966 |
+
op(arr, Timedelta("1D"))
|
| 967 |
+
|
| 968 |
+
|
| 969 |
+
class TestTimedeltaComparison:
|
| 970 |
+
@pytest.mark.skip_ubsan
|
| 971 |
+
def test_compare_pytimedelta_bounds(self):
|
| 972 |
+
# GH#49021 don't overflow on comparison with very large pytimedeltas
|
| 973 |
+
|
| 974 |
+
for unit in ["ns", "us"]:
|
| 975 |
+
tdmax = Timedelta.max.as_unit(unit).max
|
| 976 |
+
tdmin = Timedelta.min.as_unit(unit).min
|
| 977 |
+
|
| 978 |
+
assert tdmax < timedelta.max
|
| 979 |
+
assert tdmax <= timedelta.max
|
| 980 |
+
assert not tdmax > timedelta.max
|
| 981 |
+
assert not tdmax >= timedelta.max
|
| 982 |
+
assert tdmax != timedelta.max
|
| 983 |
+
assert not tdmax == timedelta.max
|
| 984 |
+
|
| 985 |
+
assert tdmin > timedelta.min
|
| 986 |
+
assert tdmin >= timedelta.min
|
| 987 |
+
assert not tdmin < timedelta.min
|
| 988 |
+
assert not tdmin <= timedelta.min
|
| 989 |
+
assert tdmin != timedelta.min
|
| 990 |
+
assert not tdmin == timedelta.min
|
| 991 |
+
|
| 992 |
+
# But the "ms" and "s"-reso bounds extend pass pytimedelta
|
| 993 |
+
for unit in ["ms", "s"]:
|
| 994 |
+
tdmax = Timedelta.max.as_unit(unit).max
|
| 995 |
+
tdmin = Timedelta.min.as_unit(unit).min
|
| 996 |
+
|
| 997 |
+
assert tdmax > timedelta.max
|
| 998 |
+
assert tdmax >= timedelta.max
|
| 999 |
+
assert not tdmax < timedelta.max
|
| 1000 |
+
assert not tdmax <= timedelta.max
|
| 1001 |
+
assert tdmax != timedelta.max
|
| 1002 |
+
assert not tdmax == timedelta.max
|
| 1003 |
+
|
| 1004 |
+
assert tdmin < timedelta.min
|
| 1005 |
+
assert tdmin <= timedelta.min
|
| 1006 |
+
assert not tdmin > timedelta.min
|
| 1007 |
+
assert not tdmin >= timedelta.min
|
| 1008 |
+
assert tdmin != timedelta.min
|
| 1009 |
+
assert not tdmin == timedelta.min
|
| 1010 |
+
|
| 1011 |
+
def test_compare_pytimedelta_bounds2(self):
|
| 1012 |
+
# a pytimedelta outside the microsecond bounds
|
| 1013 |
+
pytd = timedelta(days=999999999, seconds=86399)
|
| 1014 |
+
# NB: np.timedelta64(td, "s"") incorrectly overflows
|
| 1015 |
+
td64 = np.timedelta64(pytd.days, "D") + np.timedelta64(pytd.seconds, "s")
|
| 1016 |
+
td = Timedelta(td64)
|
| 1017 |
+
assert td.days == pytd.days
|
| 1018 |
+
assert td.seconds == pytd.seconds
|
| 1019 |
+
|
| 1020 |
+
assert td == pytd
|
| 1021 |
+
assert not td != pytd
|
| 1022 |
+
assert not td < pytd
|
| 1023 |
+
assert not td > pytd
|
| 1024 |
+
assert td <= pytd
|
| 1025 |
+
assert td >= pytd
|
| 1026 |
+
|
| 1027 |
+
td2 = td - Timedelta(seconds=1).as_unit("s")
|
| 1028 |
+
assert td2 != pytd
|
| 1029 |
+
assert not td2 == pytd
|
| 1030 |
+
assert td2 < pytd
|
| 1031 |
+
assert td2 <= pytd
|
| 1032 |
+
assert not td2 > pytd
|
| 1033 |
+
assert not td2 >= pytd
|
| 1034 |
+
|
| 1035 |
+
def test_compare_tick(self, tick_classes):
|
| 1036 |
+
cls = tick_classes
|
| 1037 |
+
|
| 1038 |
+
off = cls(4)
|
| 1039 |
+
td = off._as_pd_timedelta
|
| 1040 |
+
assert isinstance(td, Timedelta)
|
| 1041 |
+
|
| 1042 |
+
assert td == off
|
| 1043 |
+
assert not td != off
|
| 1044 |
+
assert td <= off
|
| 1045 |
+
assert td >= off
|
| 1046 |
+
assert not td < off
|
| 1047 |
+
assert not td > off
|
| 1048 |
+
|
| 1049 |
+
assert not td == 2 * off
|
| 1050 |
+
assert td != 2 * off
|
| 1051 |
+
assert td <= 2 * off
|
| 1052 |
+
assert td < 2 * off
|
| 1053 |
+
assert not td >= 2 * off
|
| 1054 |
+
assert not td > 2 * off
|
| 1055 |
+
|
| 1056 |
+
def test_comparison_object_array(self):
|
| 1057 |
+
# analogous to GH#15183
|
| 1058 |
+
td = Timedelta("2 days")
|
| 1059 |
+
other = Timedelta("3 hours")
|
| 1060 |
+
|
| 1061 |
+
arr = np.array([other, td], dtype=object)
|
| 1062 |
+
res = arr == td
|
| 1063 |
+
expected = np.array([False, True], dtype=bool)
|
| 1064 |
+
assert (res == expected).all()
|
| 1065 |
+
|
| 1066 |
+
# 2D case
|
| 1067 |
+
arr = np.array([[other, td], [td, other]], dtype=object)
|
| 1068 |
+
res = arr != td
|
| 1069 |
+
expected = np.array([[True, False], [False, True]], dtype=bool)
|
| 1070 |
+
assert res.shape == expected.shape
|
| 1071 |
+
assert (res == expected).all()
|
| 1072 |
+
|
| 1073 |
+
def test_compare_timedelta_ndarray(self):
|
| 1074 |
+
# GH#11835
|
| 1075 |
+
periods = [Timedelta("0 days 01:00:00"), Timedelta("0 days 01:00:00")]
|
| 1076 |
+
arr = np.array(periods)
|
| 1077 |
+
result = arr[0] > arr
|
| 1078 |
+
expected = np.array([False, False])
|
| 1079 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 1080 |
+
|
| 1081 |
+
def test_compare_td64_ndarray(self):
|
| 1082 |
+
# GG#33441
|
| 1083 |
+
arr = np.arange(5).astype("timedelta64[ns]")
|
| 1084 |
+
td = Timedelta(arr[1])
|
| 1085 |
+
|
| 1086 |
+
expected = np.array([False, True, False, False, False], dtype=bool)
|
| 1087 |
+
|
| 1088 |
+
result = td == arr
|
| 1089 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 1090 |
+
|
| 1091 |
+
result = arr == td
|
| 1092 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 1093 |
+
|
| 1094 |
+
result = td != arr
|
| 1095 |
+
tm.assert_numpy_array_equal(result, ~expected)
|
| 1096 |
+
|
| 1097 |
+
result = arr != td
|
| 1098 |
+
tm.assert_numpy_array_equal(result, ~expected)
|
| 1099 |
+
|
| 1100 |
+
def test_compare_custom_object(self):
|
| 1101 |
+
"""
|
| 1102 |
+
Make sure non supported operations on Timedelta returns NonImplemented
|
| 1103 |
+
and yields to other operand (GH#20829).
|
| 1104 |
+
"""
|
| 1105 |
+
|
| 1106 |
+
class CustomClass:
|
| 1107 |
+
def __init__(self, cmp_result=None) -> None:
|
| 1108 |
+
self.cmp_result = cmp_result
|
| 1109 |
+
|
| 1110 |
+
def generic_result(self):
|
| 1111 |
+
if self.cmp_result is None:
|
| 1112 |
+
return NotImplemented
|
| 1113 |
+
else:
|
| 1114 |
+
return self.cmp_result
|
| 1115 |
+
|
| 1116 |
+
def __eq__(self, other):
|
| 1117 |
+
return self.generic_result()
|
| 1118 |
+
|
| 1119 |
+
def __gt__(self, other):
|
| 1120 |
+
return self.generic_result()
|
| 1121 |
+
|
| 1122 |
+
t = Timedelta("1s")
|
| 1123 |
+
|
| 1124 |
+
assert t != "string"
|
| 1125 |
+
assert t != 1
|
| 1126 |
+
assert t != CustomClass()
|
| 1127 |
+
assert t != CustomClass(cmp_result=False)
|
| 1128 |
+
|
| 1129 |
+
assert t < CustomClass(cmp_result=True)
|
| 1130 |
+
assert not t < CustomClass(cmp_result=False)
|
| 1131 |
+
|
| 1132 |
+
assert t == CustomClass(cmp_result=True)
|
| 1133 |
+
|
| 1134 |
+
@pytest.mark.parametrize("val", ["string", 1])
|
| 1135 |
+
def test_compare_unknown_type(self, val):
|
| 1136 |
+
# GH#20829
|
| 1137 |
+
t = Timedelta("1s")
|
| 1138 |
+
msg = "not supported between instances of 'Timedelta' and '(int|str)'"
|
| 1139 |
+
with pytest.raises(TypeError, match=msg):
|
| 1140 |
+
t >= val
|
| 1141 |
+
with pytest.raises(TypeError, match=msg):
|
| 1142 |
+
t > val
|
| 1143 |
+
with pytest.raises(TypeError, match=msg):
|
| 1144 |
+
t <= val
|
| 1145 |
+
with pytest.raises(TypeError, match=msg):
|
| 1146 |
+
t < val
|
| 1147 |
+
|
| 1148 |
+
|
| 1149 |
+
def test_ops_notimplemented():
|
| 1150 |
+
class Other:
|
| 1151 |
+
pass
|
| 1152 |
+
|
| 1153 |
+
other = Other()
|
| 1154 |
+
|
| 1155 |
+
td = Timedelta("1 day")
|
| 1156 |
+
assert td.__add__(other) is NotImplemented
|
| 1157 |
+
assert td.__sub__(other) is NotImplemented
|
| 1158 |
+
assert td.__truediv__(other) is NotImplemented
|
| 1159 |
+
assert td.__mul__(other) is NotImplemented
|
| 1160 |
+
assert td.__floordiv__(other) is NotImplemented
|
| 1161 |
+
|
| 1162 |
+
|
| 1163 |
+
def test_ops_error_str():
|
| 1164 |
+
# GH#13624
|
| 1165 |
+
td = Timedelta("1 day")
|
| 1166 |
+
|
| 1167 |
+
for left, right in [(td, "a"), ("a", td)]:
|
| 1168 |
+
msg = "|".join(
|
| 1169 |
+
[
|
| 1170 |
+
"unsupported operand type",
|
| 1171 |
+
r'can only concatenate str \(not "Timedelta"\) to str',
|
| 1172 |
+
"must be str, not Timedelta",
|
| 1173 |
+
]
|
| 1174 |
+
)
|
| 1175 |
+
with pytest.raises(TypeError, match=msg):
|
| 1176 |
+
left + right
|
| 1177 |
+
|
| 1178 |
+
msg = "not supported between instances of"
|
| 1179 |
+
with pytest.raises(TypeError, match=msg):
|
| 1180 |
+
left > right
|
| 1181 |
+
|
| 1182 |
+
assert not left == right # pylint: disable=unneeded-not
|
| 1183 |
+
assert left != right
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/test_constructors.py
ADDED
|
@@ -0,0 +1,698 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import timedelta
|
| 2 |
+
from itertools import product
|
| 3 |
+
|
| 4 |
+
import numpy as np
|
| 5 |
+
import pytest
|
| 6 |
+
|
| 7 |
+
from pandas._libs.tslibs import OutOfBoundsTimedelta
|
| 8 |
+
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
| 9 |
+
|
| 10 |
+
from pandas import (
|
| 11 |
+
Index,
|
| 12 |
+
NaT,
|
| 13 |
+
Timedelta,
|
| 14 |
+
TimedeltaIndex,
|
| 15 |
+
offsets,
|
| 16 |
+
to_timedelta,
|
| 17 |
+
)
|
| 18 |
+
import pandas._testing as tm
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class TestTimedeltaConstructorUnitKeyword:
|
| 22 |
+
@pytest.mark.parametrize("unit", ["Y", "y", "M"])
|
| 23 |
+
def test_unit_m_y_raises(self, unit):
|
| 24 |
+
msg = "Units 'M', 'Y', and 'y' are no longer supported"
|
| 25 |
+
|
| 26 |
+
with pytest.raises(ValueError, match=msg):
|
| 27 |
+
Timedelta(10, unit)
|
| 28 |
+
|
| 29 |
+
with pytest.raises(ValueError, match=msg):
|
| 30 |
+
to_timedelta(10, unit)
|
| 31 |
+
|
| 32 |
+
with pytest.raises(ValueError, match=msg):
|
| 33 |
+
to_timedelta([1, 2], unit)
|
| 34 |
+
|
| 35 |
+
@pytest.mark.parametrize(
|
| 36 |
+
"unit,unit_depr",
|
| 37 |
+
[
|
| 38 |
+
("h", "H"),
|
| 39 |
+
("min", "T"),
|
| 40 |
+
("s", "S"),
|
| 41 |
+
("ms", "L"),
|
| 42 |
+
("ns", "N"),
|
| 43 |
+
("us", "U"),
|
| 44 |
+
],
|
| 45 |
+
)
|
| 46 |
+
def test_units_H_T_S_L_N_U_deprecated(self, unit, unit_depr):
|
| 47 |
+
# GH#52536
|
| 48 |
+
msg = f"'{unit_depr}' is deprecated and will be removed in a future version."
|
| 49 |
+
|
| 50 |
+
expected = Timedelta(1, unit=unit)
|
| 51 |
+
with tm.assert_produces_warning(FutureWarning, match=msg):
|
| 52 |
+
result = Timedelta(1, unit=unit_depr)
|
| 53 |
+
tm.assert_equal(result, expected)
|
| 54 |
+
|
| 55 |
+
@pytest.mark.parametrize(
|
| 56 |
+
"unit, np_unit",
|
| 57 |
+
[(value, "W") for value in ["W", "w"]]
|
| 58 |
+
+ [(value, "D") for value in ["D", "d", "days", "day", "Days", "Day"]]
|
| 59 |
+
+ [
|
| 60 |
+
(value, "m")
|
| 61 |
+
for value in [
|
| 62 |
+
"m",
|
| 63 |
+
"minute",
|
| 64 |
+
"min",
|
| 65 |
+
"minutes",
|
| 66 |
+
"Minute",
|
| 67 |
+
"Min",
|
| 68 |
+
"Minutes",
|
| 69 |
+
]
|
| 70 |
+
]
|
| 71 |
+
+ [
|
| 72 |
+
(value, "s")
|
| 73 |
+
for value in [
|
| 74 |
+
"s",
|
| 75 |
+
"seconds",
|
| 76 |
+
"sec",
|
| 77 |
+
"second",
|
| 78 |
+
"Seconds",
|
| 79 |
+
"Sec",
|
| 80 |
+
"Second",
|
| 81 |
+
]
|
| 82 |
+
]
|
| 83 |
+
+ [
|
| 84 |
+
(value, "ms")
|
| 85 |
+
for value in [
|
| 86 |
+
"ms",
|
| 87 |
+
"milliseconds",
|
| 88 |
+
"millisecond",
|
| 89 |
+
"milli",
|
| 90 |
+
"millis",
|
| 91 |
+
"MS",
|
| 92 |
+
"Milliseconds",
|
| 93 |
+
"Millisecond",
|
| 94 |
+
"Milli",
|
| 95 |
+
"Millis",
|
| 96 |
+
]
|
| 97 |
+
]
|
| 98 |
+
+ [
|
| 99 |
+
(value, "us")
|
| 100 |
+
for value in [
|
| 101 |
+
"us",
|
| 102 |
+
"microseconds",
|
| 103 |
+
"microsecond",
|
| 104 |
+
"micro",
|
| 105 |
+
"micros",
|
| 106 |
+
"u",
|
| 107 |
+
"US",
|
| 108 |
+
"Microseconds",
|
| 109 |
+
"Microsecond",
|
| 110 |
+
"Micro",
|
| 111 |
+
"Micros",
|
| 112 |
+
"U",
|
| 113 |
+
]
|
| 114 |
+
]
|
| 115 |
+
+ [
|
| 116 |
+
(value, "ns")
|
| 117 |
+
for value in [
|
| 118 |
+
"ns",
|
| 119 |
+
"nanoseconds",
|
| 120 |
+
"nanosecond",
|
| 121 |
+
"nano",
|
| 122 |
+
"nanos",
|
| 123 |
+
"n",
|
| 124 |
+
"NS",
|
| 125 |
+
"Nanoseconds",
|
| 126 |
+
"Nanosecond",
|
| 127 |
+
"Nano",
|
| 128 |
+
"Nanos",
|
| 129 |
+
"N",
|
| 130 |
+
]
|
| 131 |
+
],
|
| 132 |
+
)
|
| 133 |
+
@pytest.mark.parametrize("wrapper", [np.array, list, Index])
|
| 134 |
+
def test_unit_parser(self, unit, np_unit, wrapper):
|
| 135 |
+
# validate all units, GH 6855, GH 21762
|
| 136 |
+
# array-likes
|
| 137 |
+
expected = TimedeltaIndex(
|
| 138 |
+
[np.timedelta64(i, np_unit) for i in np.arange(5).tolist()],
|
| 139 |
+
dtype="m8[ns]",
|
| 140 |
+
)
|
| 141 |
+
# TODO(2.0): the desired output dtype may have non-nano resolution
|
| 142 |
+
msg = f"'{unit}' is deprecated and will be removed in a future version."
|
| 143 |
+
|
| 144 |
+
if (unit, np_unit) in (("u", "us"), ("U", "us"), ("n", "ns"), ("N", "ns")):
|
| 145 |
+
warn = FutureWarning
|
| 146 |
+
else:
|
| 147 |
+
warn = FutureWarning
|
| 148 |
+
msg = "The 'unit' keyword in TimedeltaIndex construction is deprecated"
|
| 149 |
+
with tm.assert_produces_warning(warn, match=msg):
|
| 150 |
+
result = to_timedelta(wrapper(range(5)), unit=unit)
|
| 151 |
+
tm.assert_index_equal(result, expected)
|
| 152 |
+
result = TimedeltaIndex(wrapper(range(5)), unit=unit)
|
| 153 |
+
tm.assert_index_equal(result, expected)
|
| 154 |
+
|
| 155 |
+
str_repr = [f"{x}{unit}" for x in np.arange(5)]
|
| 156 |
+
result = to_timedelta(wrapper(str_repr))
|
| 157 |
+
tm.assert_index_equal(result, expected)
|
| 158 |
+
result = to_timedelta(wrapper(str_repr))
|
| 159 |
+
tm.assert_index_equal(result, expected)
|
| 160 |
+
|
| 161 |
+
# scalar
|
| 162 |
+
expected = Timedelta(np.timedelta64(2, np_unit).astype("timedelta64[ns]"))
|
| 163 |
+
result = to_timedelta(2, unit=unit)
|
| 164 |
+
assert result == expected
|
| 165 |
+
result = Timedelta(2, unit=unit)
|
| 166 |
+
assert result == expected
|
| 167 |
+
|
| 168 |
+
result = to_timedelta(f"2{unit}")
|
| 169 |
+
assert result == expected
|
| 170 |
+
result = Timedelta(f"2{unit}")
|
| 171 |
+
assert result == expected
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
def test_construct_from_kwargs_overflow():
|
| 175 |
+
# GH#55503
|
| 176 |
+
msg = "seconds=86400000000000000000, milliseconds=0, microseconds=0, nanoseconds=0"
|
| 177 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 178 |
+
Timedelta(days=10**6)
|
| 179 |
+
msg = "seconds=60000000000000000000, milliseconds=0, microseconds=0, nanoseconds=0"
|
| 180 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 181 |
+
Timedelta(minutes=10**9)
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
def test_construct_with_weeks_unit_overflow():
|
| 185 |
+
# GH#47268 don't silently wrap around
|
| 186 |
+
with pytest.raises(OutOfBoundsTimedelta, match="without overflow"):
|
| 187 |
+
Timedelta(1000000000000000000, unit="W")
|
| 188 |
+
|
| 189 |
+
with pytest.raises(OutOfBoundsTimedelta, match="without overflow"):
|
| 190 |
+
Timedelta(1000000000000000000.0, unit="W")
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
def test_construct_from_td64_with_unit():
|
| 194 |
+
# ignore the unit, as it may cause silently overflows leading to incorrect
|
| 195 |
+
# results, and in non-overflow cases is irrelevant GH#46827
|
| 196 |
+
obj = np.timedelta64(123456789000000000, "h")
|
| 197 |
+
|
| 198 |
+
with pytest.raises(OutOfBoundsTimedelta, match="123456789000000000 hours"):
|
| 199 |
+
Timedelta(obj, unit="ps")
|
| 200 |
+
|
| 201 |
+
with pytest.raises(OutOfBoundsTimedelta, match="123456789000000000 hours"):
|
| 202 |
+
Timedelta(obj, unit="ns")
|
| 203 |
+
|
| 204 |
+
with pytest.raises(OutOfBoundsTimedelta, match="123456789000000000 hours"):
|
| 205 |
+
Timedelta(obj)
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
def test_from_td64_retain_resolution():
|
| 209 |
+
# case where we retain millisecond resolution
|
| 210 |
+
obj = np.timedelta64(12345, "ms")
|
| 211 |
+
|
| 212 |
+
td = Timedelta(obj)
|
| 213 |
+
assert td._value == obj.view("i8")
|
| 214 |
+
assert td._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
| 215 |
+
|
| 216 |
+
# Case where we cast to nearest-supported reso
|
| 217 |
+
obj2 = np.timedelta64(1234, "D")
|
| 218 |
+
td2 = Timedelta(obj2)
|
| 219 |
+
assert td2._creso == NpyDatetimeUnit.NPY_FR_s.value
|
| 220 |
+
assert td2 == obj2
|
| 221 |
+
assert td2.days == 1234
|
| 222 |
+
|
| 223 |
+
# Case that _would_ overflow if we didn't support non-nano
|
| 224 |
+
obj3 = np.timedelta64(1000000000000000000, "us")
|
| 225 |
+
td3 = Timedelta(obj3)
|
| 226 |
+
assert td3.total_seconds() == 1000000000000
|
| 227 |
+
assert td3._creso == NpyDatetimeUnit.NPY_FR_us.value
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
def test_from_pytimedelta_us_reso():
|
| 231 |
+
# pytimedelta has microsecond resolution, so Timedelta(pytd) inherits that
|
| 232 |
+
td = timedelta(days=4, minutes=3)
|
| 233 |
+
result = Timedelta(td)
|
| 234 |
+
assert result.to_pytimedelta() == td
|
| 235 |
+
assert result._creso == NpyDatetimeUnit.NPY_FR_us.value
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
def test_from_tick_reso():
|
| 239 |
+
tick = offsets.Nano()
|
| 240 |
+
assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
| 241 |
+
|
| 242 |
+
tick = offsets.Micro()
|
| 243 |
+
assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_us.value
|
| 244 |
+
|
| 245 |
+
tick = offsets.Milli()
|
| 246 |
+
assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
| 247 |
+
|
| 248 |
+
tick = offsets.Second()
|
| 249 |
+
assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_s.value
|
| 250 |
+
|
| 251 |
+
# everything above Second gets cast to the closest supported reso: second
|
| 252 |
+
tick = offsets.Minute()
|
| 253 |
+
assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_s.value
|
| 254 |
+
|
| 255 |
+
tick = offsets.Hour()
|
| 256 |
+
assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_s.value
|
| 257 |
+
|
| 258 |
+
tick = offsets.Day()
|
| 259 |
+
assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_s.value
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
def test_construction():
|
| 263 |
+
expected = np.timedelta64(10, "D").astype("m8[ns]").view("i8")
|
| 264 |
+
assert Timedelta(10, unit="d")._value == expected
|
| 265 |
+
assert Timedelta(10.0, unit="d")._value == expected
|
| 266 |
+
assert Timedelta("10 days")._value == expected
|
| 267 |
+
assert Timedelta(days=10)._value == expected
|
| 268 |
+
assert Timedelta(days=10.0)._value == expected
|
| 269 |
+
|
| 270 |
+
expected += np.timedelta64(10, "s").astype("m8[ns]").view("i8")
|
| 271 |
+
assert Timedelta("10 days 00:00:10")._value == expected
|
| 272 |
+
assert Timedelta(days=10, seconds=10)._value == expected
|
| 273 |
+
assert Timedelta(days=10, milliseconds=10 * 1000)._value == expected
|
| 274 |
+
assert Timedelta(days=10, microseconds=10 * 1000 * 1000)._value == expected
|
| 275 |
+
|
| 276 |
+
# rounding cases
|
| 277 |
+
assert Timedelta(82739999850000)._value == 82739999850000
|
| 278 |
+
assert "0 days 22:58:59.999850" in str(Timedelta(82739999850000))
|
| 279 |
+
assert Timedelta(123072001000000)._value == 123072001000000
|
| 280 |
+
assert "1 days 10:11:12.001" in str(Timedelta(123072001000000))
|
| 281 |
+
|
| 282 |
+
# string conversion with/without leading zero
|
| 283 |
+
# GH#9570
|
| 284 |
+
assert Timedelta("0:00:00") == timedelta(hours=0)
|
| 285 |
+
assert Timedelta("00:00:00") == timedelta(hours=0)
|
| 286 |
+
assert Timedelta("-1:00:00") == -timedelta(hours=1)
|
| 287 |
+
assert Timedelta("-01:00:00") == -timedelta(hours=1)
|
| 288 |
+
|
| 289 |
+
# more strings & abbrevs
|
| 290 |
+
# GH#8190
|
| 291 |
+
assert Timedelta("1 h") == timedelta(hours=1)
|
| 292 |
+
assert Timedelta("1 hour") == timedelta(hours=1)
|
| 293 |
+
assert Timedelta("1 hr") == timedelta(hours=1)
|
| 294 |
+
assert Timedelta("1 hours") == timedelta(hours=1)
|
| 295 |
+
assert Timedelta("-1 hours") == -timedelta(hours=1)
|
| 296 |
+
assert Timedelta("1 m") == timedelta(minutes=1)
|
| 297 |
+
assert Timedelta("1.5 m") == timedelta(seconds=90)
|
| 298 |
+
assert Timedelta("1 minute") == timedelta(minutes=1)
|
| 299 |
+
assert Timedelta("1 minutes") == timedelta(minutes=1)
|
| 300 |
+
assert Timedelta("1 s") == timedelta(seconds=1)
|
| 301 |
+
assert Timedelta("1 second") == timedelta(seconds=1)
|
| 302 |
+
assert Timedelta("1 seconds") == timedelta(seconds=1)
|
| 303 |
+
assert Timedelta("1 ms") == timedelta(milliseconds=1)
|
| 304 |
+
assert Timedelta("1 milli") == timedelta(milliseconds=1)
|
| 305 |
+
assert Timedelta("1 millisecond") == timedelta(milliseconds=1)
|
| 306 |
+
assert Timedelta("1 us") == timedelta(microseconds=1)
|
| 307 |
+
assert Timedelta("1 µs") == timedelta(microseconds=1)
|
| 308 |
+
assert Timedelta("1 micros") == timedelta(microseconds=1)
|
| 309 |
+
assert Timedelta("1 microsecond") == timedelta(microseconds=1)
|
| 310 |
+
assert Timedelta("1.5 microsecond") == Timedelta("00:00:00.000001500")
|
| 311 |
+
assert Timedelta("1 ns") == Timedelta("00:00:00.000000001")
|
| 312 |
+
assert Timedelta("1 nano") == Timedelta("00:00:00.000000001")
|
| 313 |
+
assert Timedelta("1 nanosecond") == Timedelta("00:00:00.000000001")
|
| 314 |
+
|
| 315 |
+
# combos
|
| 316 |
+
assert Timedelta("10 days 1 hour") == timedelta(days=10, hours=1)
|
| 317 |
+
assert Timedelta("10 days 1 h") == timedelta(days=10, hours=1)
|
| 318 |
+
assert Timedelta("10 days 1 h 1m 1s") == timedelta(
|
| 319 |
+
days=10, hours=1, minutes=1, seconds=1
|
| 320 |
+
)
|
| 321 |
+
assert Timedelta("-10 days 1 h 1m 1s") == -timedelta(
|
| 322 |
+
days=10, hours=1, minutes=1, seconds=1
|
| 323 |
+
)
|
| 324 |
+
assert Timedelta("-10 days 1 h 1m 1s") == -timedelta(
|
| 325 |
+
days=10, hours=1, minutes=1, seconds=1
|
| 326 |
+
)
|
| 327 |
+
assert Timedelta("-10 days 1 h 1m 1s 3us") == -timedelta(
|
| 328 |
+
days=10, hours=1, minutes=1, seconds=1, microseconds=3
|
| 329 |
+
)
|
| 330 |
+
assert Timedelta("-10 days 1 h 1.5m 1s 3us") == -timedelta(
|
| 331 |
+
days=10, hours=1, minutes=1, seconds=31, microseconds=3
|
| 332 |
+
)
|
| 333 |
+
|
| 334 |
+
# Currently invalid as it has a - on the hh:mm:dd part
|
| 335 |
+
# (only allowed on the days)
|
| 336 |
+
msg = "only leading negative signs are allowed"
|
| 337 |
+
with pytest.raises(ValueError, match=msg):
|
| 338 |
+
Timedelta("-10 days -1 h 1.5m 1s 3us")
|
| 339 |
+
|
| 340 |
+
# only leading neg signs are allowed
|
| 341 |
+
with pytest.raises(ValueError, match=msg):
|
| 342 |
+
Timedelta("10 days -1 h 1.5m 1s 3us")
|
| 343 |
+
|
| 344 |
+
# no units specified
|
| 345 |
+
msg = "no units specified"
|
| 346 |
+
with pytest.raises(ValueError, match=msg):
|
| 347 |
+
Timedelta("3.1415")
|
| 348 |
+
|
| 349 |
+
# invalid construction
|
| 350 |
+
msg = "cannot construct a Timedelta"
|
| 351 |
+
with pytest.raises(ValueError, match=msg):
|
| 352 |
+
Timedelta()
|
| 353 |
+
|
| 354 |
+
msg = "unit abbreviation w/o a number"
|
| 355 |
+
with pytest.raises(ValueError, match=msg):
|
| 356 |
+
Timedelta("foo")
|
| 357 |
+
|
| 358 |
+
msg = (
|
| 359 |
+
"cannot construct a Timedelta from "
|
| 360 |
+
"the passed arguments, allowed keywords are "
|
| 361 |
+
)
|
| 362 |
+
with pytest.raises(ValueError, match=msg):
|
| 363 |
+
Timedelta(day=10)
|
| 364 |
+
|
| 365 |
+
# floats
|
| 366 |
+
expected = np.timedelta64(10, "s").astype("m8[ns]").view("i8") + np.timedelta64(
|
| 367 |
+
500, "ms"
|
| 368 |
+
).astype("m8[ns]").view("i8")
|
| 369 |
+
assert Timedelta(10.5, unit="s")._value == expected
|
| 370 |
+
|
| 371 |
+
# offset
|
| 372 |
+
assert to_timedelta(offsets.Hour(2)) == Timedelta(hours=2)
|
| 373 |
+
assert Timedelta(offsets.Hour(2)) == Timedelta(hours=2)
|
| 374 |
+
assert Timedelta(offsets.Second(2)) == Timedelta(seconds=2)
|
| 375 |
+
|
| 376 |
+
# GH#11995: unicode
|
| 377 |
+
expected = Timedelta("1h")
|
| 378 |
+
result = Timedelta("1h")
|
| 379 |
+
assert result == expected
|
| 380 |
+
assert to_timedelta(offsets.Hour(2)) == Timedelta("0 days, 02:00:00")
|
| 381 |
+
|
| 382 |
+
msg = "unit abbreviation w/o a number"
|
| 383 |
+
with pytest.raises(ValueError, match=msg):
|
| 384 |
+
Timedelta("foo bar")
|
| 385 |
+
|
| 386 |
+
|
| 387 |
+
@pytest.mark.parametrize(
|
| 388 |
+
"item",
|
| 389 |
+
list(
|
| 390 |
+
{
|
| 391 |
+
"days": "D",
|
| 392 |
+
"seconds": "s",
|
| 393 |
+
"microseconds": "us",
|
| 394 |
+
"milliseconds": "ms",
|
| 395 |
+
"minutes": "m",
|
| 396 |
+
"hours": "h",
|
| 397 |
+
"weeks": "W",
|
| 398 |
+
}.items()
|
| 399 |
+
),
|
| 400 |
+
)
|
| 401 |
+
@pytest.mark.parametrize(
|
| 402 |
+
"npdtype", [np.int64, np.int32, np.int16, np.float64, np.float32, np.float16]
|
| 403 |
+
)
|
| 404 |
+
def test_td_construction_with_np_dtypes(npdtype, item):
|
| 405 |
+
# GH#8757: test construction with np dtypes
|
| 406 |
+
pykwarg, npkwarg = item
|
| 407 |
+
expected = np.timedelta64(1, npkwarg).astype("m8[ns]").view("i8")
|
| 408 |
+
assert Timedelta(**{pykwarg: npdtype(1)})._value == expected
|
| 409 |
+
|
| 410 |
+
|
| 411 |
+
@pytest.mark.parametrize(
|
| 412 |
+
"val",
|
| 413 |
+
[
|
| 414 |
+
"1s",
|
| 415 |
+
"-1s",
|
| 416 |
+
"1us",
|
| 417 |
+
"-1us",
|
| 418 |
+
"1 day",
|
| 419 |
+
"-1 day",
|
| 420 |
+
"-23:59:59.999999",
|
| 421 |
+
"-1 days +23:59:59.999999",
|
| 422 |
+
"-1ns",
|
| 423 |
+
"1ns",
|
| 424 |
+
"-23:59:59.999999999",
|
| 425 |
+
],
|
| 426 |
+
)
|
| 427 |
+
def test_td_from_repr_roundtrip(val):
|
| 428 |
+
# round-trip both for string and value
|
| 429 |
+
td = Timedelta(val)
|
| 430 |
+
assert Timedelta(td._value) == td
|
| 431 |
+
|
| 432 |
+
assert Timedelta(str(td)) == td
|
| 433 |
+
assert Timedelta(td._repr_base(format="all")) == td
|
| 434 |
+
assert Timedelta(td._repr_base()) == td
|
| 435 |
+
|
| 436 |
+
|
| 437 |
+
def test_overflow_on_construction():
|
| 438 |
+
# GH#3374
|
| 439 |
+
value = Timedelta("1day")._value * 20169940
|
| 440 |
+
msg = "Cannot cast 1742682816000000000000 from ns to 'ns' without overflow"
|
| 441 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 442 |
+
Timedelta(value)
|
| 443 |
+
|
| 444 |
+
# xref GH#17637
|
| 445 |
+
msg = "Cannot cast 139993 from D to 'ns' without overflow"
|
| 446 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 447 |
+
Timedelta(7 * 19999, unit="D")
|
| 448 |
+
|
| 449 |
+
# used to overflow before non-ns support
|
| 450 |
+
td = Timedelta(timedelta(days=13 * 19999))
|
| 451 |
+
assert td._creso == NpyDatetimeUnit.NPY_FR_us.value
|
| 452 |
+
assert td.days == 13 * 19999
|
| 453 |
+
|
| 454 |
+
|
| 455 |
+
@pytest.mark.parametrize(
|
| 456 |
+
"val, unit",
|
| 457 |
+
[
|
| 458 |
+
(15251, "W"), # 1
|
| 459 |
+
(106752, "D"), # change from previous:
|
| 460 |
+
(2562048, "h"), # 0 hours
|
| 461 |
+
(153722868, "m"), # 13 minutes
|
| 462 |
+
(9223372037, "s"), # 44 seconds
|
| 463 |
+
],
|
| 464 |
+
)
|
| 465 |
+
def test_construction_out_of_bounds_td64ns(val, unit):
|
| 466 |
+
# TODO: parametrize over units just above/below the implementation bounds
|
| 467 |
+
# once GH#38964 is resolved
|
| 468 |
+
|
| 469 |
+
# Timedelta.max is just under 106752 days
|
| 470 |
+
td64 = np.timedelta64(val, unit)
|
| 471 |
+
assert td64.astype("m8[ns]").view("i8") < 0 # i.e. naive astype will be wrong
|
| 472 |
+
|
| 473 |
+
td = Timedelta(td64)
|
| 474 |
+
if unit != "M":
|
| 475 |
+
# with unit="M" the conversion to "s" is poorly defined
|
| 476 |
+
# (and numpy issues DeprecationWarning)
|
| 477 |
+
assert td.asm8 == td64
|
| 478 |
+
assert td.asm8.dtype == "m8[s]"
|
| 479 |
+
msg = r"Cannot cast 1067\d\d days .* to unit='ns' without overflow"
|
| 480 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 481 |
+
td.as_unit("ns")
|
| 482 |
+
|
| 483 |
+
# But just back in bounds and we are OK
|
| 484 |
+
assert Timedelta(td64 - 1) == td64 - 1
|
| 485 |
+
|
| 486 |
+
td64 *= -1
|
| 487 |
+
assert td64.astype("m8[ns]").view("i8") > 0 # i.e. naive astype will be wrong
|
| 488 |
+
|
| 489 |
+
td2 = Timedelta(td64)
|
| 490 |
+
msg = r"Cannot cast -1067\d\d days .* to unit='ns' without overflow"
|
| 491 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 492 |
+
td2.as_unit("ns")
|
| 493 |
+
|
| 494 |
+
# But just back in bounds and we are OK
|
| 495 |
+
assert Timedelta(td64 + 1) == td64 + 1
|
| 496 |
+
|
| 497 |
+
|
| 498 |
+
@pytest.mark.parametrize(
|
| 499 |
+
"val, unit",
|
| 500 |
+
[
|
| 501 |
+
(15251 * 10**9, "W"),
|
| 502 |
+
(106752 * 10**9, "D"),
|
| 503 |
+
(2562048 * 10**9, "h"),
|
| 504 |
+
(153722868 * 10**9, "m"),
|
| 505 |
+
],
|
| 506 |
+
)
|
| 507 |
+
def test_construction_out_of_bounds_td64s(val, unit):
|
| 508 |
+
td64 = np.timedelta64(val, unit)
|
| 509 |
+
with pytest.raises(OutOfBoundsTimedelta, match=str(td64)):
|
| 510 |
+
Timedelta(td64)
|
| 511 |
+
|
| 512 |
+
# But just back in bounds and we are OK
|
| 513 |
+
assert Timedelta(td64 - 10**9) == td64 - 10**9
|
| 514 |
+
|
| 515 |
+
|
| 516 |
+
@pytest.mark.parametrize(
|
| 517 |
+
"fmt,exp",
|
| 518 |
+
[
|
| 519 |
+
(
|
| 520 |
+
"P6DT0H50M3.010010012S",
|
| 521 |
+
Timedelta(
|
| 522 |
+
days=6,
|
| 523 |
+
minutes=50,
|
| 524 |
+
seconds=3,
|
| 525 |
+
milliseconds=10,
|
| 526 |
+
microseconds=10,
|
| 527 |
+
nanoseconds=12,
|
| 528 |
+
),
|
| 529 |
+
),
|
| 530 |
+
(
|
| 531 |
+
"P-6DT0H50M3.010010012S",
|
| 532 |
+
Timedelta(
|
| 533 |
+
days=-6,
|
| 534 |
+
minutes=50,
|
| 535 |
+
seconds=3,
|
| 536 |
+
milliseconds=10,
|
| 537 |
+
microseconds=10,
|
| 538 |
+
nanoseconds=12,
|
| 539 |
+
),
|
| 540 |
+
),
|
| 541 |
+
("P4DT12H30M5S", Timedelta(days=4, hours=12, minutes=30, seconds=5)),
|
| 542 |
+
("P0DT0H0M0.000000123S", Timedelta(nanoseconds=123)),
|
| 543 |
+
("P0DT0H0M0.00001S", Timedelta(microseconds=10)),
|
| 544 |
+
("P0DT0H0M0.001S", Timedelta(milliseconds=1)),
|
| 545 |
+
("P0DT0H1M0S", Timedelta(minutes=1)),
|
| 546 |
+
("P1DT25H61M61S", Timedelta(days=1, hours=25, minutes=61, seconds=61)),
|
| 547 |
+
("PT1S", Timedelta(seconds=1)),
|
| 548 |
+
("PT0S", Timedelta(seconds=0)),
|
| 549 |
+
("P1WT0S", Timedelta(days=7, seconds=0)),
|
| 550 |
+
("P1D", Timedelta(days=1)),
|
| 551 |
+
("P1DT1H", Timedelta(days=1, hours=1)),
|
| 552 |
+
("P1W", Timedelta(days=7)),
|
| 553 |
+
("PT300S", Timedelta(seconds=300)),
|
| 554 |
+
("P1DT0H0M00000000000S", Timedelta(days=1)),
|
| 555 |
+
("PT-6H3M", Timedelta(hours=-6, minutes=3)),
|
| 556 |
+
("-PT6H3M", Timedelta(hours=-6, minutes=-3)),
|
| 557 |
+
("-PT-6H+3M", Timedelta(hours=6, minutes=-3)),
|
| 558 |
+
],
|
| 559 |
+
)
|
| 560 |
+
def test_iso_constructor(fmt, exp):
|
| 561 |
+
assert Timedelta(fmt) == exp
|
| 562 |
+
|
| 563 |
+
|
| 564 |
+
@pytest.mark.parametrize(
|
| 565 |
+
"fmt",
|
| 566 |
+
[
|
| 567 |
+
"PPPPPPPPPPPP",
|
| 568 |
+
"PDTHMS",
|
| 569 |
+
"P0DT999H999M999S",
|
| 570 |
+
"P1DT0H0M0.0000000000000S",
|
| 571 |
+
"P1DT0H0M0.S",
|
| 572 |
+
"P",
|
| 573 |
+
"-P",
|
| 574 |
+
],
|
| 575 |
+
)
|
| 576 |
+
def test_iso_constructor_raises(fmt):
|
| 577 |
+
msg = f"Invalid ISO 8601 Duration format - {fmt}"
|
| 578 |
+
with pytest.raises(ValueError, match=msg):
|
| 579 |
+
Timedelta(fmt)
|
| 580 |
+
|
| 581 |
+
|
| 582 |
+
@pytest.mark.parametrize(
|
| 583 |
+
"constructed_td, conversion",
|
| 584 |
+
[
|
| 585 |
+
(Timedelta(nanoseconds=100), "100ns"),
|
| 586 |
+
(
|
| 587 |
+
Timedelta(
|
| 588 |
+
days=1,
|
| 589 |
+
hours=1,
|
| 590 |
+
minutes=1,
|
| 591 |
+
weeks=1,
|
| 592 |
+
seconds=1,
|
| 593 |
+
milliseconds=1,
|
| 594 |
+
microseconds=1,
|
| 595 |
+
nanoseconds=1,
|
| 596 |
+
),
|
| 597 |
+
694861001001001,
|
| 598 |
+
),
|
| 599 |
+
(Timedelta(microseconds=1) + Timedelta(nanoseconds=1), "1us1ns"),
|
| 600 |
+
(Timedelta(microseconds=1) - Timedelta(nanoseconds=1), "999ns"),
|
| 601 |
+
(Timedelta(microseconds=1) + 5 * Timedelta(nanoseconds=-2), "990ns"),
|
| 602 |
+
],
|
| 603 |
+
)
|
| 604 |
+
def test_td_constructor_on_nanoseconds(constructed_td, conversion):
|
| 605 |
+
# GH#9273
|
| 606 |
+
assert constructed_td == Timedelta(conversion)
|
| 607 |
+
|
| 608 |
+
|
| 609 |
+
def test_td_constructor_value_error():
|
| 610 |
+
msg = "Invalid type <class 'str'>. Must be int or float."
|
| 611 |
+
with pytest.raises(TypeError, match=msg):
|
| 612 |
+
Timedelta(nanoseconds="abc")
|
| 613 |
+
|
| 614 |
+
|
| 615 |
+
def test_timedelta_constructor_identity():
|
| 616 |
+
# Test for #30543
|
| 617 |
+
expected = Timedelta(np.timedelta64(1, "s"))
|
| 618 |
+
result = Timedelta(expected)
|
| 619 |
+
assert result is expected
|
| 620 |
+
|
| 621 |
+
|
| 622 |
+
def test_timedelta_pass_td_and_kwargs_raises():
|
| 623 |
+
# don't silently ignore the kwargs GH#48898
|
| 624 |
+
td = Timedelta(days=1)
|
| 625 |
+
msg = (
|
| 626 |
+
"Cannot pass both a Timedelta input and timedelta keyword arguments, "
|
| 627 |
+
r"got \['days'\]"
|
| 628 |
+
)
|
| 629 |
+
with pytest.raises(ValueError, match=msg):
|
| 630 |
+
Timedelta(td, days=2)
|
| 631 |
+
|
| 632 |
+
|
| 633 |
+
@pytest.mark.parametrize(
|
| 634 |
+
"constructor, value, unit, expectation",
|
| 635 |
+
[
|
| 636 |
+
(Timedelta, "10s", "ms", (ValueError, "unit must not be specified")),
|
| 637 |
+
(to_timedelta, "10s", "ms", (ValueError, "unit must not be specified")),
|
| 638 |
+
(to_timedelta, ["1", 2, 3], "s", (ValueError, "unit must not be specified")),
|
| 639 |
+
],
|
| 640 |
+
)
|
| 641 |
+
def test_string_with_unit(constructor, value, unit, expectation):
|
| 642 |
+
exp, match = expectation
|
| 643 |
+
with pytest.raises(exp, match=match):
|
| 644 |
+
_ = constructor(value, unit=unit)
|
| 645 |
+
|
| 646 |
+
|
| 647 |
+
@pytest.mark.parametrize(
|
| 648 |
+
"value",
|
| 649 |
+
[
|
| 650 |
+
"".join(elements)
|
| 651 |
+
for repetition in (1, 2)
|
| 652 |
+
for elements in product("+-, ", repeat=repetition)
|
| 653 |
+
],
|
| 654 |
+
)
|
| 655 |
+
def test_string_without_numbers(value):
|
| 656 |
+
# GH39710 Timedelta input string with only symbols and no digits raises an error
|
| 657 |
+
msg = (
|
| 658 |
+
"symbols w/o a number"
|
| 659 |
+
if value != "--"
|
| 660 |
+
else "only leading negative signs are allowed"
|
| 661 |
+
)
|
| 662 |
+
with pytest.raises(ValueError, match=msg):
|
| 663 |
+
Timedelta(value)
|
| 664 |
+
|
| 665 |
+
|
| 666 |
+
def test_timedelta_new_npnat():
|
| 667 |
+
# GH#48898
|
| 668 |
+
nat = np.timedelta64("NaT", "h")
|
| 669 |
+
assert Timedelta(nat) is NaT
|
| 670 |
+
|
| 671 |
+
|
| 672 |
+
def test_subclass_respected():
|
| 673 |
+
# GH#49579
|
| 674 |
+
class MyCustomTimedelta(Timedelta):
|
| 675 |
+
pass
|
| 676 |
+
|
| 677 |
+
td = MyCustomTimedelta("1 minute")
|
| 678 |
+
assert isinstance(td, MyCustomTimedelta)
|
| 679 |
+
|
| 680 |
+
|
| 681 |
+
def test_non_nano_value():
|
| 682 |
+
# https://github.com/pandas-dev/pandas/issues/49076
|
| 683 |
+
result = Timedelta(10, unit="D").as_unit("s").value
|
| 684 |
+
# `.value` shows nanoseconds, even though unit is 's'
|
| 685 |
+
assert result == 864000000000000
|
| 686 |
+
|
| 687 |
+
# out-of-nanoseconds-bounds `.value` raises informative message
|
| 688 |
+
msg = (
|
| 689 |
+
r"Cannot convert Timedelta to nanoseconds without overflow. "
|
| 690 |
+
r"Use `.asm8.view\('i8'\)` to cast represent Timedelta in its "
|
| 691 |
+
r"own unit \(here, s\).$"
|
| 692 |
+
)
|
| 693 |
+
td = Timedelta(1_000, "D").as_unit("s") * 1_000
|
| 694 |
+
with pytest.raises(OverflowError, match=msg):
|
| 695 |
+
td.value
|
| 696 |
+
# check that the suggested workaround actually works
|
| 697 |
+
result = td.asm8.view("i8")
|
| 698 |
+
assert result == 86400000000
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/test_formats.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
|
| 3 |
+
from pandas import Timedelta
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
@pytest.mark.parametrize(
|
| 7 |
+
"td, expected_repr",
|
| 8 |
+
[
|
| 9 |
+
(Timedelta(10, unit="d"), "Timedelta('10 days 00:00:00')"),
|
| 10 |
+
(Timedelta(10, unit="s"), "Timedelta('0 days 00:00:10')"),
|
| 11 |
+
(Timedelta(10, unit="ms"), "Timedelta('0 days 00:00:00.010000')"),
|
| 12 |
+
(Timedelta(-10, unit="ms"), "Timedelta('-1 days +23:59:59.990000')"),
|
| 13 |
+
],
|
| 14 |
+
)
|
| 15 |
+
def test_repr(td, expected_repr):
|
| 16 |
+
assert repr(td) == expected_repr
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
@pytest.mark.parametrize(
|
| 20 |
+
"td, expected_iso",
|
| 21 |
+
[
|
| 22 |
+
(
|
| 23 |
+
Timedelta(
|
| 24 |
+
days=6,
|
| 25 |
+
minutes=50,
|
| 26 |
+
seconds=3,
|
| 27 |
+
milliseconds=10,
|
| 28 |
+
microseconds=10,
|
| 29 |
+
nanoseconds=12,
|
| 30 |
+
),
|
| 31 |
+
"P6DT0H50M3.010010012S",
|
| 32 |
+
),
|
| 33 |
+
(Timedelta(days=4, hours=12, minutes=30, seconds=5), "P4DT12H30M5S"),
|
| 34 |
+
(Timedelta(nanoseconds=123), "P0DT0H0M0.000000123S"),
|
| 35 |
+
# trim nano
|
| 36 |
+
(Timedelta(microseconds=10), "P0DT0H0M0.00001S"),
|
| 37 |
+
# trim micro
|
| 38 |
+
(Timedelta(milliseconds=1), "P0DT0H0M0.001S"),
|
| 39 |
+
# don't strip every 0
|
| 40 |
+
(Timedelta(minutes=1), "P0DT0H1M0S"),
|
| 41 |
+
],
|
| 42 |
+
)
|
| 43 |
+
def test_isoformat(td, expected_iso):
|
| 44 |
+
assert td.isoformat() == expected_iso
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class TestReprBase:
|
| 48 |
+
def test_none(self):
|
| 49 |
+
delta_1d = Timedelta(1, unit="D")
|
| 50 |
+
delta_0d = Timedelta(0, unit="D")
|
| 51 |
+
delta_1s = Timedelta(1, unit="s")
|
| 52 |
+
delta_500ms = Timedelta(500, unit="ms")
|
| 53 |
+
|
| 54 |
+
drepr = lambda x: x._repr_base()
|
| 55 |
+
assert drepr(delta_1d) == "1 days"
|
| 56 |
+
assert drepr(-delta_1d) == "-1 days"
|
| 57 |
+
assert drepr(delta_0d) == "0 days"
|
| 58 |
+
assert drepr(delta_1s) == "0 days 00:00:01"
|
| 59 |
+
assert drepr(delta_500ms) == "0 days 00:00:00.500000"
|
| 60 |
+
assert drepr(delta_1d + delta_1s) == "1 days 00:00:01"
|
| 61 |
+
assert drepr(-delta_1d + delta_1s) == "-1 days +00:00:01"
|
| 62 |
+
assert drepr(delta_1d + delta_500ms) == "1 days 00:00:00.500000"
|
| 63 |
+
assert drepr(-delta_1d + delta_500ms) == "-1 days +00:00:00.500000"
|
| 64 |
+
|
| 65 |
+
def test_sub_day(self):
|
| 66 |
+
delta_1d = Timedelta(1, unit="D")
|
| 67 |
+
delta_0d = Timedelta(0, unit="D")
|
| 68 |
+
delta_1s = Timedelta(1, unit="s")
|
| 69 |
+
delta_500ms = Timedelta(500, unit="ms")
|
| 70 |
+
|
| 71 |
+
drepr = lambda x: x._repr_base(format="sub_day")
|
| 72 |
+
assert drepr(delta_1d) == "1 days"
|
| 73 |
+
assert drepr(-delta_1d) == "-1 days"
|
| 74 |
+
assert drepr(delta_0d) == "00:00:00"
|
| 75 |
+
assert drepr(delta_1s) == "00:00:01"
|
| 76 |
+
assert drepr(delta_500ms) == "00:00:00.500000"
|
| 77 |
+
assert drepr(delta_1d + delta_1s) == "1 days 00:00:01"
|
| 78 |
+
assert drepr(-delta_1d + delta_1s) == "-1 days +00:00:01"
|
| 79 |
+
assert drepr(delta_1d + delta_500ms) == "1 days 00:00:00.500000"
|
| 80 |
+
assert drepr(-delta_1d + delta_500ms) == "-1 days +00:00:00.500000"
|
| 81 |
+
|
| 82 |
+
def test_long(self):
|
| 83 |
+
delta_1d = Timedelta(1, unit="D")
|
| 84 |
+
delta_0d = Timedelta(0, unit="D")
|
| 85 |
+
delta_1s = Timedelta(1, unit="s")
|
| 86 |
+
delta_500ms = Timedelta(500, unit="ms")
|
| 87 |
+
|
| 88 |
+
drepr = lambda x: x._repr_base(format="long")
|
| 89 |
+
assert drepr(delta_1d) == "1 days 00:00:00"
|
| 90 |
+
assert drepr(-delta_1d) == "-1 days +00:00:00"
|
| 91 |
+
assert drepr(delta_0d) == "0 days 00:00:00"
|
| 92 |
+
assert drepr(delta_1s) == "0 days 00:00:01"
|
| 93 |
+
assert drepr(delta_500ms) == "0 days 00:00:00.500000"
|
| 94 |
+
assert drepr(delta_1d + delta_1s) == "1 days 00:00:01"
|
| 95 |
+
assert drepr(-delta_1d + delta_1s) == "-1 days +00:00:01"
|
| 96 |
+
assert drepr(delta_1d + delta_500ms) == "1 days 00:00:00.500000"
|
| 97 |
+
assert drepr(-delta_1d + delta_500ms) == "-1 days +00:00:00.500000"
|
| 98 |
+
|
| 99 |
+
def test_all(self):
|
| 100 |
+
delta_1d = Timedelta(1, unit="D")
|
| 101 |
+
delta_0d = Timedelta(0, unit="D")
|
| 102 |
+
delta_1ns = Timedelta(1, unit="ns")
|
| 103 |
+
|
| 104 |
+
drepr = lambda x: x._repr_base(format="all")
|
| 105 |
+
assert drepr(delta_1d) == "1 days 00:00:00.000000000"
|
| 106 |
+
assert drepr(-delta_1d) == "-1 days +00:00:00.000000000"
|
| 107 |
+
assert drepr(delta_0d) == "0 days 00:00:00.000000000"
|
| 108 |
+
assert drepr(delta_1ns) == "0 days 00:00:00.000000001"
|
| 109 |
+
assert drepr(-delta_1d + delta_1ns) == "-1 days +00:00:00.000000001"
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timedelta/test_timedelta.py
ADDED
|
@@ -0,0 +1,666 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" test the scalar Timedelta """
|
| 2 |
+
from datetime import timedelta
|
| 3 |
+
import sys
|
| 4 |
+
|
| 5 |
+
from hypothesis import (
|
| 6 |
+
given,
|
| 7 |
+
strategies as st,
|
| 8 |
+
)
|
| 9 |
+
import numpy as np
|
| 10 |
+
import pytest
|
| 11 |
+
|
| 12 |
+
from pandas._libs import lib
|
| 13 |
+
from pandas._libs.tslibs import (
|
| 14 |
+
NaT,
|
| 15 |
+
iNaT,
|
| 16 |
+
)
|
| 17 |
+
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
| 18 |
+
from pandas.errors import OutOfBoundsTimedelta
|
| 19 |
+
|
| 20 |
+
from pandas import (
|
| 21 |
+
Timedelta,
|
| 22 |
+
to_timedelta,
|
| 23 |
+
)
|
| 24 |
+
import pandas._testing as tm
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class TestNonNano:
|
| 28 |
+
@pytest.fixture(params=["s", "ms", "us"])
|
| 29 |
+
def unit_str(self, request):
|
| 30 |
+
return request.param
|
| 31 |
+
|
| 32 |
+
@pytest.fixture
|
| 33 |
+
def unit(self, unit_str):
|
| 34 |
+
# 7, 8, 9 correspond to second, millisecond, and microsecond, respectively
|
| 35 |
+
attr = f"NPY_FR_{unit_str}"
|
| 36 |
+
return getattr(NpyDatetimeUnit, attr).value
|
| 37 |
+
|
| 38 |
+
@pytest.fixture
|
| 39 |
+
def val(self, unit):
|
| 40 |
+
# microsecond that would be just out of bounds for nano
|
| 41 |
+
us = 9223372800000000
|
| 42 |
+
if unit == NpyDatetimeUnit.NPY_FR_us.value:
|
| 43 |
+
value = us
|
| 44 |
+
elif unit == NpyDatetimeUnit.NPY_FR_ms.value:
|
| 45 |
+
value = us // 1000
|
| 46 |
+
else:
|
| 47 |
+
value = us // 1_000_000
|
| 48 |
+
return value
|
| 49 |
+
|
| 50 |
+
@pytest.fixture
|
| 51 |
+
def td(self, unit, val):
|
| 52 |
+
return Timedelta._from_value_and_reso(val, unit)
|
| 53 |
+
|
| 54 |
+
def test_from_value_and_reso(self, unit, val):
|
| 55 |
+
# Just checking that the fixture is giving us what we asked for
|
| 56 |
+
td = Timedelta._from_value_and_reso(val, unit)
|
| 57 |
+
assert td._value == val
|
| 58 |
+
assert td._creso == unit
|
| 59 |
+
assert td.days == 106752
|
| 60 |
+
|
| 61 |
+
def test_unary_non_nano(self, td, unit):
|
| 62 |
+
assert abs(td)._creso == unit
|
| 63 |
+
assert (-td)._creso == unit
|
| 64 |
+
assert (+td)._creso == unit
|
| 65 |
+
|
| 66 |
+
def test_sub_preserves_reso(self, td, unit):
|
| 67 |
+
res = td - td
|
| 68 |
+
expected = Timedelta._from_value_and_reso(0, unit)
|
| 69 |
+
assert res == expected
|
| 70 |
+
assert res._creso == unit
|
| 71 |
+
|
| 72 |
+
def test_mul_preserves_reso(self, td, unit):
|
| 73 |
+
# The td fixture should always be far from the implementation
|
| 74 |
+
# bound, so doubling does not risk overflow.
|
| 75 |
+
res = td * 2
|
| 76 |
+
assert res._value == td._value * 2
|
| 77 |
+
assert res._creso == unit
|
| 78 |
+
|
| 79 |
+
def test_cmp_cross_reso(self, td):
|
| 80 |
+
# numpy gets this wrong because of silent overflow
|
| 81 |
+
other = Timedelta(days=106751, unit="ns")
|
| 82 |
+
assert other < td
|
| 83 |
+
assert td > other
|
| 84 |
+
assert not other == td
|
| 85 |
+
assert td != other
|
| 86 |
+
|
| 87 |
+
def test_to_pytimedelta(self, td):
|
| 88 |
+
res = td.to_pytimedelta()
|
| 89 |
+
expected = timedelta(days=106752)
|
| 90 |
+
assert type(res) is timedelta
|
| 91 |
+
assert res == expected
|
| 92 |
+
|
| 93 |
+
def test_to_timedelta64(self, td, unit):
|
| 94 |
+
for res in [td.to_timedelta64(), td.to_numpy(), td.asm8]:
|
| 95 |
+
assert isinstance(res, np.timedelta64)
|
| 96 |
+
assert res.view("i8") == td._value
|
| 97 |
+
if unit == NpyDatetimeUnit.NPY_FR_s.value:
|
| 98 |
+
assert res.dtype == "m8[s]"
|
| 99 |
+
elif unit == NpyDatetimeUnit.NPY_FR_ms.value:
|
| 100 |
+
assert res.dtype == "m8[ms]"
|
| 101 |
+
elif unit == NpyDatetimeUnit.NPY_FR_us.value:
|
| 102 |
+
assert res.dtype == "m8[us]"
|
| 103 |
+
|
| 104 |
+
def test_truediv_timedeltalike(self, td):
|
| 105 |
+
assert td / td == 1
|
| 106 |
+
assert (2.5 * td) / td == 2.5
|
| 107 |
+
|
| 108 |
+
other = Timedelta(td._value)
|
| 109 |
+
msg = "Cannot cast 106752 days 00:00:00 to unit='ns' without overflow."
|
| 110 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 111 |
+
td / other
|
| 112 |
+
|
| 113 |
+
# Timedelta(other.to_pytimedelta()) has microsecond resolution,
|
| 114 |
+
# so the division doesn't require casting all the way to nanos,
|
| 115 |
+
# so succeeds
|
| 116 |
+
res = other.to_pytimedelta() / td
|
| 117 |
+
expected = other.to_pytimedelta() / td.to_pytimedelta()
|
| 118 |
+
assert res == expected
|
| 119 |
+
|
| 120 |
+
# if there's no overflow, we cast to the higher reso
|
| 121 |
+
left = Timedelta._from_value_and_reso(50, NpyDatetimeUnit.NPY_FR_us.value)
|
| 122 |
+
right = Timedelta._from_value_and_reso(50, NpyDatetimeUnit.NPY_FR_ms.value)
|
| 123 |
+
result = left / right
|
| 124 |
+
assert result == 0.001
|
| 125 |
+
|
| 126 |
+
result = right / left
|
| 127 |
+
assert result == 1000
|
| 128 |
+
|
| 129 |
+
def test_truediv_numeric(self, td):
|
| 130 |
+
assert td / np.nan is NaT
|
| 131 |
+
|
| 132 |
+
res = td / 2
|
| 133 |
+
assert res._value == td._value / 2
|
| 134 |
+
assert res._creso == td._creso
|
| 135 |
+
|
| 136 |
+
res = td / 2.0
|
| 137 |
+
assert res._value == td._value / 2
|
| 138 |
+
assert res._creso == td._creso
|
| 139 |
+
|
| 140 |
+
def test_floordiv_timedeltalike(self, td):
|
| 141 |
+
assert td // td == 1
|
| 142 |
+
assert (2.5 * td) // td == 2
|
| 143 |
+
|
| 144 |
+
other = Timedelta(td._value)
|
| 145 |
+
msg = "Cannot cast 106752 days 00:00:00 to unit='ns' without overflow"
|
| 146 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 147 |
+
td // other
|
| 148 |
+
|
| 149 |
+
# Timedelta(other.to_pytimedelta()) has microsecond resolution,
|
| 150 |
+
# so the floordiv doesn't require casting all the way to nanos,
|
| 151 |
+
# so succeeds
|
| 152 |
+
res = other.to_pytimedelta() // td
|
| 153 |
+
assert res == 0
|
| 154 |
+
|
| 155 |
+
# if there's no overflow, we cast to the higher reso
|
| 156 |
+
left = Timedelta._from_value_and_reso(50050, NpyDatetimeUnit.NPY_FR_us.value)
|
| 157 |
+
right = Timedelta._from_value_and_reso(50, NpyDatetimeUnit.NPY_FR_ms.value)
|
| 158 |
+
result = left // right
|
| 159 |
+
assert result == 1
|
| 160 |
+
result = right // left
|
| 161 |
+
assert result == 0
|
| 162 |
+
|
| 163 |
+
def test_floordiv_numeric(self, td):
|
| 164 |
+
assert td // np.nan is NaT
|
| 165 |
+
|
| 166 |
+
res = td // 2
|
| 167 |
+
assert res._value == td._value // 2
|
| 168 |
+
assert res._creso == td._creso
|
| 169 |
+
|
| 170 |
+
res = td // 2.0
|
| 171 |
+
assert res._value == td._value // 2
|
| 172 |
+
assert res._creso == td._creso
|
| 173 |
+
|
| 174 |
+
assert td // np.array(np.nan) is NaT
|
| 175 |
+
|
| 176 |
+
res = td // np.array(2)
|
| 177 |
+
assert res._value == td._value // 2
|
| 178 |
+
assert res._creso == td._creso
|
| 179 |
+
|
| 180 |
+
res = td // np.array(2.0)
|
| 181 |
+
assert res._value == td._value // 2
|
| 182 |
+
assert res._creso == td._creso
|
| 183 |
+
|
| 184 |
+
def test_addsub_mismatched_reso(self, td):
|
| 185 |
+
# need to cast to since td is out of bounds for ns, so
|
| 186 |
+
# so we would raise OverflowError without casting
|
| 187 |
+
other = Timedelta(days=1).as_unit("us")
|
| 188 |
+
|
| 189 |
+
# td is out of bounds for ns
|
| 190 |
+
result = td + other
|
| 191 |
+
assert result._creso == other._creso
|
| 192 |
+
assert result.days == td.days + 1
|
| 193 |
+
|
| 194 |
+
result = other + td
|
| 195 |
+
assert result._creso == other._creso
|
| 196 |
+
assert result.days == td.days + 1
|
| 197 |
+
|
| 198 |
+
result = td - other
|
| 199 |
+
assert result._creso == other._creso
|
| 200 |
+
assert result.days == td.days - 1
|
| 201 |
+
|
| 202 |
+
result = other - td
|
| 203 |
+
assert result._creso == other._creso
|
| 204 |
+
assert result.days == 1 - td.days
|
| 205 |
+
|
| 206 |
+
other2 = Timedelta(500)
|
| 207 |
+
msg = "Cannot cast 106752 days 00:00:00 to unit='ns' without overflow"
|
| 208 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 209 |
+
td + other2
|
| 210 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 211 |
+
other2 + td
|
| 212 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 213 |
+
td - other2
|
| 214 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 215 |
+
other2 - td
|
| 216 |
+
|
| 217 |
+
def test_min(self, td):
|
| 218 |
+
assert td.min <= td
|
| 219 |
+
assert td.min._creso == td._creso
|
| 220 |
+
assert td.min._value == NaT._value + 1
|
| 221 |
+
|
| 222 |
+
def test_max(self, td):
|
| 223 |
+
assert td.max >= td
|
| 224 |
+
assert td.max._creso == td._creso
|
| 225 |
+
assert td.max._value == np.iinfo(np.int64).max
|
| 226 |
+
|
| 227 |
+
def test_resolution(self, td):
|
| 228 |
+
expected = Timedelta._from_value_and_reso(1, td._creso)
|
| 229 |
+
result = td.resolution
|
| 230 |
+
assert result == expected
|
| 231 |
+
assert result._creso == expected._creso
|
| 232 |
+
|
| 233 |
+
def test_hash(self) -> None:
|
| 234 |
+
# GH#54037
|
| 235 |
+
second_resolution_max = Timedelta(0).as_unit("s").max
|
| 236 |
+
|
| 237 |
+
assert hash(second_resolution_max)
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
def test_timedelta_class_min_max_resolution():
|
| 241 |
+
# when accessed on the class (as opposed to an instance), we default
|
| 242 |
+
# to nanoseconds
|
| 243 |
+
assert Timedelta.min == Timedelta(NaT._value + 1)
|
| 244 |
+
assert Timedelta.min._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
| 245 |
+
|
| 246 |
+
assert Timedelta.max == Timedelta(np.iinfo(np.int64).max)
|
| 247 |
+
assert Timedelta.max._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
| 248 |
+
|
| 249 |
+
assert Timedelta.resolution == Timedelta(1)
|
| 250 |
+
assert Timedelta.resolution._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
class TestTimedeltaUnaryOps:
|
| 254 |
+
def test_invert(self):
|
| 255 |
+
td = Timedelta(10, unit="d")
|
| 256 |
+
|
| 257 |
+
msg = "bad operand type for unary ~"
|
| 258 |
+
with pytest.raises(TypeError, match=msg):
|
| 259 |
+
~td
|
| 260 |
+
|
| 261 |
+
# check this matches pytimedelta and timedelta64
|
| 262 |
+
with pytest.raises(TypeError, match=msg):
|
| 263 |
+
~(td.to_pytimedelta())
|
| 264 |
+
|
| 265 |
+
umsg = "ufunc 'invert' not supported for the input types"
|
| 266 |
+
with pytest.raises(TypeError, match=umsg):
|
| 267 |
+
~(td.to_timedelta64())
|
| 268 |
+
|
| 269 |
+
def test_unary_ops(self):
|
| 270 |
+
td = Timedelta(10, unit="d")
|
| 271 |
+
|
| 272 |
+
# __neg__, __pos__
|
| 273 |
+
assert -td == Timedelta(-10, unit="d")
|
| 274 |
+
assert -td == Timedelta("-10d")
|
| 275 |
+
assert +td == Timedelta(10, unit="d")
|
| 276 |
+
|
| 277 |
+
# __abs__, __abs__(__neg__)
|
| 278 |
+
assert abs(td) == td
|
| 279 |
+
assert abs(-td) == td
|
| 280 |
+
assert abs(-td) == Timedelta("10d")
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
class TestTimedeltas:
|
| 284 |
+
@pytest.mark.parametrize(
|
| 285 |
+
"unit, value, expected",
|
| 286 |
+
[
|
| 287 |
+
("us", 9.999, 9999),
|
| 288 |
+
("ms", 9.999999, 9999999),
|
| 289 |
+
("s", 9.999999999, 9999999999),
|
| 290 |
+
],
|
| 291 |
+
)
|
| 292 |
+
def test_rounding_on_int_unit_construction(self, unit, value, expected):
|
| 293 |
+
# GH 12690
|
| 294 |
+
result = Timedelta(value, unit=unit)
|
| 295 |
+
assert result._value == expected
|
| 296 |
+
result = Timedelta(str(value) + unit)
|
| 297 |
+
assert result._value == expected
|
| 298 |
+
|
| 299 |
+
def test_total_seconds_scalar(self):
|
| 300 |
+
# see gh-10939
|
| 301 |
+
rng = Timedelta("1 days, 10:11:12.100123456")
|
| 302 |
+
expt = 1 * 86400 + 10 * 3600 + 11 * 60 + 12 + 100123456.0 / 1e9
|
| 303 |
+
tm.assert_almost_equal(rng.total_seconds(), expt)
|
| 304 |
+
|
| 305 |
+
rng = Timedelta(np.nan)
|
| 306 |
+
assert np.isnan(rng.total_seconds())
|
| 307 |
+
|
| 308 |
+
def test_conversion(self):
|
| 309 |
+
for td in [Timedelta(10, unit="d"), Timedelta("1 days, 10:11:12.012345")]:
|
| 310 |
+
pydt = td.to_pytimedelta()
|
| 311 |
+
assert td == Timedelta(pydt)
|
| 312 |
+
assert td == pydt
|
| 313 |
+
assert isinstance(pydt, timedelta) and not isinstance(pydt, Timedelta)
|
| 314 |
+
|
| 315 |
+
assert td == np.timedelta64(td._value, "ns")
|
| 316 |
+
td64 = td.to_timedelta64()
|
| 317 |
+
|
| 318 |
+
assert td64 == np.timedelta64(td._value, "ns")
|
| 319 |
+
assert td == td64
|
| 320 |
+
|
| 321 |
+
assert isinstance(td64, np.timedelta64)
|
| 322 |
+
|
| 323 |
+
# this is NOT equal and cannot be roundtripped (because of the nanos)
|
| 324 |
+
td = Timedelta("1 days, 10:11:12.012345678")
|
| 325 |
+
assert td != td.to_pytimedelta()
|
| 326 |
+
|
| 327 |
+
def test_fields(self):
|
| 328 |
+
def check(value):
|
| 329 |
+
# that we are int
|
| 330 |
+
assert isinstance(value, int)
|
| 331 |
+
|
| 332 |
+
# compat to datetime.timedelta
|
| 333 |
+
rng = to_timedelta("1 days, 10:11:12")
|
| 334 |
+
assert rng.days == 1
|
| 335 |
+
assert rng.seconds == 10 * 3600 + 11 * 60 + 12
|
| 336 |
+
assert rng.microseconds == 0
|
| 337 |
+
assert rng.nanoseconds == 0
|
| 338 |
+
|
| 339 |
+
msg = "'Timedelta' object has no attribute '{}'"
|
| 340 |
+
with pytest.raises(AttributeError, match=msg.format("hours")):
|
| 341 |
+
rng.hours
|
| 342 |
+
with pytest.raises(AttributeError, match=msg.format("minutes")):
|
| 343 |
+
rng.minutes
|
| 344 |
+
with pytest.raises(AttributeError, match=msg.format("milliseconds")):
|
| 345 |
+
rng.milliseconds
|
| 346 |
+
|
| 347 |
+
# GH 10050
|
| 348 |
+
check(rng.days)
|
| 349 |
+
check(rng.seconds)
|
| 350 |
+
check(rng.microseconds)
|
| 351 |
+
check(rng.nanoseconds)
|
| 352 |
+
|
| 353 |
+
td = Timedelta("-1 days, 10:11:12")
|
| 354 |
+
assert abs(td) == Timedelta("13:48:48")
|
| 355 |
+
assert str(td) == "-1 days +10:11:12"
|
| 356 |
+
assert -td == Timedelta("0 days 13:48:48")
|
| 357 |
+
assert -Timedelta("-1 days, 10:11:12")._value == 49728000000000
|
| 358 |
+
assert Timedelta("-1 days, 10:11:12")._value == -49728000000000
|
| 359 |
+
|
| 360 |
+
rng = to_timedelta("-1 days, 10:11:12.100123456")
|
| 361 |
+
assert rng.days == -1
|
| 362 |
+
assert rng.seconds == 10 * 3600 + 11 * 60 + 12
|
| 363 |
+
assert rng.microseconds == 100 * 1000 + 123
|
| 364 |
+
assert rng.nanoseconds == 456
|
| 365 |
+
msg = "'Timedelta' object has no attribute '{}'"
|
| 366 |
+
with pytest.raises(AttributeError, match=msg.format("hours")):
|
| 367 |
+
rng.hours
|
| 368 |
+
with pytest.raises(AttributeError, match=msg.format("minutes")):
|
| 369 |
+
rng.minutes
|
| 370 |
+
with pytest.raises(AttributeError, match=msg.format("milliseconds")):
|
| 371 |
+
rng.milliseconds
|
| 372 |
+
|
| 373 |
+
# components
|
| 374 |
+
tup = to_timedelta(-1, "us").components
|
| 375 |
+
assert tup.days == -1
|
| 376 |
+
assert tup.hours == 23
|
| 377 |
+
assert tup.minutes == 59
|
| 378 |
+
assert tup.seconds == 59
|
| 379 |
+
assert tup.milliseconds == 999
|
| 380 |
+
assert tup.microseconds == 999
|
| 381 |
+
assert tup.nanoseconds == 0
|
| 382 |
+
|
| 383 |
+
# GH 10050
|
| 384 |
+
check(tup.days)
|
| 385 |
+
check(tup.hours)
|
| 386 |
+
check(tup.minutes)
|
| 387 |
+
check(tup.seconds)
|
| 388 |
+
check(tup.milliseconds)
|
| 389 |
+
check(tup.microseconds)
|
| 390 |
+
check(tup.nanoseconds)
|
| 391 |
+
|
| 392 |
+
tup = Timedelta("-1 days 1 us").components
|
| 393 |
+
assert tup.days == -2
|
| 394 |
+
assert tup.hours == 23
|
| 395 |
+
assert tup.minutes == 59
|
| 396 |
+
assert tup.seconds == 59
|
| 397 |
+
assert tup.milliseconds == 999
|
| 398 |
+
assert tup.microseconds == 999
|
| 399 |
+
assert tup.nanoseconds == 0
|
| 400 |
+
|
| 401 |
+
# TODO: this is a test of to_timedelta string parsing
|
| 402 |
+
def test_iso_conversion(self):
|
| 403 |
+
# GH #21877
|
| 404 |
+
expected = Timedelta(1, unit="s")
|
| 405 |
+
assert to_timedelta("P0DT0H0M1S") == expected
|
| 406 |
+
|
| 407 |
+
# TODO: this is a test of to_timedelta returning NaT
|
| 408 |
+
def test_nat_converters(self):
|
| 409 |
+
result = to_timedelta("nat").to_numpy()
|
| 410 |
+
assert result.dtype.kind == "M"
|
| 411 |
+
assert result.astype("int64") == iNaT
|
| 412 |
+
|
| 413 |
+
result = to_timedelta("nan").to_numpy()
|
| 414 |
+
assert result.dtype.kind == "M"
|
| 415 |
+
assert result.astype("int64") == iNaT
|
| 416 |
+
|
| 417 |
+
def test_numeric_conversions(self):
|
| 418 |
+
assert Timedelta(0) == np.timedelta64(0, "ns")
|
| 419 |
+
assert Timedelta(10) == np.timedelta64(10, "ns")
|
| 420 |
+
assert Timedelta(10, unit="ns") == np.timedelta64(10, "ns")
|
| 421 |
+
|
| 422 |
+
assert Timedelta(10, unit="us") == np.timedelta64(10, "us")
|
| 423 |
+
assert Timedelta(10, unit="ms") == np.timedelta64(10, "ms")
|
| 424 |
+
assert Timedelta(10, unit="s") == np.timedelta64(10, "s")
|
| 425 |
+
assert Timedelta(10, unit="d") == np.timedelta64(10, "D")
|
| 426 |
+
|
| 427 |
+
def test_timedelta_conversions(self):
|
| 428 |
+
assert Timedelta(timedelta(seconds=1)) == np.timedelta64(1, "s").astype(
|
| 429 |
+
"m8[ns]"
|
| 430 |
+
)
|
| 431 |
+
assert Timedelta(timedelta(microseconds=1)) == np.timedelta64(1, "us").astype(
|
| 432 |
+
"m8[ns]"
|
| 433 |
+
)
|
| 434 |
+
assert Timedelta(timedelta(days=1)) == np.timedelta64(1, "D").astype("m8[ns]")
|
| 435 |
+
|
| 436 |
+
def test_to_numpy_alias(self):
|
| 437 |
+
# GH 24653: alias .to_numpy() for scalars
|
| 438 |
+
td = Timedelta("10m7s")
|
| 439 |
+
assert td.to_timedelta64() == td.to_numpy()
|
| 440 |
+
|
| 441 |
+
# GH#44460
|
| 442 |
+
msg = "dtype and copy arguments are ignored"
|
| 443 |
+
with pytest.raises(ValueError, match=msg):
|
| 444 |
+
td.to_numpy("m8[s]")
|
| 445 |
+
with pytest.raises(ValueError, match=msg):
|
| 446 |
+
td.to_numpy(copy=True)
|
| 447 |
+
|
| 448 |
+
def test_identity(self):
|
| 449 |
+
td = Timedelta(10, unit="d")
|
| 450 |
+
assert isinstance(td, Timedelta)
|
| 451 |
+
assert isinstance(td, timedelta)
|
| 452 |
+
|
| 453 |
+
def test_short_format_converters(self):
|
| 454 |
+
def conv(v):
|
| 455 |
+
return v.astype("m8[ns]")
|
| 456 |
+
|
| 457 |
+
assert Timedelta("10") == np.timedelta64(10, "ns")
|
| 458 |
+
assert Timedelta("10ns") == np.timedelta64(10, "ns")
|
| 459 |
+
assert Timedelta("100") == np.timedelta64(100, "ns")
|
| 460 |
+
assert Timedelta("100ns") == np.timedelta64(100, "ns")
|
| 461 |
+
|
| 462 |
+
assert Timedelta("1000") == np.timedelta64(1000, "ns")
|
| 463 |
+
assert Timedelta("1000ns") == np.timedelta64(1000, "ns")
|
| 464 |
+
assert Timedelta("1000NS") == np.timedelta64(1000, "ns")
|
| 465 |
+
|
| 466 |
+
assert Timedelta("10us") == np.timedelta64(10000, "ns")
|
| 467 |
+
assert Timedelta("100us") == np.timedelta64(100000, "ns")
|
| 468 |
+
assert Timedelta("1000us") == np.timedelta64(1000000, "ns")
|
| 469 |
+
assert Timedelta("1000Us") == np.timedelta64(1000000, "ns")
|
| 470 |
+
assert Timedelta("1000uS") == np.timedelta64(1000000, "ns")
|
| 471 |
+
|
| 472 |
+
assert Timedelta("1ms") == np.timedelta64(1000000, "ns")
|
| 473 |
+
assert Timedelta("10ms") == np.timedelta64(10000000, "ns")
|
| 474 |
+
assert Timedelta("100ms") == np.timedelta64(100000000, "ns")
|
| 475 |
+
assert Timedelta("1000ms") == np.timedelta64(1000000000, "ns")
|
| 476 |
+
|
| 477 |
+
assert Timedelta("-1s") == -np.timedelta64(1000000000, "ns")
|
| 478 |
+
assert Timedelta("1s") == np.timedelta64(1000000000, "ns")
|
| 479 |
+
assert Timedelta("10s") == np.timedelta64(10000000000, "ns")
|
| 480 |
+
assert Timedelta("100s") == np.timedelta64(100000000000, "ns")
|
| 481 |
+
assert Timedelta("1000s") == np.timedelta64(1000000000000, "ns")
|
| 482 |
+
|
| 483 |
+
assert Timedelta("1d") == conv(np.timedelta64(1, "D"))
|
| 484 |
+
assert Timedelta("-1d") == -conv(np.timedelta64(1, "D"))
|
| 485 |
+
assert Timedelta("1D") == conv(np.timedelta64(1, "D"))
|
| 486 |
+
assert Timedelta("10D") == conv(np.timedelta64(10, "D"))
|
| 487 |
+
assert Timedelta("100D") == conv(np.timedelta64(100, "D"))
|
| 488 |
+
assert Timedelta("1000D") == conv(np.timedelta64(1000, "D"))
|
| 489 |
+
assert Timedelta("10000D") == conv(np.timedelta64(10000, "D"))
|
| 490 |
+
|
| 491 |
+
# space
|
| 492 |
+
assert Timedelta(" 10000D ") == conv(np.timedelta64(10000, "D"))
|
| 493 |
+
assert Timedelta(" - 10000D ") == -conv(np.timedelta64(10000, "D"))
|
| 494 |
+
|
| 495 |
+
# invalid
|
| 496 |
+
msg = "invalid unit abbreviation"
|
| 497 |
+
with pytest.raises(ValueError, match=msg):
|
| 498 |
+
Timedelta("1foo")
|
| 499 |
+
msg = "unit abbreviation w/o a number"
|
| 500 |
+
with pytest.raises(ValueError, match=msg):
|
| 501 |
+
Timedelta("foo")
|
| 502 |
+
|
| 503 |
+
def test_full_format_converters(self):
|
| 504 |
+
def conv(v):
|
| 505 |
+
return v.astype("m8[ns]")
|
| 506 |
+
|
| 507 |
+
d1 = np.timedelta64(1, "D")
|
| 508 |
+
|
| 509 |
+
assert Timedelta("1days") == conv(d1)
|
| 510 |
+
assert Timedelta("1days,") == conv(d1)
|
| 511 |
+
assert Timedelta("- 1days,") == -conv(d1)
|
| 512 |
+
|
| 513 |
+
assert Timedelta("00:00:01") == conv(np.timedelta64(1, "s"))
|
| 514 |
+
assert Timedelta("06:00:01") == conv(np.timedelta64(6 * 3600 + 1, "s"))
|
| 515 |
+
assert Timedelta("06:00:01.0") == conv(np.timedelta64(6 * 3600 + 1, "s"))
|
| 516 |
+
assert Timedelta("06:00:01.01") == conv(
|
| 517 |
+
np.timedelta64(1000 * (6 * 3600 + 1) + 10, "ms")
|
| 518 |
+
)
|
| 519 |
+
|
| 520 |
+
assert Timedelta("- 1days, 00:00:01") == conv(-d1 + np.timedelta64(1, "s"))
|
| 521 |
+
assert Timedelta("1days, 06:00:01") == conv(
|
| 522 |
+
d1 + np.timedelta64(6 * 3600 + 1, "s")
|
| 523 |
+
)
|
| 524 |
+
assert Timedelta("1days, 06:00:01.01") == conv(
|
| 525 |
+
d1 + np.timedelta64(1000 * (6 * 3600 + 1) + 10, "ms")
|
| 526 |
+
)
|
| 527 |
+
|
| 528 |
+
# invalid
|
| 529 |
+
msg = "have leftover units"
|
| 530 |
+
with pytest.raises(ValueError, match=msg):
|
| 531 |
+
Timedelta("- 1days, 00")
|
| 532 |
+
|
| 533 |
+
def test_pickle(self):
|
| 534 |
+
v = Timedelta("1 days 10:11:12.0123456")
|
| 535 |
+
v_p = tm.round_trip_pickle(v)
|
| 536 |
+
assert v == v_p
|
| 537 |
+
|
| 538 |
+
def test_timedelta_hash_equality(self):
|
| 539 |
+
# GH 11129
|
| 540 |
+
v = Timedelta(1, "D")
|
| 541 |
+
td = timedelta(days=1)
|
| 542 |
+
assert hash(v) == hash(td)
|
| 543 |
+
|
| 544 |
+
d = {td: 2}
|
| 545 |
+
assert d[v] == 2
|
| 546 |
+
|
| 547 |
+
tds = [Timedelta(seconds=1) + Timedelta(days=n) for n in range(20)]
|
| 548 |
+
assert all(hash(td) == hash(td.to_pytimedelta()) for td in tds)
|
| 549 |
+
|
| 550 |
+
# python timedeltas drop ns resolution
|
| 551 |
+
ns_td = Timedelta(1, "ns")
|
| 552 |
+
assert hash(ns_td) != hash(ns_td.to_pytimedelta())
|
| 553 |
+
|
| 554 |
+
@pytest.mark.skip_ubsan
|
| 555 |
+
@pytest.mark.xfail(
|
| 556 |
+
reason="pd.Timedelta violates the Python hash invariant (GH#44504).",
|
| 557 |
+
)
|
| 558 |
+
@given(
|
| 559 |
+
st.integers(
|
| 560 |
+
min_value=(-sys.maxsize - 1) // 500,
|
| 561 |
+
max_value=sys.maxsize // 500,
|
| 562 |
+
)
|
| 563 |
+
)
|
| 564 |
+
def test_hash_equality_invariance(self, half_microseconds: int) -> None:
|
| 565 |
+
# GH#44504
|
| 566 |
+
|
| 567 |
+
nanoseconds = half_microseconds * 500
|
| 568 |
+
|
| 569 |
+
pandas_timedelta = Timedelta(nanoseconds)
|
| 570 |
+
numpy_timedelta = np.timedelta64(nanoseconds)
|
| 571 |
+
|
| 572 |
+
# See: https://docs.python.org/3/glossary.html#term-hashable
|
| 573 |
+
# Hashable objects which compare equal must have the same hash value.
|
| 574 |
+
assert pandas_timedelta != numpy_timedelta or hash(pandas_timedelta) == hash(
|
| 575 |
+
numpy_timedelta
|
| 576 |
+
)
|
| 577 |
+
|
| 578 |
+
def test_implementation_limits(self):
|
| 579 |
+
min_td = Timedelta(Timedelta.min)
|
| 580 |
+
max_td = Timedelta(Timedelta.max)
|
| 581 |
+
|
| 582 |
+
# GH 12727
|
| 583 |
+
# timedelta limits correspond to int64 boundaries
|
| 584 |
+
assert min_td._value == iNaT + 1
|
| 585 |
+
assert max_td._value == lib.i8max
|
| 586 |
+
|
| 587 |
+
# Beyond lower limit, a NAT before the Overflow
|
| 588 |
+
assert (min_td - Timedelta(1, "ns")) is NaT
|
| 589 |
+
|
| 590 |
+
msg = "int too (large|big) to convert"
|
| 591 |
+
with pytest.raises(OverflowError, match=msg):
|
| 592 |
+
min_td - Timedelta(2, "ns")
|
| 593 |
+
|
| 594 |
+
with pytest.raises(OverflowError, match=msg):
|
| 595 |
+
max_td + Timedelta(1, "ns")
|
| 596 |
+
|
| 597 |
+
# Same tests using the internal nanosecond values
|
| 598 |
+
td = Timedelta(min_td._value - 1, "ns")
|
| 599 |
+
assert td is NaT
|
| 600 |
+
|
| 601 |
+
msg = "Cannot cast -9223372036854775809 from ns to 'ns' without overflow"
|
| 602 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 603 |
+
Timedelta(min_td._value - 2, "ns")
|
| 604 |
+
|
| 605 |
+
msg = "Cannot cast 9223372036854775808 from ns to 'ns' without overflow"
|
| 606 |
+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
| 607 |
+
Timedelta(max_td._value + 1, "ns")
|
| 608 |
+
|
| 609 |
+
def test_total_seconds_precision(self):
|
| 610 |
+
# GH 19458
|
| 611 |
+
assert Timedelta("30s").total_seconds() == 30.0
|
| 612 |
+
assert Timedelta("0").total_seconds() == 0.0
|
| 613 |
+
assert Timedelta("-2s").total_seconds() == -2.0
|
| 614 |
+
assert Timedelta("5.324s").total_seconds() == 5.324
|
| 615 |
+
assert (Timedelta("30s").total_seconds() - 30.0) < 1e-20
|
| 616 |
+
assert (30.0 - Timedelta("30s").total_seconds()) < 1e-20
|
| 617 |
+
|
| 618 |
+
def test_resolution_string(self):
|
| 619 |
+
assert Timedelta(days=1).resolution_string == "D"
|
| 620 |
+
assert Timedelta(days=1, hours=6).resolution_string == "h"
|
| 621 |
+
assert Timedelta(days=1, minutes=6).resolution_string == "min"
|
| 622 |
+
assert Timedelta(days=1, seconds=6).resolution_string == "s"
|
| 623 |
+
assert Timedelta(days=1, milliseconds=6).resolution_string == "ms"
|
| 624 |
+
assert Timedelta(days=1, microseconds=6).resolution_string == "us"
|
| 625 |
+
assert Timedelta(days=1, nanoseconds=6).resolution_string == "ns"
|
| 626 |
+
|
| 627 |
+
def test_resolution_deprecated(self):
|
| 628 |
+
# GH#21344
|
| 629 |
+
td = Timedelta(days=4, hours=3)
|
| 630 |
+
result = td.resolution
|
| 631 |
+
assert result == Timedelta(nanoseconds=1)
|
| 632 |
+
|
| 633 |
+
# Check that the attribute is available on the class, mirroring
|
| 634 |
+
# the stdlib timedelta behavior
|
| 635 |
+
result = Timedelta.resolution
|
| 636 |
+
assert result == Timedelta(nanoseconds=1)
|
| 637 |
+
|
| 638 |
+
|
| 639 |
+
@pytest.mark.parametrize(
|
| 640 |
+
"value, expected",
|
| 641 |
+
[
|
| 642 |
+
(Timedelta("10s"), True),
|
| 643 |
+
(Timedelta("-10s"), True),
|
| 644 |
+
(Timedelta(10, unit="ns"), True),
|
| 645 |
+
(Timedelta(0, unit="ns"), False),
|
| 646 |
+
(Timedelta(-10, unit="ns"), True),
|
| 647 |
+
(Timedelta(None), True),
|
| 648 |
+
(NaT, True),
|
| 649 |
+
],
|
| 650 |
+
)
|
| 651 |
+
def test_truthiness(value, expected):
|
| 652 |
+
# https://github.com/pandas-dev/pandas/issues/21484
|
| 653 |
+
assert bool(value) is expected
|
| 654 |
+
|
| 655 |
+
|
| 656 |
+
def test_timedelta_attribute_precision():
|
| 657 |
+
# GH 31354
|
| 658 |
+
td = Timedelta(1552211999999999872, unit="ns")
|
| 659 |
+
result = td.days * 86400
|
| 660 |
+
result += td.seconds
|
| 661 |
+
result *= 1000000
|
| 662 |
+
result += td.microseconds
|
| 663 |
+
result *= 1000
|
| 664 |
+
result += td.nanoseconds
|
| 665 |
+
expected = td._value
|
| 666 |
+
assert result == expected
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/__init__.py
ADDED
|
File without changes
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/__init__.py
ADDED
|
File without changes
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_as_unit.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
|
| 3 |
+
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
| 4 |
+
from pandas.errors import OutOfBoundsDatetime
|
| 5 |
+
|
| 6 |
+
from pandas import Timestamp
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class TestTimestampAsUnit:
|
| 10 |
+
def test_as_unit(self):
|
| 11 |
+
ts = Timestamp("1970-01-01").as_unit("ns")
|
| 12 |
+
assert ts.unit == "ns"
|
| 13 |
+
|
| 14 |
+
assert ts.as_unit("ns") is ts
|
| 15 |
+
|
| 16 |
+
res = ts.as_unit("us")
|
| 17 |
+
assert res._value == ts._value // 1000
|
| 18 |
+
assert res._creso == NpyDatetimeUnit.NPY_FR_us.value
|
| 19 |
+
|
| 20 |
+
rt = res.as_unit("ns")
|
| 21 |
+
assert rt._value == ts._value
|
| 22 |
+
assert rt._creso == ts._creso
|
| 23 |
+
|
| 24 |
+
res = ts.as_unit("ms")
|
| 25 |
+
assert res._value == ts._value // 1_000_000
|
| 26 |
+
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
| 27 |
+
|
| 28 |
+
rt = res.as_unit("ns")
|
| 29 |
+
assert rt._value == ts._value
|
| 30 |
+
assert rt._creso == ts._creso
|
| 31 |
+
|
| 32 |
+
res = ts.as_unit("s")
|
| 33 |
+
assert res._value == ts._value // 1_000_000_000
|
| 34 |
+
assert res._creso == NpyDatetimeUnit.NPY_FR_s.value
|
| 35 |
+
|
| 36 |
+
rt = res.as_unit("ns")
|
| 37 |
+
assert rt._value == ts._value
|
| 38 |
+
assert rt._creso == ts._creso
|
| 39 |
+
|
| 40 |
+
def test_as_unit_overflows(self):
|
| 41 |
+
# microsecond that would be just out of bounds for nano
|
| 42 |
+
us = 9223372800000000
|
| 43 |
+
ts = Timestamp._from_value_and_reso(us, NpyDatetimeUnit.NPY_FR_us.value, None)
|
| 44 |
+
|
| 45 |
+
msg = "Cannot cast 2262-04-12 00:00:00 to unit='ns' without overflow"
|
| 46 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 47 |
+
ts.as_unit("ns")
|
| 48 |
+
|
| 49 |
+
res = ts.as_unit("ms")
|
| 50 |
+
assert res._value == us // 1000
|
| 51 |
+
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
| 52 |
+
|
| 53 |
+
def test_as_unit_rounding(self):
|
| 54 |
+
ts = Timestamp(1_500_000) # i.e. 1500 microseconds
|
| 55 |
+
res = ts.as_unit("ms")
|
| 56 |
+
|
| 57 |
+
expected = Timestamp(1_000_000) # i.e. 1 millisecond
|
| 58 |
+
assert res == expected
|
| 59 |
+
|
| 60 |
+
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
| 61 |
+
assert res._value == 1
|
| 62 |
+
|
| 63 |
+
with pytest.raises(ValueError, match="Cannot losslessly convert units"):
|
| 64 |
+
ts.as_unit("ms", round_ok=False)
|
| 65 |
+
|
| 66 |
+
def test_as_unit_non_nano(self):
|
| 67 |
+
# case where we are going neither to nor from nano
|
| 68 |
+
ts = Timestamp("1970-01-02").as_unit("ms")
|
| 69 |
+
assert ts.year == 1970
|
| 70 |
+
assert ts.month == 1
|
| 71 |
+
assert ts.day == 2
|
| 72 |
+
assert ts.hour == ts.minute == ts.second == ts.microsecond == ts.nanosecond == 0
|
| 73 |
+
|
| 74 |
+
res = ts.as_unit("s")
|
| 75 |
+
assert res._value == 24 * 3600
|
| 76 |
+
assert res.year == 1970
|
| 77 |
+
assert res.month == 1
|
| 78 |
+
assert res.day == 2
|
| 79 |
+
assert (
|
| 80 |
+
res.hour
|
| 81 |
+
== res.minute
|
| 82 |
+
== res.second
|
| 83 |
+
== res.microsecond
|
| 84 |
+
== res.nanosecond
|
| 85 |
+
== 0
|
| 86 |
+
)
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_normalize.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
|
| 3 |
+
from pandas._libs.tslibs import Timestamp
|
| 4 |
+
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class TestTimestampNormalize:
|
| 8 |
+
@pytest.mark.parametrize("arg", ["2013-11-30", "2013-11-30 12:00:00"])
|
| 9 |
+
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
| 10 |
+
def test_normalize(self, tz_naive_fixture, arg, unit):
|
| 11 |
+
tz = tz_naive_fixture
|
| 12 |
+
ts = Timestamp(arg, tz=tz).as_unit(unit)
|
| 13 |
+
result = ts.normalize()
|
| 14 |
+
expected = Timestamp("2013-11-30", tz=tz)
|
| 15 |
+
assert result == expected
|
| 16 |
+
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
| 17 |
+
|
| 18 |
+
def test_normalize_pre_epoch_dates(self):
|
| 19 |
+
# GH: 36294
|
| 20 |
+
result = Timestamp("1969-01-01 09:00:00").normalize()
|
| 21 |
+
expected = Timestamp("1969-01-01 00:00:00")
|
| 22 |
+
assert result == expected
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_replace.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import datetime
|
| 2 |
+
|
| 3 |
+
from dateutil.tz import gettz
|
| 4 |
+
import numpy as np
|
| 5 |
+
import pytest
|
| 6 |
+
import pytz
|
| 7 |
+
|
| 8 |
+
from pandas._libs.tslibs import (
|
| 9 |
+
OutOfBoundsDatetime,
|
| 10 |
+
Timestamp,
|
| 11 |
+
conversion,
|
| 12 |
+
)
|
| 13 |
+
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
| 14 |
+
import pandas.util._test_decorators as td
|
| 15 |
+
|
| 16 |
+
import pandas._testing as tm
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class TestTimestampReplace:
|
| 20 |
+
def test_replace_out_of_pydatetime_bounds(self):
|
| 21 |
+
# GH#50348
|
| 22 |
+
ts = Timestamp("2016-01-01").as_unit("ns")
|
| 23 |
+
|
| 24 |
+
msg = "Out of bounds timestamp: 99999-01-01 00:00:00 with frequency 'ns'"
|
| 25 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 26 |
+
ts.replace(year=99_999)
|
| 27 |
+
|
| 28 |
+
ts = ts.as_unit("ms")
|
| 29 |
+
result = ts.replace(year=99_999)
|
| 30 |
+
assert result.year == 99_999
|
| 31 |
+
assert result._value == Timestamp(np.datetime64("99999-01-01", "ms"))._value
|
| 32 |
+
|
| 33 |
+
def test_replace_non_nano(self):
|
| 34 |
+
ts = Timestamp._from_value_and_reso(
|
| 35 |
+
91514880000000000, NpyDatetimeUnit.NPY_FR_us.value, None
|
| 36 |
+
)
|
| 37 |
+
assert ts.to_pydatetime() == datetime(4869, 12, 28)
|
| 38 |
+
|
| 39 |
+
result = ts.replace(year=4900)
|
| 40 |
+
assert result._creso == ts._creso
|
| 41 |
+
assert result.to_pydatetime() == datetime(4900, 12, 28)
|
| 42 |
+
|
| 43 |
+
def test_replace_naive(self):
|
| 44 |
+
# GH#14621, GH#7825
|
| 45 |
+
ts = Timestamp("2016-01-01 09:00:00")
|
| 46 |
+
result = ts.replace(hour=0)
|
| 47 |
+
expected = Timestamp("2016-01-01 00:00:00")
|
| 48 |
+
assert result == expected
|
| 49 |
+
|
| 50 |
+
def test_replace_aware(self, tz_aware_fixture):
|
| 51 |
+
tz = tz_aware_fixture
|
| 52 |
+
# GH#14621, GH#7825
|
| 53 |
+
# replacing datetime components with and w/o presence of a timezone
|
| 54 |
+
ts = Timestamp("2016-01-01 09:00:00", tz=tz)
|
| 55 |
+
result = ts.replace(hour=0)
|
| 56 |
+
expected = Timestamp("2016-01-01 00:00:00", tz=tz)
|
| 57 |
+
assert result == expected
|
| 58 |
+
|
| 59 |
+
def test_replace_preserves_nanos(self, tz_aware_fixture):
|
| 60 |
+
tz = tz_aware_fixture
|
| 61 |
+
# GH#14621, GH#7825
|
| 62 |
+
ts = Timestamp("2016-01-01 09:00:00.000000123", tz=tz)
|
| 63 |
+
result = ts.replace(hour=0)
|
| 64 |
+
expected = Timestamp("2016-01-01 00:00:00.000000123", tz=tz)
|
| 65 |
+
assert result == expected
|
| 66 |
+
|
| 67 |
+
def test_replace_multiple(self, tz_aware_fixture):
|
| 68 |
+
tz = tz_aware_fixture
|
| 69 |
+
# GH#14621, GH#7825
|
| 70 |
+
# replacing datetime components with and w/o presence of a timezone
|
| 71 |
+
# test all
|
| 72 |
+
ts = Timestamp("2016-01-01 09:00:00.000000123", tz=tz)
|
| 73 |
+
result = ts.replace(
|
| 74 |
+
year=2015,
|
| 75 |
+
month=2,
|
| 76 |
+
day=2,
|
| 77 |
+
hour=0,
|
| 78 |
+
minute=5,
|
| 79 |
+
second=5,
|
| 80 |
+
microsecond=5,
|
| 81 |
+
nanosecond=5,
|
| 82 |
+
)
|
| 83 |
+
expected = Timestamp("2015-02-02 00:05:05.000005005", tz=tz)
|
| 84 |
+
assert result == expected
|
| 85 |
+
|
| 86 |
+
def test_replace_invalid_kwarg(self, tz_aware_fixture):
|
| 87 |
+
tz = tz_aware_fixture
|
| 88 |
+
# GH#14621, GH#7825
|
| 89 |
+
ts = Timestamp("2016-01-01 09:00:00.000000123", tz=tz)
|
| 90 |
+
msg = r"replace\(\) got an unexpected keyword argument"
|
| 91 |
+
with pytest.raises(TypeError, match=msg):
|
| 92 |
+
ts.replace(foo=5)
|
| 93 |
+
|
| 94 |
+
def test_replace_integer_args(self, tz_aware_fixture):
|
| 95 |
+
tz = tz_aware_fixture
|
| 96 |
+
# GH#14621, GH#7825
|
| 97 |
+
ts = Timestamp("2016-01-01 09:00:00.000000123", tz=tz)
|
| 98 |
+
msg = "value must be an integer, received <class 'float'> for hour"
|
| 99 |
+
with pytest.raises(ValueError, match=msg):
|
| 100 |
+
ts.replace(hour=0.1)
|
| 101 |
+
|
| 102 |
+
def test_replace_tzinfo_equiv_tz_localize_none(self):
|
| 103 |
+
# GH#14621, GH#7825
|
| 104 |
+
# assert conversion to naive is the same as replacing tzinfo with None
|
| 105 |
+
ts = Timestamp("2013-11-03 01:59:59.999999-0400", tz="US/Eastern")
|
| 106 |
+
assert ts.tz_localize(None) == ts.replace(tzinfo=None)
|
| 107 |
+
|
| 108 |
+
@td.skip_if_windows
|
| 109 |
+
def test_replace_tzinfo(self):
|
| 110 |
+
# GH#15683
|
| 111 |
+
dt = datetime(2016, 3, 27, 1)
|
| 112 |
+
tzinfo = pytz.timezone("CET").localize(dt, is_dst=False).tzinfo
|
| 113 |
+
|
| 114 |
+
result_dt = dt.replace(tzinfo=tzinfo)
|
| 115 |
+
result_pd = Timestamp(dt).replace(tzinfo=tzinfo)
|
| 116 |
+
|
| 117 |
+
# datetime.timestamp() converts in the local timezone
|
| 118 |
+
with tm.set_timezone("UTC"):
|
| 119 |
+
assert result_dt.timestamp() == result_pd.timestamp()
|
| 120 |
+
|
| 121 |
+
assert result_dt == result_pd
|
| 122 |
+
assert result_dt == result_pd.to_pydatetime()
|
| 123 |
+
|
| 124 |
+
result_dt = dt.replace(tzinfo=tzinfo).replace(tzinfo=None)
|
| 125 |
+
result_pd = Timestamp(dt).replace(tzinfo=tzinfo).replace(tzinfo=None)
|
| 126 |
+
|
| 127 |
+
# datetime.timestamp() converts in the local timezone
|
| 128 |
+
with tm.set_timezone("UTC"):
|
| 129 |
+
assert result_dt.timestamp() == result_pd.timestamp()
|
| 130 |
+
|
| 131 |
+
assert result_dt == result_pd
|
| 132 |
+
assert result_dt == result_pd.to_pydatetime()
|
| 133 |
+
|
| 134 |
+
@pytest.mark.parametrize(
|
| 135 |
+
"tz, normalize",
|
| 136 |
+
[
|
| 137 |
+
(pytz.timezone("US/Eastern"), lambda x: x.tzinfo.normalize(x)),
|
| 138 |
+
(gettz("US/Eastern"), lambda x: x),
|
| 139 |
+
],
|
| 140 |
+
)
|
| 141 |
+
def test_replace_across_dst(self, tz, normalize):
|
| 142 |
+
# GH#18319 check that 1) timezone is correctly normalized and
|
| 143 |
+
# 2) that hour is not incorrectly changed by this normalization
|
| 144 |
+
ts_naive = Timestamp("2017-12-03 16:03:30")
|
| 145 |
+
ts_aware = conversion.localize_pydatetime(ts_naive, tz)
|
| 146 |
+
|
| 147 |
+
# Preliminary sanity-check
|
| 148 |
+
assert ts_aware == normalize(ts_aware)
|
| 149 |
+
|
| 150 |
+
# Replace across DST boundary
|
| 151 |
+
ts2 = ts_aware.replace(month=6)
|
| 152 |
+
|
| 153 |
+
# Check that `replace` preserves hour literal
|
| 154 |
+
assert (ts2.hour, ts2.minute) == (ts_aware.hour, ts_aware.minute)
|
| 155 |
+
|
| 156 |
+
# Check that post-replace object is appropriately normalized
|
| 157 |
+
ts2b = normalize(ts2)
|
| 158 |
+
assert ts2 == ts2b
|
| 159 |
+
|
| 160 |
+
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
| 161 |
+
def test_replace_dst_border(self, unit):
|
| 162 |
+
# Gh 7825
|
| 163 |
+
t = Timestamp("2013-11-3", tz="America/Chicago").as_unit(unit)
|
| 164 |
+
result = t.replace(hour=3)
|
| 165 |
+
expected = Timestamp("2013-11-3 03:00:00", tz="America/Chicago")
|
| 166 |
+
assert result == expected
|
| 167 |
+
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
| 168 |
+
|
| 169 |
+
@pytest.mark.parametrize("fold", [0, 1])
|
| 170 |
+
@pytest.mark.parametrize("tz", ["dateutil/Europe/London", "Europe/London"])
|
| 171 |
+
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
| 172 |
+
def test_replace_dst_fold(self, fold, tz, unit):
|
| 173 |
+
# GH 25017
|
| 174 |
+
d = datetime(2019, 10, 27, 2, 30)
|
| 175 |
+
ts = Timestamp(d, tz=tz).as_unit(unit)
|
| 176 |
+
result = ts.replace(hour=1, fold=fold)
|
| 177 |
+
expected = Timestamp(datetime(2019, 10, 27, 1, 30)).tz_localize(
|
| 178 |
+
tz, ambiguous=not fold
|
| 179 |
+
)
|
| 180 |
+
assert result == expected
|
| 181 |
+
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
| 182 |
+
|
| 183 |
+
@pytest.mark.parametrize("fold", [0, 1])
|
| 184 |
+
def test_replace_preserves_fold(self, fold):
|
| 185 |
+
# GH#37610. Check that replace preserves Timestamp fold property
|
| 186 |
+
tz = gettz("Europe/Moscow")
|
| 187 |
+
|
| 188 |
+
ts = Timestamp(
|
| 189 |
+
year=2009, month=10, day=25, hour=2, minute=30, fold=fold, tzinfo=tz
|
| 190 |
+
)
|
| 191 |
+
ts_replaced = ts.replace(second=1)
|
| 192 |
+
|
| 193 |
+
assert ts_replaced.fold == fold
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_round.py
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from hypothesis import (
|
| 2 |
+
given,
|
| 3 |
+
strategies as st,
|
| 4 |
+
)
|
| 5 |
+
import numpy as np
|
| 6 |
+
import pytest
|
| 7 |
+
import pytz
|
| 8 |
+
|
| 9 |
+
from pandas._libs import lib
|
| 10 |
+
from pandas._libs.tslibs import (
|
| 11 |
+
NaT,
|
| 12 |
+
OutOfBoundsDatetime,
|
| 13 |
+
Timedelta,
|
| 14 |
+
Timestamp,
|
| 15 |
+
iNaT,
|
| 16 |
+
to_offset,
|
| 17 |
+
)
|
| 18 |
+
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
| 19 |
+
from pandas._libs.tslibs.period import INVALID_FREQ_ERR_MSG
|
| 20 |
+
|
| 21 |
+
import pandas._testing as tm
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class TestTimestampRound:
|
| 25 |
+
def test_round_division_by_zero_raises(self):
|
| 26 |
+
ts = Timestamp("2016-01-01")
|
| 27 |
+
|
| 28 |
+
msg = "Division by zero in rounding"
|
| 29 |
+
with pytest.raises(ValueError, match=msg):
|
| 30 |
+
ts.round("0ns")
|
| 31 |
+
|
| 32 |
+
@pytest.mark.parametrize(
|
| 33 |
+
"timestamp, freq, expected",
|
| 34 |
+
[
|
| 35 |
+
("20130101 09:10:11", "D", "20130101"),
|
| 36 |
+
("20130101 19:10:11", "D", "20130102"),
|
| 37 |
+
("20130201 12:00:00", "D", "20130202"),
|
| 38 |
+
("20130104 12:00:00", "D", "20130105"),
|
| 39 |
+
("2000-01-05 05:09:15.13", "D", "2000-01-05 00:00:00"),
|
| 40 |
+
("2000-01-05 05:09:15.13", "h", "2000-01-05 05:00:00"),
|
| 41 |
+
("2000-01-05 05:09:15.13", "s", "2000-01-05 05:09:15"),
|
| 42 |
+
],
|
| 43 |
+
)
|
| 44 |
+
def test_round_frequencies(self, timestamp, freq, expected):
|
| 45 |
+
dt = Timestamp(timestamp)
|
| 46 |
+
result = dt.round(freq)
|
| 47 |
+
expected = Timestamp(expected)
|
| 48 |
+
assert result == expected
|
| 49 |
+
|
| 50 |
+
def test_round_tzaware(self):
|
| 51 |
+
dt = Timestamp("20130101 09:10:11", tz="US/Eastern")
|
| 52 |
+
result = dt.round("D")
|
| 53 |
+
expected = Timestamp("20130101", tz="US/Eastern")
|
| 54 |
+
assert result == expected
|
| 55 |
+
|
| 56 |
+
dt = Timestamp("20130101 09:10:11", tz="US/Eastern")
|
| 57 |
+
result = dt.round("s")
|
| 58 |
+
assert result == dt
|
| 59 |
+
|
| 60 |
+
def test_round_30min(self):
|
| 61 |
+
# round
|
| 62 |
+
dt = Timestamp("20130104 12:32:00")
|
| 63 |
+
result = dt.round("30Min")
|
| 64 |
+
expected = Timestamp("20130104 12:30:00")
|
| 65 |
+
assert result == expected
|
| 66 |
+
|
| 67 |
+
def test_round_subsecond(self):
|
| 68 |
+
# GH#14440 & GH#15578
|
| 69 |
+
result = Timestamp("2016-10-17 12:00:00.0015").round("ms")
|
| 70 |
+
expected = Timestamp("2016-10-17 12:00:00.002000")
|
| 71 |
+
assert result == expected
|
| 72 |
+
|
| 73 |
+
result = Timestamp("2016-10-17 12:00:00.00149").round("ms")
|
| 74 |
+
expected = Timestamp("2016-10-17 12:00:00.001000")
|
| 75 |
+
assert result == expected
|
| 76 |
+
|
| 77 |
+
ts = Timestamp("2016-10-17 12:00:00.0015")
|
| 78 |
+
for freq in ["us", "ns"]:
|
| 79 |
+
assert ts == ts.round(freq)
|
| 80 |
+
|
| 81 |
+
result = Timestamp("2016-10-17 12:00:00.001501031").round("10ns")
|
| 82 |
+
expected = Timestamp("2016-10-17 12:00:00.001501030")
|
| 83 |
+
assert result == expected
|
| 84 |
+
|
| 85 |
+
def test_round_nonstandard_freq(self):
|
| 86 |
+
with tm.assert_produces_warning(False):
|
| 87 |
+
Timestamp("2016-10-17 12:00:00.001501031").round("1010ns")
|
| 88 |
+
|
| 89 |
+
def test_round_invalid_arg(self):
|
| 90 |
+
stamp = Timestamp("2000-01-05 05:09:15.13")
|
| 91 |
+
with pytest.raises(ValueError, match=INVALID_FREQ_ERR_MSG):
|
| 92 |
+
stamp.round("foo")
|
| 93 |
+
|
| 94 |
+
@pytest.mark.parametrize(
|
| 95 |
+
"test_input, rounder, freq, expected",
|
| 96 |
+
[
|
| 97 |
+
("2117-01-01 00:00:45", "floor", "15s", "2117-01-01 00:00:45"),
|
| 98 |
+
("2117-01-01 00:00:45", "ceil", "15s", "2117-01-01 00:00:45"),
|
| 99 |
+
(
|
| 100 |
+
"2117-01-01 00:00:45.000000012",
|
| 101 |
+
"floor",
|
| 102 |
+
"10ns",
|
| 103 |
+
"2117-01-01 00:00:45.000000010",
|
| 104 |
+
),
|
| 105 |
+
(
|
| 106 |
+
"1823-01-01 00:00:01.000000012",
|
| 107 |
+
"ceil",
|
| 108 |
+
"10ns",
|
| 109 |
+
"1823-01-01 00:00:01.000000020",
|
| 110 |
+
),
|
| 111 |
+
("1823-01-01 00:00:01", "floor", "1s", "1823-01-01 00:00:01"),
|
| 112 |
+
("1823-01-01 00:00:01", "ceil", "1s", "1823-01-01 00:00:01"),
|
| 113 |
+
("NaT", "floor", "1s", "NaT"),
|
| 114 |
+
("NaT", "ceil", "1s", "NaT"),
|
| 115 |
+
],
|
| 116 |
+
)
|
| 117 |
+
def test_ceil_floor_edge(self, test_input, rounder, freq, expected):
|
| 118 |
+
dt = Timestamp(test_input)
|
| 119 |
+
func = getattr(dt, rounder)
|
| 120 |
+
result = func(freq)
|
| 121 |
+
|
| 122 |
+
if dt is NaT:
|
| 123 |
+
assert result is NaT
|
| 124 |
+
else:
|
| 125 |
+
expected = Timestamp(expected)
|
| 126 |
+
assert result == expected
|
| 127 |
+
|
| 128 |
+
@pytest.mark.parametrize(
|
| 129 |
+
"test_input, freq, expected",
|
| 130 |
+
[
|
| 131 |
+
("2018-01-01 00:02:06", "2s", "2018-01-01 00:02:06"),
|
| 132 |
+
("2018-01-01 00:02:00", "2min", "2018-01-01 00:02:00"),
|
| 133 |
+
("2018-01-01 00:04:00", "4min", "2018-01-01 00:04:00"),
|
| 134 |
+
("2018-01-01 00:15:00", "15min", "2018-01-01 00:15:00"),
|
| 135 |
+
("2018-01-01 00:20:00", "20min", "2018-01-01 00:20:00"),
|
| 136 |
+
("2018-01-01 03:00:00", "3h", "2018-01-01 03:00:00"),
|
| 137 |
+
],
|
| 138 |
+
)
|
| 139 |
+
@pytest.mark.parametrize("rounder", ["ceil", "floor", "round"])
|
| 140 |
+
def test_round_minute_freq(self, test_input, freq, expected, rounder):
|
| 141 |
+
# Ensure timestamps that shouldn't round dont!
|
| 142 |
+
# GH#21262
|
| 143 |
+
|
| 144 |
+
dt = Timestamp(test_input)
|
| 145 |
+
expected = Timestamp(expected)
|
| 146 |
+
func = getattr(dt, rounder)
|
| 147 |
+
result = func(freq)
|
| 148 |
+
assert result == expected
|
| 149 |
+
|
| 150 |
+
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
| 151 |
+
def test_ceil(self, unit):
|
| 152 |
+
dt = Timestamp("20130101 09:10:11").as_unit(unit)
|
| 153 |
+
result = dt.ceil("D")
|
| 154 |
+
expected = Timestamp("20130102")
|
| 155 |
+
assert result == expected
|
| 156 |
+
assert result._creso == dt._creso
|
| 157 |
+
|
| 158 |
+
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
| 159 |
+
def test_floor(self, unit):
|
| 160 |
+
dt = Timestamp("20130101 09:10:11").as_unit(unit)
|
| 161 |
+
result = dt.floor("D")
|
| 162 |
+
expected = Timestamp("20130101")
|
| 163 |
+
assert result == expected
|
| 164 |
+
assert result._creso == dt._creso
|
| 165 |
+
|
| 166 |
+
@pytest.mark.parametrize("method", ["ceil", "round", "floor"])
|
| 167 |
+
@pytest.mark.parametrize(
|
| 168 |
+
"unit",
|
| 169 |
+
["ns", "us", "ms", "s"],
|
| 170 |
+
)
|
| 171 |
+
def test_round_dst_border_ambiguous(self, method, unit):
|
| 172 |
+
# GH 18946 round near "fall back" DST
|
| 173 |
+
ts = Timestamp("2017-10-29 00:00:00", tz="UTC").tz_convert("Europe/Madrid")
|
| 174 |
+
ts = ts.as_unit(unit)
|
| 175 |
+
#
|
| 176 |
+
result = getattr(ts, method)("h", ambiguous=True)
|
| 177 |
+
assert result == ts
|
| 178 |
+
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
| 179 |
+
|
| 180 |
+
result = getattr(ts, method)("h", ambiguous=False)
|
| 181 |
+
expected = Timestamp("2017-10-29 01:00:00", tz="UTC").tz_convert(
|
| 182 |
+
"Europe/Madrid"
|
| 183 |
+
)
|
| 184 |
+
assert result == expected
|
| 185 |
+
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
| 186 |
+
|
| 187 |
+
result = getattr(ts, method)("h", ambiguous="NaT")
|
| 188 |
+
assert result is NaT
|
| 189 |
+
|
| 190 |
+
msg = "Cannot infer dst time"
|
| 191 |
+
with pytest.raises(pytz.AmbiguousTimeError, match=msg):
|
| 192 |
+
getattr(ts, method)("h", ambiguous="raise")
|
| 193 |
+
|
| 194 |
+
@pytest.mark.parametrize(
|
| 195 |
+
"method, ts_str, freq",
|
| 196 |
+
[
|
| 197 |
+
["ceil", "2018-03-11 01:59:00-0600", "5min"],
|
| 198 |
+
["round", "2018-03-11 01:59:00-0600", "5min"],
|
| 199 |
+
["floor", "2018-03-11 03:01:00-0500", "2h"],
|
| 200 |
+
],
|
| 201 |
+
)
|
| 202 |
+
@pytest.mark.parametrize(
|
| 203 |
+
"unit",
|
| 204 |
+
["ns", "us", "ms", "s"],
|
| 205 |
+
)
|
| 206 |
+
def test_round_dst_border_nonexistent(self, method, ts_str, freq, unit):
|
| 207 |
+
# GH 23324 round near "spring forward" DST
|
| 208 |
+
ts = Timestamp(ts_str, tz="America/Chicago").as_unit(unit)
|
| 209 |
+
result = getattr(ts, method)(freq, nonexistent="shift_forward")
|
| 210 |
+
expected = Timestamp("2018-03-11 03:00:00", tz="America/Chicago")
|
| 211 |
+
assert result == expected
|
| 212 |
+
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
| 213 |
+
|
| 214 |
+
result = getattr(ts, method)(freq, nonexistent="NaT")
|
| 215 |
+
assert result is NaT
|
| 216 |
+
|
| 217 |
+
msg = "2018-03-11 02:00:00"
|
| 218 |
+
with pytest.raises(pytz.NonExistentTimeError, match=msg):
|
| 219 |
+
getattr(ts, method)(freq, nonexistent="raise")
|
| 220 |
+
|
| 221 |
+
@pytest.mark.parametrize(
|
| 222 |
+
"timestamp",
|
| 223 |
+
[
|
| 224 |
+
"2018-01-01 0:0:0.124999360",
|
| 225 |
+
"2018-01-01 0:0:0.125000367",
|
| 226 |
+
"2018-01-01 0:0:0.125500",
|
| 227 |
+
"2018-01-01 0:0:0.126500",
|
| 228 |
+
"2018-01-01 12:00:00",
|
| 229 |
+
"2019-01-01 12:00:00",
|
| 230 |
+
],
|
| 231 |
+
)
|
| 232 |
+
@pytest.mark.parametrize(
|
| 233 |
+
"freq",
|
| 234 |
+
[
|
| 235 |
+
"2ns",
|
| 236 |
+
"3ns",
|
| 237 |
+
"4ns",
|
| 238 |
+
"5ns",
|
| 239 |
+
"6ns",
|
| 240 |
+
"7ns",
|
| 241 |
+
"250ns",
|
| 242 |
+
"500ns",
|
| 243 |
+
"750ns",
|
| 244 |
+
"1us",
|
| 245 |
+
"19us",
|
| 246 |
+
"250us",
|
| 247 |
+
"500us",
|
| 248 |
+
"750us",
|
| 249 |
+
"1s",
|
| 250 |
+
"2s",
|
| 251 |
+
"3s",
|
| 252 |
+
"1D",
|
| 253 |
+
],
|
| 254 |
+
)
|
| 255 |
+
def test_round_int64(self, timestamp, freq):
|
| 256 |
+
# check that all rounding modes are accurate to int64 precision
|
| 257 |
+
# see GH#22591
|
| 258 |
+
dt = Timestamp(timestamp).as_unit("ns")
|
| 259 |
+
unit = to_offset(freq).nanos
|
| 260 |
+
|
| 261 |
+
# test floor
|
| 262 |
+
result = dt.floor(freq)
|
| 263 |
+
assert result._value % unit == 0, f"floor not a {freq} multiple"
|
| 264 |
+
assert 0 <= dt._value - result._value < unit, "floor error"
|
| 265 |
+
|
| 266 |
+
# test ceil
|
| 267 |
+
result = dt.ceil(freq)
|
| 268 |
+
assert result._value % unit == 0, f"ceil not a {freq} multiple"
|
| 269 |
+
assert 0 <= result._value - dt._value < unit, "ceil error"
|
| 270 |
+
|
| 271 |
+
# test round
|
| 272 |
+
result = dt.round(freq)
|
| 273 |
+
assert result._value % unit == 0, f"round not a {freq} multiple"
|
| 274 |
+
assert abs(result._value - dt._value) <= unit // 2, "round error"
|
| 275 |
+
if unit % 2 == 0 and abs(result._value - dt._value) == unit // 2:
|
| 276 |
+
# round half to even
|
| 277 |
+
assert result._value // unit % 2 == 0, "round half to even error"
|
| 278 |
+
|
| 279 |
+
def test_round_implementation_bounds(self):
|
| 280 |
+
# See also: analogous test for Timedelta
|
| 281 |
+
result = Timestamp.min.ceil("s")
|
| 282 |
+
expected = Timestamp(1677, 9, 21, 0, 12, 44)
|
| 283 |
+
assert result == expected
|
| 284 |
+
|
| 285 |
+
result = Timestamp.max.floor("s")
|
| 286 |
+
expected = Timestamp.max - Timedelta(854775807)
|
| 287 |
+
assert result == expected
|
| 288 |
+
|
| 289 |
+
msg = "Cannot round 1677-09-21 00:12:43.145224193 to freq=<Second>"
|
| 290 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 291 |
+
Timestamp.min.floor("s")
|
| 292 |
+
|
| 293 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 294 |
+
Timestamp.min.round("s")
|
| 295 |
+
|
| 296 |
+
msg = "Cannot round 2262-04-11 23:47:16.854775807 to freq=<Second>"
|
| 297 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 298 |
+
Timestamp.max.ceil("s")
|
| 299 |
+
|
| 300 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 301 |
+
Timestamp.max.round("s")
|
| 302 |
+
|
| 303 |
+
@given(val=st.integers(iNaT + 1, lib.i8max))
|
| 304 |
+
@pytest.mark.parametrize(
|
| 305 |
+
"method", [Timestamp.round, Timestamp.floor, Timestamp.ceil]
|
| 306 |
+
)
|
| 307 |
+
def test_round_sanity(self, val, method):
|
| 308 |
+
cls = Timestamp
|
| 309 |
+
err_cls = OutOfBoundsDatetime
|
| 310 |
+
|
| 311 |
+
val = np.int64(val)
|
| 312 |
+
ts = cls(val)
|
| 313 |
+
|
| 314 |
+
def checker(ts, nanos, unit):
|
| 315 |
+
# First check that we do raise in cases where we should
|
| 316 |
+
if nanos == 1:
|
| 317 |
+
pass
|
| 318 |
+
else:
|
| 319 |
+
div, mod = divmod(ts._value, nanos)
|
| 320 |
+
diff = int(nanos - mod)
|
| 321 |
+
lb = ts._value - mod
|
| 322 |
+
assert lb <= ts._value # i.e. no overflows with python ints
|
| 323 |
+
ub = ts._value + diff
|
| 324 |
+
assert ub > ts._value # i.e. no overflows with python ints
|
| 325 |
+
|
| 326 |
+
msg = "without overflow"
|
| 327 |
+
if mod == 0:
|
| 328 |
+
# We should never be raising in this
|
| 329 |
+
pass
|
| 330 |
+
elif method is cls.ceil:
|
| 331 |
+
if ub > cls.max._value:
|
| 332 |
+
with pytest.raises(err_cls, match=msg):
|
| 333 |
+
method(ts, unit)
|
| 334 |
+
return
|
| 335 |
+
elif method is cls.floor:
|
| 336 |
+
if lb < cls.min._value:
|
| 337 |
+
with pytest.raises(err_cls, match=msg):
|
| 338 |
+
method(ts, unit)
|
| 339 |
+
return
|
| 340 |
+
elif mod >= diff:
|
| 341 |
+
if ub > cls.max._value:
|
| 342 |
+
with pytest.raises(err_cls, match=msg):
|
| 343 |
+
method(ts, unit)
|
| 344 |
+
return
|
| 345 |
+
elif lb < cls.min._value:
|
| 346 |
+
with pytest.raises(err_cls, match=msg):
|
| 347 |
+
method(ts, unit)
|
| 348 |
+
return
|
| 349 |
+
|
| 350 |
+
res = method(ts, unit)
|
| 351 |
+
|
| 352 |
+
td = res - ts
|
| 353 |
+
diff = abs(td._value)
|
| 354 |
+
assert diff < nanos
|
| 355 |
+
assert res._value % nanos == 0
|
| 356 |
+
|
| 357 |
+
if method is cls.round:
|
| 358 |
+
assert diff <= nanos / 2
|
| 359 |
+
elif method is cls.floor:
|
| 360 |
+
assert res <= ts
|
| 361 |
+
elif method is cls.ceil:
|
| 362 |
+
assert res >= ts
|
| 363 |
+
|
| 364 |
+
nanos = 1
|
| 365 |
+
checker(ts, nanos, "ns")
|
| 366 |
+
|
| 367 |
+
nanos = 1000
|
| 368 |
+
checker(ts, nanos, "us")
|
| 369 |
+
|
| 370 |
+
nanos = 1_000_000
|
| 371 |
+
checker(ts, nanos, "ms")
|
| 372 |
+
|
| 373 |
+
nanos = 1_000_000_000
|
| 374 |
+
checker(ts, nanos, "s")
|
| 375 |
+
|
| 376 |
+
nanos = 60 * 1_000_000_000
|
| 377 |
+
checker(ts, nanos, "min")
|
| 378 |
+
|
| 379 |
+
nanos = 60 * 60 * 1_000_000_000
|
| 380 |
+
checker(ts, nanos, "h")
|
| 381 |
+
|
| 382 |
+
nanos = 24 * 60 * 60 * 1_000_000_000
|
| 383 |
+
checker(ts, nanos, "D")
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_timestamp_method.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# NB: This is for the Timestamp.timestamp *method* specifically, not
|
| 2 |
+
# the Timestamp class in general.
|
| 3 |
+
|
| 4 |
+
from pytz import utc
|
| 5 |
+
|
| 6 |
+
from pandas._libs.tslibs import Timestamp
|
| 7 |
+
import pandas.util._test_decorators as td
|
| 8 |
+
|
| 9 |
+
import pandas._testing as tm
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class TestTimestampMethod:
|
| 13 |
+
@td.skip_if_windows
|
| 14 |
+
def test_timestamp(self, fixed_now_ts):
|
| 15 |
+
# GH#17329
|
| 16 |
+
# tz-naive --> treat it as if it were UTC for purposes of timestamp()
|
| 17 |
+
ts = fixed_now_ts
|
| 18 |
+
uts = ts.replace(tzinfo=utc)
|
| 19 |
+
assert ts.timestamp() == uts.timestamp()
|
| 20 |
+
|
| 21 |
+
tsc = Timestamp("2014-10-11 11:00:01.12345678", tz="US/Central")
|
| 22 |
+
utsc = tsc.tz_convert("UTC")
|
| 23 |
+
|
| 24 |
+
# utsc is a different representation of the same time
|
| 25 |
+
assert tsc.timestamp() == utsc.timestamp()
|
| 26 |
+
|
| 27 |
+
# datetime.timestamp() converts in the local timezone
|
| 28 |
+
with tm.set_timezone("UTC"):
|
| 29 |
+
# should agree with datetime.timestamp method
|
| 30 |
+
dt = ts.to_pydatetime()
|
| 31 |
+
assert dt.timestamp() == ts.timestamp()
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_to_julian_date.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pandas import Timestamp
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class TestTimestampToJulianDate:
|
| 5 |
+
def test_compare_1700(self):
|
| 6 |
+
ts = Timestamp("1700-06-23")
|
| 7 |
+
res = ts.to_julian_date()
|
| 8 |
+
assert res == 2_342_145.5
|
| 9 |
+
|
| 10 |
+
def test_compare_2000(self):
|
| 11 |
+
ts = Timestamp("2000-04-12")
|
| 12 |
+
res = ts.to_julian_date()
|
| 13 |
+
assert res == 2_451_646.5
|
| 14 |
+
|
| 15 |
+
def test_compare_2100(self):
|
| 16 |
+
ts = Timestamp("2100-08-12")
|
| 17 |
+
res = ts.to_julian_date()
|
| 18 |
+
assert res == 2_488_292.5
|
| 19 |
+
|
| 20 |
+
def test_compare_hour01(self):
|
| 21 |
+
ts = Timestamp("2000-08-12T01:00:00")
|
| 22 |
+
res = ts.to_julian_date()
|
| 23 |
+
assert res == 2_451_768.5416666666666666
|
| 24 |
+
|
| 25 |
+
def test_compare_hour13(self):
|
| 26 |
+
ts = Timestamp("2000-08-12T13:00:00")
|
| 27 |
+
res = ts.to_julian_date()
|
| 28 |
+
assert res == 2_451_769.0416666666666666
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_to_pydatetime.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import (
|
| 2 |
+
datetime,
|
| 3 |
+
timedelta,
|
| 4 |
+
)
|
| 5 |
+
|
| 6 |
+
import pytz
|
| 7 |
+
|
| 8 |
+
from pandas._libs.tslibs.timezones import dateutil_gettz as gettz
|
| 9 |
+
import pandas.util._test_decorators as td
|
| 10 |
+
|
| 11 |
+
from pandas import Timestamp
|
| 12 |
+
import pandas._testing as tm
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class TestTimestampToPyDatetime:
|
| 16 |
+
def test_to_pydatetime_fold(self):
|
| 17 |
+
# GH#45087
|
| 18 |
+
tzstr = "dateutil/usr/share/zoneinfo/America/Chicago"
|
| 19 |
+
ts = Timestamp(year=2013, month=11, day=3, hour=1, minute=0, fold=1, tz=tzstr)
|
| 20 |
+
dt = ts.to_pydatetime()
|
| 21 |
+
assert dt.fold == 1
|
| 22 |
+
|
| 23 |
+
def test_to_pydatetime_nonzero_nano(self):
|
| 24 |
+
ts = Timestamp("2011-01-01 9:00:00.123456789")
|
| 25 |
+
|
| 26 |
+
# Warn the user of data loss (nanoseconds).
|
| 27 |
+
with tm.assert_produces_warning(UserWarning):
|
| 28 |
+
expected = datetime(2011, 1, 1, 9, 0, 0, 123456)
|
| 29 |
+
result = ts.to_pydatetime()
|
| 30 |
+
assert result == expected
|
| 31 |
+
|
| 32 |
+
def test_timestamp_to_datetime(self):
|
| 33 |
+
stamp = Timestamp("20090415", tz="US/Eastern")
|
| 34 |
+
dtval = stamp.to_pydatetime()
|
| 35 |
+
assert stamp == dtval
|
| 36 |
+
assert stamp.tzinfo == dtval.tzinfo
|
| 37 |
+
|
| 38 |
+
def test_timestamp_to_pydatetime_dateutil(self):
|
| 39 |
+
stamp = Timestamp("20090415", tz="dateutil/US/Eastern")
|
| 40 |
+
dtval = stamp.to_pydatetime()
|
| 41 |
+
assert stamp == dtval
|
| 42 |
+
assert stamp.tzinfo == dtval.tzinfo
|
| 43 |
+
|
| 44 |
+
def test_timestamp_to_pydatetime_explicit_pytz(self):
|
| 45 |
+
stamp = Timestamp("20090415", tz=pytz.timezone("US/Eastern"))
|
| 46 |
+
dtval = stamp.to_pydatetime()
|
| 47 |
+
assert stamp == dtval
|
| 48 |
+
assert stamp.tzinfo == dtval.tzinfo
|
| 49 |
+
|
| 50 |
+
@td.skip_if_windows
|
| 51 |
+
def test_timestamp_to_pydatetime_explicit_dateutil(self):
|
| 52 |
+
stamp = Timestamp("20090415", tz=gettz("US/Eastern"))
|
| 53 |
+
dtval = stamp.to_pydatetime()
|
| 54 |
+
assert stamp == dtval
|
| 55 |
+
assert stamp.tzinfo == dtval.tzinfo
|
| 56 |
+
|
| 57 |
+
def test_to_pydatetime_bijective(self):
|
| 58 |
+
# Ensure that converting to datetime and back only loses precision
|
| 59 |
+
# by going from nanoseconds to microseconds.
|
| 60 |
+
exp_warning = None if Timestamp.max.nanosecond == 0 else UserWarning
|
| 61 |
+
with tm.assert_produces_warning(exp_warning):
|
| 62 |
+
pydt_max = Timestamp.max.to_pydatetime()
|
| 63 |
+
|
| 64 |
+
assert (
|
| 65 |
+
Timestamp(pydt_max).as_unit("ns")._value / 1000
|
| 66 |
+
== Timestamp.max._value / 1000
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
exp_warning = None if Timestamp.min.nanosecond == 0 else UserWarning
|
| 70 |
+
with tm.assert_produces_warning(exp_warning):
|
| 71 |
+
pydt_min = Timestamp.min.to_pydatetime()
|
| 72 |
+
|
| 73 |
+
# The next assertion can be enabled once GH#39221 is merged
|
| 74 |
+
# assert pydt_min < Timestamp.min # this is bc nanos are dropped
|
| 75 |
+
tdus = timedelta(microseconds=1)
|
| 76 |
+
assert pydt_min + tdus > Timestamp.min
|
| 77 |
+
|
| 78 |
+
assert (
|
| 79 |
+
Timestamp(pydt_min + tdus).as_unit("ns")._value / 1000
|
| 80 |
+
== Timestamp.min._value / 1000
|
| 81 |
+
)
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_tz_convert.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import dateutil
|
| 2 |
+
import pytest
|
| 3 |
+
|
| 4 |
+
from pandas._libs.tslibs import timezones
|
| 5 |
+
import pandas.util._test_decorators as td
|
| 6 |
+
|
| 7 |
+
from pandas import Timestamp
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class TestTimestampTZConvert:
|
| 11 |
+
@pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
|
| 12 |
+
def test_astimezone(self, tzstr):
|
| 13 |
+
# astimezone is an alias for tz_convert, so keep it with
|
| 14 |
+
# the tz_convert tests
|
| 15 |
+
utcdate = Timestamp("3/11/2012 22:00", tz="UTC")
|
| 16 |
+
expected = utcdate.tz_convert(tzstr)
|
| 17 |
+
result = utcdate.astimezone(tzstr)
|
| 18 |
+
assert expected == result
|
| 19 |
+
assert isinstance(result, Timestamp)
|
| 20 |
+
|
| 21 |
+
@pytest.mark.parametrize(
|
| 22 |
+
"stamp",
|
| 23 |
+
[
|
| 24 |
+
"2014-02-01 09:00",
|
| 25 |
+
"2014-07-08 09:00",
|
| 26 |
+
"2014-11-01 17:00",
|
| 27 |
+
"2014-11-05 00:00",
|
| 28 |
+
],
|
| 29 |
+
)
|
| 30 |
+
def test_tz_convert_roundtrip(self, stamp, tz_aware_fixture):
|
| 31 |
+
tz = tz_aware_fixture
|
| 32 |
+
|
| 33 |
+
ts = Timestamp(stamp, tz="UTC")
|
| 34 |
+
converted = ts.tz_convert(tz)
|
| 35 |
+
|
| 36 |
+
reset = converted.tz_convert(None)
|
| 37 |
+
assert reset == Timestamp(stamp)
|
| 38 |
+
assert reset.tzinfo is None
|
| 39 |
+
assert reset == converted.tz_convert("UTC").tz_localize(None)
|
| 40 |
+
|
| 41 |
+
@td.skip_if_windows
|
| 42 |
+
def test_tz_convert_utc_with_system_utc(self):
|
| 43 |
+
# from system utc to real utc
|
| 44 |
+
ts = Timestamp("2001-01-05 11:56", tz=timezones.maybe_get_tz("dateutil/UTC"))
|
| 45 |
+
# check that the time hasn't changed.
|
| 46 |
+
assert ts == ts.tz_convert(dateutil.tz.tzutc())
|
| 47 |
+
|
| 48 |
+
# from system utc to real utc
|
| 49 |
+
ts = Timestamp("2001-01-05 11:56", tz=timezones.maybe_get_tz("dateutil/UTC"))
|
| 50 |
+
# check that the time hasn't changed.
|
| 51 |
+
assert ts == ts.tz_convert(dateutil.tz.tzutc())
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/methods/test_tz_localize.py
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import timedelta
|
| 2 |
+
import re
|
| 3 |
+
|
| 4 |
+
from dateutil.tz import gettz
|
| 5 |
+
import pytest
|
| 6 |
+
import pytz
|
| 7 |
+
from pytz.exceptions import (
|
| 8 |
+
AmbiguousTimeError,
|
| 9 |
+
NonExistentTimeError,
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
| 13 |
+
from pandas.errors import OutOfBoundsDatetime
|
| 14 |
+
|
| 15 |
+
from pandas import (
|
| 16 |
+
NaT,
|
| 17 |
+
Timestamp,
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
try:
|
| 21 |
+
from zoneinfo import ZoneInfo
|
| 22 |
+
except ImportError:
|
| 23 |
+
# Cannot assign to a type
|
| 24 |
+
ZoneInfo = None # type: ignore[misc, assignment]
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class TestTimestampTZLocalize:
|
| 28 |
+
@pytest.mark.skip_ubsan
|
| 29 |
+
def test_tz_localize_pushes_out_of_bounds(self):
|
| 30 |
+
# GH#12677
|
| 31 |
+
# tz_localize that pushes away from the boundary is OK
|
| 32 |
+
msg = (
|
| 33 |
+
f"Converting {Timestamp.min.strftime('%Y-%m-%d %H:%M:%S')} "
|
| 34 |
+
f"underflows past {Timestamp.min}"
|
| 35 |
+
)
|
| 36 |
+
pac = Timestamp.min.tz_localize("US/Pacific")
|
| 37 |
+
assert pac._value > Timestamp.min._value
|
| 38 |
+
pac.tz_convert("Asia/Tokyo") # tz_convert doesn't change value
|
| 39 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 40 |
+
Timestamp.min.tz_localize("Asia/Tokyo")
|
| 41 |
+
|
| 42 |
+
# tz_localize that pushes away from the boundary is OK
|
| 43 |
+
msg = (
|
| 44 |
+
f"Converting {Timestamp.max.strftime('%Y-%m-%d %H:%M:%S')} "
|
| 45 |
+
f"overflows past {Timestamp.max}"
|
| 46 |
+
)
|
| 47 |
+
tokyo = Timestamp.max.tz_localize("Asia/Tokyo")
|
| 48 |
+
assert tokyo._value < Timestamp.max._value
|
| 49 |
+
tokyo.tz_convert("US/Pacific") # tz_convert doesn't change value
|
| 50 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 51 |
+
Timestamp.max.tz_localize("US/Pacific")
|
| 52 |
+
|
| 53 |
+
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
| 54 |
+
def test_tz_localize_ambiguous_bool(self, unit):
|
| 55 |
+
# make sure that we are correctly accepting bool values as ambiguous
|
| 56 |
+
# GH#14402
|
| 57 |
+
ts = Timestamp("2015-11-01 01:00:03").as_unit(unit)
|
| 58 |
+
expected0 = Timestamp("2015-11-01 01:00:03-0500", tz="US/Central")
|
| 59 |
+
expected1 = Timestamp("2015-11-01 01:00:03-0600", tz="US/Central")
|
| 60 |
+
|
| 61 |
+
msg = "Cannot infer dst time from 2015-11-01 01:00:03"
|
| 62 |
+
with pytest.raises(pytz.AmbiguousTimeError, match=msg):
|
| 63 |
+
ts.tz_localize("US/Central")
|
| 64 |
+
|
| 65 |
+
with pytest.raises(pytz.AmbiguousTimeError, match=msg):
|
| 66 |
+
ts.tz_localize("dateutil/US/Central")
|
| 67 |
+
|
| 68 |
+
if ZoneInfo is not None:
|
| 69 |
+
try:
|
| 70 |
+
tz = ZoneInfo("US/Central")
|
| 71 |
+
except KeyError:
|
| 72 |
+
# no tzdata
|
| 73 |
+
pass
|
| 74 |
+
else:
|
| 75 |
+
with pytest.raises(pytz.AmbiguousTimeError, match=msg):
|
| 76 |
+
ts.tz_localize(tz)
|
| 77 |
+
|
| 78 |
+
result = ts.tz_localize("US/Central", ambiguous=True)
|
| 79 |
+
assert result == expected0
|
| 80 |
+
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
| 81 |
+
|
| 82 |
+
result = ts.tz_localize("US/Central", ambiguous=False)
|
| 83 |
+
assert result == expected1
|
| 84 |
+
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
| 85 |
+
|
| 86 |
+
def test_tz_localize_ambiguous(self):
|
| 87 |
+
ts = Timestamp("2014-11-02 01:00")
|
| 88 |
+
ts_dst = ts.tz_localize("US/Eastern", ambiguous=True)
|
| 89 |
+
ts_no_dst = ts.tz_localize("US/Eastern", ambiguous=False)
|
| 90 |
+
|
| 91 |
+
assert ts_no_dst._value - ts_dst._value == 3600
|
| 92 |
+
msg = re.escape(
|
| 93 |
+
"'ambiguous' parameter must be one of: "
|
| 94 |
+
"True, False, 'NaT', 'raise' (default)"
|
| 95 |
+
)
|
| 96 |
+
with pytest.raises(ValueError, match=msg):
|
| 97 |
+
ts.tz_localize("US/Eastern", ambiguous="infer")
|
| 98 |
+
|
| 99 |
+
# GH#8025
|
| 100 |
+
msg = "Cannot localize tz-aware Timestamp, use tz_convert for conversions"
|
| 101 |
+
with pytest.raises(TypeError, match=msg):
|
| 102 |
+
Timestamp("2011-01-01", tz="US/Eastern").tz_localize("Asia/Tokyo")
|
| 103 |
+
|
| 104 |
+
msg = "Cannot convert tz-naive Timestamp, use tz_localize to localize"
|
| 105 |
+
with pytest.raises(TypeError, match=msg):
|
| 106 |
+
Timestamp("2011-01-01").tz_convert("Asia/Tokyo")
|
| 107 |
+
|
| 108 |
+
@pytest.mark.parametrize(
|
| 109 |
+
"stamp, tz",
|
| 110 |
+
[
|
| 111 |
+
("2015-03-08 02:00", "US/Eastern"),
|
| 112 |
+
("2015-03-08 02:30", "US/Pacific"),
|
| 113 |
+
("2015-03-29 02:00", "Europe/Paris"),
|
| 114 |
+
("2015-03-29 02:30", "Europe/Belgrade"),
|
| 115 |
+
],
|
| 116 |
+
)
|
| 117 |
+
def test_tz_localize_nonexistent(self, stamp, tz):
|
| 118 |
+
# GH#13057
|
| 119 |
+
ts = Timestamp(stamp)
|
| 120 |
+
with pytest.raises(NonExistentTimeError, match=stamp):
|
| 121 |
+
ts.tz_localize(tz)
|
| 122 |
+
# GH 22644
|
| 123 |
+
with pytest.raises(NonExistentTimeError, match=stamp):
|
| 124 |
+
ts.tz_localize(tz, nonexistent="raise")
|
| 125 |
+
assert ts.tz_localize(tz, nonexistent="NaT") is NaT
|
| 126 |
+
|
| 127 |
+
@pytest.mark.parametrize(
|
| 128 |
+
"stamp, tz, forward_expected, backward_expected",
|
| 129 |
+
[
|
| 130 |
+
(
|
| 131 |
+
"2015-03-29 02:00:00",
|
| 132 |
+
"Europe/Warsaw",
|
| 133 |
+
"2015-03-29 03:00:00",
|
| 134 |
+
"2015-03-29 01:59:59",
|
| 135 |
+
), # utc+1 -> utc+2
|
| 136 |
+
(
|
| 137 |
+
"2023-03-12 02:00:00",
|
| 138 |
+
"America/Los_Angeles",
|
| 139 |
+
"2023-03-12 03:00:00",
|
| 140 |
+
"2023-03-12 01:59:59",
|
| 141 |
+
), # utc-8 -> utc-7
|
| 142 |
+
(
|
| 143 |
+
"2023-03-26 01:00:00",
|
| 144 |
+
"Europe/London",
|
| 145 |
+
"2023-03-26 02:00:00",
|
| 146 |
+
"2023-03-26 00:59:59",
|
| 147 |
+
), # utc+0 -> utc+1
|
| 148 |
+
(
|
| 149 |
+
"2023-03-26 00:00:00",
|
| 150 |
+
"Atlantic/Azores",
|
| 151 |
+
"2023-03-26 01:00:00",
|
| 152 |
+
"2023-03-25 23:59:59",
|
| 153 |
+
), # utc-1 -> utc+0
|
| 154 |
+
],
|
| 155 |
+
)
|
| 156 |
+
def test_tz_localize_nonexistent_shift(
|
| 157 |
+
self, stamp, tz, forward_expected, backward_expected
|
| 158 |
+
):
|
| 159 |
+
ts = Timestamp(stamp)
|
| 160 |
+
forward_ts = ts.tz_localize(tz, nonexistent="shift_forward")
|
| 161 |
+
assert forward_ts == Timestamp(forward_expected, tz=tz)
|
| 162 |
+
backward_ts = ts.tz_localize(tz, nonexistent="shift_backward")
|
| 163 |
+
assert backward_ts == Timestamp(backward_expected, tz=tz)
|
| 164 |
+
|
| 165 |
+
def test_tz_localize_ambiguous_raise(self):
|
| 166 |
+
# GH#13057
|
| 167 |
+
ts = Timestamp("2015-11-1 01:00")
|
| 168 |
+
msg = "Cannot infer dst time from 2015-11-01 01:00:00,"
|
| 169 |
+
with pytest.raises(AmbiguousTimeError, match=msg):
|
| 170 |
+
ts.tz_localize("US/Pacific", ambiguous="raise")
|
| 171 |
+
|
| 172 |
+
def test_tz_localize_nonexistent_invalid_arg(self, warsaw):
|
| 173 |
+
# GH 22644
|
| 174 |
+
tz = warsaw
|
| 175 |
+
ts = Timestamp("2015-03-29 02:00:00")
|
| 176 |
+
msg = (
|
| 177 |
+
"The nonexistent argument must be one of 'raise', 'NaT', "
|
| 178 |
+
"'shift_forward', 'shift_backward' or a timedelta object"
|
| 179 |
+
)
|
| 180 |
+
with pytest.raises(ValueError, match=msg):
|
| 181 |
+
ts.tz_localize(tz, nonexistent="foo")
|
| 182 |
+
|
| 183 |
+
@pytest.mark.parametrize(
|
| 184 |
+
"stamp",
|
| 185 |
+
[
|
| 186 |
+
"2014-02-01 09:00",
|
| 187 |
+
"2014-07-08 09:00",
|
| 188 |
+
"2014-11-01 17:00",
|
| 189 |
+
"2014-11-05 00:00",
|
| 190 |
+
],
|
| 191 |
+
)
|
| 192 |
+
def test_tz_localize_roundtrip(self, stamp, tz_aware_fixture):
|
| 193 |
+
tz = tz_aware_fixture
|
| 194 |
+
ts = Timestamp(stamp)
|
| 195 |
+
localized = ts.tz_localize(tz)
|
| 196 |
+
assert localized == Timestamp(stamp, tz=tz)
|
| 197 |
+
|
| 198 |
+
msg = "Cannot localize tz-aware Timestamp"
|
| 199 |
+
with pytest.raises(TypeError, match=msg):
|
| 200 |
+
localized.tz_localize(tz)
|
| 201 |
+
|
| 202 |
+
reset = localized.tz_localize(None)
|
| 203 |
+
assert reset == ts
|
| 204 |
+
assert reset.tzinfo is None
|
| 205 |
+
|
| 206 |
+
def test_tz_localize_ambiguous_compat(self):
|
| 207 |
+
# validate that pytz and dateutil are compat for dst
|
| 208 |
+
# when the transition happens
|
| 209 |
+
naive = Timestamp("2013-10-27 01:00:00")
|
| 210 |
+
|
| 211 |
+
pytz_zone = "Europe/London"
|
| 212 |
+
dateutil_zone = "dateutil/Europe/London"
|
| 213 |
+
result_pytz = naive.tz_localize(pytz_zone, ambiguous=False)
|
| 214 |
+
result_dateutil = naive.tz_localize(dateutil_zone, ambiguous=False)
|
| 215 |
+
assert result_pytz._value == result_dateutil._value
|
| 216 |
+
assert result_pytz._value == 1382835600
|
| 217 |
+
|
| 218 |
+
# fixed ambiguous behavior
|
| 219 |
+
# see gh-14621, GH#45087
|
| 220 |
+
assert result_pytz.to_pydatetime().tzname() == "GMT"
|
| 221 |
+
assert result_dateutil.to_pydatetime().tzname() == "GMT"
|
| 222 |
+
assert str(result_pytz) == str(result_dateutil)
|
| 223 |
+
|
| 224 |
+
# 1 hour difference
|
| 225 |
+
result_pytz = naive.tz_localize(pytz_zone, ambiguous=True)
|
| 226 |
+
result_dateutil = naive.tz_localize(dateutil_zone, ambiguous=True)
|
| 227 |
+
assert result_pytz._value == result_dateutil._value
|
| 228 |
+
assert result_pytz._value == 1382832000
|
| 229 |
+
|
| 230 |
+
# see gh-14621
|
| 231 |
+
assert str(result_pytz) == str(result_dateutil)
|
| 232 |
+
assert (
|
| 233 |
+
result_pytz.to_pydatetime().tzname()
|
| 234 |
+
== result_dateutil.to_pydatetime().tzname()
|
| 235 |
+
)
|
| 236 |
+
|
| 237 |
+
@pytest.mark.parametrize(
|
| 238 |
+
"tz",
|
| 239 |
+
[
|
| 240 |
+
pytz.timezone("US/Eastern"),
|
| 241 |
+
gettz("US/Eastern"),
|
| 242 |
+
"US/Eastern",
|
| 243 |
+
"dateutil/US/Eastern",
|
| 244 |
+
],
|
| 245 |
+
)
|
| 246 |
+
def test_timestamp_tz_localize(self, tz):
|
| 247 |
+
stamp = Timestamp("3/11/2012 04:00")
|
| 248 |
+
|
| 249 |
+
result = stamp.tz_localize(tz)
|
| 250 |
+
expected = Timestamp("3/11/2012 04:00", tz=tz)
|
| 251 |
+
assert result.hour == expected.hour
|
| 252 |
+
assert result == expected
|
| 253 |
+
|
| 254 |
+
@pytest.mark.parametrize(
|
| 255 |
+
"start_ts, tz, end_ts, shift",
|
| 256 |
+
[
|
| 257 |
+
["2015-03-29 02:20:00", "Europe/Warsaw", "2015-03-29 03:00:00", "forward"],
|
| 258 |
+
[
|
| 259 |
+
"2015-03-29 02:20:00",
|
| 260 |
+
"Europe/Warsaw",
|
| 261 |
+
"2015-03-29 01:59:59.999999999",
|
| 262 |
+
"backward",
|
| 263 |
+
],
|
| 264 |
+
[
|
| 265 |
+
"2015-03-29 02:20:00",
|
| 266 |
+
"Europe/Warsaw",
|
| 267 |
+
"2015-03-29 03:20:00",
|
| 268 |
+
timedelta(hours=1),
|
| 269 |
+
],
|
| 270 |
+
[
|
| 271 |
+
"2015-03-29 02:20:00",
|
| 272 |
+
"Europe/Warsaw",
|
| 273 |
+
"2015-03-29 01:20:00",
|
| 274 |
+
timedelta(hours=-1),
|
| 275 |
+
],
|
| 276 |
+
["2018-03-11 02:33:00", "US/Pacific", "2018-03-11 03:00:00", "forward"],
|
| 277 |
+
[
|
| 278 |
+
"2018-03-11 02:33:00",
|
| 279 |
+
"US/Pacific",
|
| 280 |
+
"2018-03-11 01:59:59.999999999",
|
| 281 |
+
"backward",
|
| 282 |
+
],
|
| 283 |
+
[
|
| 284 |
+
"2018-03-11 02:33:00",
|
| 285 |
+
"US/Pacific",
|
| 286 |
+
"2018-03-11 03:33:00",
|
| 287 |
+
timedelta(hours=1),
|
| 288 |
+
],
|
| 289 |
+
[
|
| 290 |
+
"2018-03-11 02:33:00",
|
| 291 |
+
"US/Pacific",
|
| 292 |
+
"2018-03-11 01:33:00",
|
| 293 |
+
timedelta(hours=-1),
|
| 294 |
+
],
|
| 295 |
+
],
|
| 296 |
+
)
|
| 297 |
+
@pytest.mark.parametrize("tz_type", ["", "dateutil/"])
|
| 298 |
+
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
| 299 |
+
def test_timestamp_tz_localize_nonexistent_shift(
|
| 300 |
+
self, start_ts, tz, end_ts, shift, tz_type, unit
|
| 301 |
+
):
|
| 302 |
+
# GH 8917, 24466
|
| 303 |
+
tz = tz_type + tz
|
| 304 |
+
if isinstance(shift, str):
|
| 305 |
+
shift = "shift_" + shift
|
| 306 |
+
ts = Timestamp(start_ts).as_unit(unit)
|
| 307 |
+
result = ts.tz_localize(tz, nonexistent=shift)
|
| 308 |
+
expected = Timestamp(end_ts).tz_localize(tz)
|
| 309 |
+
|
| 310 |
+
if unit == "us":
|
| 311 |
+
assert result == expected.replace(nanosecond=0)
|
| 312 |
+
elif unit == "ms":
|
| 313 |
+
micros = expected.microsecond - expected.microsecond % 1000
|
| 314 |
+
assert result == expected.replace(microsecond=micros, nanosecond=0)
|
| 315 |
+
elif unit == "s":
|
| 316 |
+
assert result == expected.replace(microsecond=0, nanosecond=0)
|
| 317 |
+
else:
|
| 318 |
+
assert result == expected
|
| 319 |
+
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
| 320 |
+
|
| 321 |
+
@pytest.mark.parametrize("offset", [-1, 1])
|
| 322 |
+
def test_timestamp_tz_localize_nonexistent_shift_invalid(self, offset, warsaw):
|
| 323 |
+
# GH 8917, 24466
|
| 324 |
+
tz = warsaw
|
| 325 |
+
ts = Timestamp("2015-03-29 02:20:00")
|
| 326 |
+
msg = "The provided timedelta will relocalize on a nonexistent time"
|
| 327 |
+
with pytest.raises(ValueError, match=msg):
|
| 328 |
+
ts.tz_localize(tz, nonexistent=timedelta(seconds=offset))
|
| 329 |
+
|
| 330 |
+
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
| 331 |
+
def test_timestamp_tz_localize_nonexistent_NaT(self, warsaw, unit):
|
| 332 |
+
# GH 8917
|
| 333 |
+
tz = warsaw
|
| 334 |
+
ts = Timestamp("2015-03-29 02:20:00").as_unit(unit)
|
| 335 |
+
result = ts.tz_localize(tz, nonexistent="NaT")
|
| 336 |
+
assert result is NaT
|
| 337 |
+
|
| 338 |
+
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
| 339 |
+
def test_timestamp_tz_localize_nonexistent_raise(self, warsaw, unit):
|
| 340 |
+
# GH 8917
|
| 341 |
+
tz = warsaw
|
| 342 |
+
ts = Timestamp("2015-03-29 02:20:00").as_unit(unit)
|
| 343 |
+
msg = "2015-03-29 02:20:00"
|
| 344 |
+
with pytest.raises(pytz.NonExistentTimeError, match=msg):
|
| 345 |
+
ts.tz_localize(tz, nonexistent="raise")
|
| 346 |
+
msg = (
|
| 347 |
+
"The nonexistent argument must be one of 'raise', 'NaT', "
|
| 348 |
+
"'shift_forward', 'shift_backward' or a timedelta object"
|
| 349 |
+
)
|
| 350 |
+
with pytest.raises(ValueError, match=msg):
|
| 351 |
+
ts.tz_localize(tz, nonexistent="foo")
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/test_arithmetic.py
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import (
|
| 2 |
+
datetime,
|
| 3 |
+
timedelta,
|
| 4 |
+
timezone,
|
| 5 |
+
)
|
| 6 |
+
|
| 7 |
+
from dateutil.tz import gettz
|
| 8 |
+
import numpy as np
|
| 9 |
+
import pytest
|
| 10 |
+
import pytz
|
| 11 |
+
|
| 12 |
+
from pandas._libs.tslibs import (
|
| 13 |
+
OutOfBoundsDatetime,
|
| 14 |
+
OutOfBoundsTimedelta,
|
| 15 |
+
Timedelta,
|
| 16 |
+
Timestamp,
|
| 17 |
+
offsets,
|
| 18 |
+
to_offset,
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
import pandas._testing as tm
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class TestTimestampArithmetic:
|
| 25 |
+
def test_overflow_offset(self):
|
| 26 |
+
# no overflow expected
|
| 27 |
+
|
| 28 |
+
stamp = Timestamp("2000/1/1")
|
| 29 |
+
offset_no_overflow = to_offset("D") * 100
|
| 30 |
+
|
| 31 |
+
expected = Timestamp("2000/04/10")
|
| 32 |
+
assert stamp + offset_no_overflow == expected
|
| 33 |
+
|
| 34 |
+
assert offset_no_overflow + stamp == expected
|
| 35 |
+
|
| 36 |
+
expected = Timestamp("1999/09/23")
|
| 37 |
+
assert stamp - offset_no_overflow == expected
|
| 38 |
+
|
| 39 |
+
def test_overflow_offset_raises(self):
|
| 40 |
+
# xref https://github.com/statsmodels/statsmodels/issues/3374
|
| 41 |
+
# ends up multiplying really large numbers which overflow
|
| 42 |
+
|
| 43 |
+
stamp = Timestamp("2017-01-13 00:00:00").as_unit("ns")
|
| 44 |
+
offset_overflow = 20169940 * offsets.Day(1)
|
| 45 |
+
lmsg2 = r"Cannot cast -?20169940 days \+?00:00:00 to unit='ns' without overflow"
|
| 46 |
+
|
| 47 |
+
with pytest.raises(OutOfBoundsTimedelta, match=lmsg2):
|
| 48 |
+
stamp + offset_overflow
|
| 49 |
+
|
| 50 |
+
with pytest.raises(OutOfBoundsTimedelta, match=lmsg2):
|
| 51 |
+
offset_overflow + stamp
|
| 52 |
+
|
| 53 |
+
with pytest.raises(OutOfBoundsTimedelta, match=lmsg2):
|
| 54 |
+
stamp - offset_overflow
|
| 55 |
+
|
| 56 |
+
# xref https://github.com/pandas-dev/pandas/issues/14080
|
| 57 |
+
# used to crash, so check for proper overflow exception
|
| 58 |
+
|
| 59 |
+
stamp = Timestamp("2000/1/1").as_unit("ns")
|
| 60 |
+
offset_overflow = to_offset("D") * 100**5
|
| 61 |
+
|
| 62 |
+
lmsg3 = (
|
| 63 |
+
r"Cannot cast -?10000000000 days \+?00:00:00 to unit='ns' without overflow"
|
| 64 |
+
)
|
| 65 |
+
with pytest.raises(OutOfBoundsTimedelta, match=lmsg3):
|
| 66 |
+
stamp + offset_overflow
|
| 67 |
+
|
| 68 |
+
with pytest.raises(OutOfBoundsTimedelta, match=lmsg3):
|
| 69 |
+
offset_overflow + stamp
|
| 70 |
+
|
| 71 |
+
with pytest.raises(OutOfBoundsTimedelta, match=lmsg3):
|
| 72 |
+
stamp - offset_overflow
|
| 73 |
+
|
| 74 |
+
def test_overflow_timestamp_raises(self):
|
| 75 |
+
# https://github.com/pandas-dev/pandas/issues/31774
|
| 76 |
+
msg = "Result is too large"
|
| 77 |
+
a = Timestamp("2101-01-01 00:00:00").as_unit("ns")
|
| 78 |
+
b = Timestamp("1688-01-01 00:00:00").as_unit("ns")
|
| 79 |
+
|
| 80 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 81 |
+
a - b
|
| 82 |
+
|
| 83 |
+
# but we're OK for timestamp and datetime.datetime
|
| 84 |
+
assert (a - b.to_pydatetime()) == (a.to_pydatetime() - b)
|
| 85 |
+
|
| 86 |
+
def test_delta_preserve_nanos(self):
|
| 87 |
+
val = Timestamp(1337299200000000123)
|
| 88 |
+
result = val + timedelta(1)
|
| 89 |
+
assert result.nanosecond == val.nanosecond
|
| 90 |
+
|
| 91 |
+
def test_rsub_dtscalars(self, tz_naive_fixture):
|
| 92 |
+
# In particular, check that datetime64 - Timestamp works GH#28286
|
| 93 |
+
td = Timedelta(1235345642000)
|
| 94 |
+
ts = Timestamp("2021-01-01", tz=tz_naive_fixture)
|
| 95 |
+
other = ts + td
|
| 96 |
+
|
| 97 |
+
assert other - ts == td
|
| 98 |
+
assert other.to_pydatetime() - ts == td
|
| 99 |
+
if tz_naive_fixture is None:
|
| 100 |
+
assert other.to_datetime64() - ts == td
|
| 101 |
+
else:
|
| 102 |
+
msg = "Cannot subtract tz-naive and tz-aware datetime-like objects"
|
| 103 |
+
with pytest.raises(TypeError, match=msg):
|
| 104 |
+
other.to_datetime64() - ts
|
| 105 |
+
|
| 106 |
+
def test_timestamp_sub_datetime(self):
|
| 107 |
+
dt = datetime(2013, 10, 12)
|
| 108 |
+
ts = Timestamp(datetime(2013, 10, 13))
|
| 109 |
+
assert (ts - dt).days == 1
|
| 110 |
+
assert (dt - ts).days == -1
|
| 111 |
+
|
| 112 |
+
def test_subtract_tzaware_datetime(self):
|
| 113 |
+
t1 = Timestamp("2020-10-22T22:00:00+00:00")
|
| 114 |
+
t2 = datetime(2020, 10, 22, 22, tzinfo=timezone.utc)
|
| 115 |
+
|
| 116 |
+
result = t1 - t2
|
| 117 |
+
|
| 118 |
+
assert isinstance(result, Timedelta)
|
| 119 |
+
assert result == Timedelta("0 days")
|
| 120 |
+
|
| 121 |
+
def test_subtract_timestamp_from_different_timezone(self):
|
| 122 |
+
t1 = Timestamp("20130101").tz_localize("US/Eastern")
|
| 123 |
+
t2 = Timestamp("20130101").tz_localize("CET")
|
| 124 |
+
|
| 125 |
+
result = t1 - t2
|
| 126 |
+
|
| 127 |
+
assert isinstance(result, Timedelta)
|
| 128 |
+
assert result == Timedelta("0 days 06:00:00")
|
| 129 |
+
|
| 130 |
+
def test_subtracting_involving_datetime_with_different_tz(self):
|
| 131 |
+
t1 = datetime(2013, 1, 1, tzinfo=timezone(timedelta(hours=-5)))
|
| 132 |
+
t2 = Timestamp("20130101").tz_localize("CET")
|
| 133 |
+
|
| 134 |
+
result = t1 - t2
|
| 135 |
+
|
| 136 |
+
assert isinstance(result, Timedelta)
|
| 137 |
+
assert result == Timedelta("0 days 06:00:00")
|
| 138 |
+
|
| 139 |
+
result = t2 - t1
|
| 140 |
+
assert isinstance(result, Timedelta)
|
| 141 |
+
assert result == Timedelta("-1 days +18:00:00")
|
| 142 |
+
|
| 143 |
+
def test_subtracting_different_timezones(self, tz_aware_fixture):
|
| 144 |
+
t_raw = Timestamp("20130101")
|
| 145 |
+
t_UTC = t_raw.tz_localize("UTC")
|
| 146 |
+
t_diff = t_UTC.tz_convert(tz_aware_fixture) + Timedelta("0 days 05:00:00")
|
| 147 |
+
|
| 148 |
+
result = t_diff - t_UTC
|
| 149 |
+
|
| 150 |
+
assert isinstance(result, Timedelta)
|
| 151 |
+
assert result == Timedelta("0 days 05:00:00")
|
| 152 |
+
|
| 153 |
+
def test_addition_subtraction_types(self):
|
| 154 |
+
# Assert on the types resulting from Timestamp +/- various date/time
|
| 155 |
+
# objects
|
| 156 |
+
dt = datetime(2014, 3, 4)
|
| 157 |
+
td = timedelta(seconds=1)
|
| 158 |
+
ts = Timestamp(dt)
|
| 159 |
+
|
| 160 |
+
msg = "Addition/subtraction of integers"
|
| 161 |
+
with pytest.raises(TypeError, match=msg):
|
| 162 |
+
# GH#22535 add/sub with integers is deprecated
|
| 163 |
+
ts + 1
|
| 164 |
+
with pytest.raises(TypeError, match=msg):
|
| 165 |
+
ts - 1
|
| 166 |
+
|
| 167 |
+
# Timestamp + datetime not supported, though subtraction is supported
|
| 168 |
+
# and yields timedelta more tests in tseries/base/tests/test_base.py
|
| 169 |
+
assert type(ts - dt) == Timedelta
|
| 170 |
+
assert type(ts + td) == Timestamp
|
| 171 |
+
assert type(ts - td) == Timestamp
|
| 172 |
+
|
| 173 |
+
# Timestamp +/- datetime64 not supported, so not tested (could possibly
|
| 174 |
+
# assert error raised?)
|
| 175 |
+
td64 = np.timedelta64(1, "D")
|
| 176 |
+
assert type(ts + td64) == Timestamp
|
| 177 |
+
assert type(ts - td64) == Timestamp
|
| 178 |
+
|
| 179 |
+
@pytest.mark.parametrize(
|
| 180 |
+
"td", [Timedelta(hours=3), np.timedelta64(3, "h"), timedelta(hours=3)]
|
| 181 |
+
)
|
| 182 |
+
def test_radd_tdscalar(self, td, fixed_now_ts):
|
| 183 |
+
# GH#24775 timedelta64+Timestamp should not raise
|
| 184 |
+
ts = fixed_now_ts
|
| 185 |
+
assert td + ts == ts + td
|
| 186 |
+
|
| 187 |
+
@pytest.mark.parametrize(
|
| 188 |
+
"other,expected_difference",
|
| 189 |
+
[
|
| 190 |
+
(np.timedelta64(-123, "ns"), -123),
|
| 191 |
+
(np.timedelta64(1234567898, "ns"), 1234567898),
|
| 192 |
+
(np.timedelta64(-123, "us"), -123000),
|
| 193 |
+
(np.timedelta64(-123, "ms"), -123000000),
|
| 194 |
+
],
|
| 195 |
+
)
|
| 196 |
+
def test_timestamp_add_timedelta64_unit(self, other, expected_difference):
|
| 197 |
+
now = datetime.now(timezone.utc)
|
| 198 |
+
ts = Timestamp(now).as_unit("ns")
|
| 199 |
+
result = ts + other
|
| 200 |
+
valdiff = result._value - ts._value
|
| 201 |
+
assert valdiff == expected_difference
|
| 202 |
+
|
| 203 |
+
ts2 = Timestamp(now)
|
| 204 |
+
assert ts2 + other == result
|
| 205 |
+
|
| 206 |
+
@pytest.mark.parametrize(
|
| 207 |
+
"ts",
|
| 208 |
+
[
|
| 209 |
+
Timestamp("1776-07-04"),
|
| 210 |
+
Timestamp("1776-07-04", tz="UTC"),
|
| 211 |
+
],
|
| 212 |
+
)
|
| 213 |
+
@pytest.mark.parametrize(
|
| 214 |
+
"other",
|
| 215 |
+
[
|
| 216 |
+
1,
|
| 217 |
+
np.int64(1),
|
| 218 |
+
np.array([1, 2], dtype=np.int32),
|
| 219 |
+
np.array([3, 4], dtype=np.uint64),
|
| 220 |
+
],
|
| 221 |
+
)
|
| 222 |
+
def test_add_int_with_freq(self, ts, other):
|
| 223 |
+
msg = "Addition/subtraction of integers and integer-arrays"
|
| 224 |
+
with pytest.raises(TypeError, match=msg):
|
| 225 |
+
ts + other
|
| 226 |
+
with pytest.raises(TypeError, match=msg):
|
| 227 |
+
other + ts
|
| 228 |
+
|
| 229 |
+
with pytest.raises(TypeError, match=msg):
|
| 230 |
+
ts - other
|
| 231 |
+
|
| 232 |
+
msg = "unsupported operand type"
|
| 233 |
+
with pytest.raises(TypeError, match=msg):
|
| 234 |
+
other - ts
|
| 235 |
+
|
| 236 |
+
@pytest.mark.parametrize("shape", [(6,), (2, 3)])
|
| 237 |
+
def test_addsub_m8ndarray(self, shape):
|
| 238 |
+
# GH#33296
|
| 239 |
+
ts = Timestamp("2020-04-04 15:45").as_unit("ns")
|
| 240 |
+
other = np.arange(6).astype("m8[h]").reshape(shape)
|
| 241 |
+
|
| 242 |
+
result = ts + other
|
| 243 |
+
|
| 244 |
+
ex_stamps = [ts + Timedelta(hours=n) for n in range(6)]
|
| 245 |
+
expected = np.array([x.asm8 for x in ex_stamps], dtype="M8[ns]").reshape(shape)
|
| 246 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 247 |
+
|
| 248 |
+
result = other + ts
|
| 249 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 250 |
+
|
| 251 |
+
result = ts - other
|
| 252 |
+
ex_stamps = [ts - Timedelta(hours=n) for n in range(6)]
|
| 253 |
+
expected = np.array([x.asm8 for x in ex_stamps], dtype="M8[ns]").reshape(shape)
|
| 254 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 255 |
+
|
| 256 |
+
msg = r"unsupported operand type\(s\) for -: 'numpy.ndarray' and 'Timestamp'"
|
| 257 |
+
with pytest.raises(TypeError, match=msg):
|
| 258 |
+
other - ts
|
| 259 |
+
|
| 260 |
+
@pytest.mark.parametrize("shape", [(6,), (2, 3)])
|
| 261 |
+
def test_addsub_m8ndarray_tzaware(self, shape):
|
| 262 |
+
# GH#33296
|
| 263 |
+
ts = Timestamp("2020-04-04 15:45", tz="US/Pacific")
|
| 264 |
+
|
| 265 |
+
other = np.arange(6).astype("m8[h]").reshape(shape)
|
| 266 |
+
|
| 267 |
+
result = ts + other
|
| 268 |
+
|
| 269 |
+
ex_stamps = [ts + Timedelta(hours=n) for n in range(6)]
|
| 270 |
+
expected = np.array(ex_stamps).reshape(shape)
|
| 271 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 272 |
+
|
| 273 |
+
result = other + ts
|
| 274 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 275 |
+
|
| 276 |
+
result = ts - other
|
| 277 |
+
ex_stamps = [ts - Timedelta(hours=n) for n in range(6)]
|
| 278 |
+
expected = np.array(ex_stamps).reshape(shape)
|
| 279 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 280 |
+
|
| 281 |
+
msg = r"unsupported operand type\(s\) for -: 'numpy.ndarray' and 'Timestamp'"
|
| 282 |
+
with pytest.raises(TypeError, match=msg):
|
| 283 |
+
other - ts
|
| 284 |
+
|
| 285 |
+
def test_subtract_different_utc_objects(self, utc_fixture, utc_fixture2):
|
| 286 |
+
# GH 32619
|
| 287 |
+
dt = datetime(2021, 1, 1)
|
| 288 |
+
ts1 = Timestamp(dt, tz=utc_fixture)
|
| 289 |
+
ts2 = Timestamp(dt, tz=utc_fixture2)
|
| 290 |
+
result = ts1 - ts2
|
| 291 |
+
expected = Timedelta(0)
|
| 292 |
+
assert result == expected
|
| 293 |
+
|
| 294 |
+
@pytest.mark.parametrize(
|
| 295 |
+
"tz",
|
| 296 |
+
[
|
| 297 |
+
pytz.timezone("US/Eastern"),
|
| 298 |
+
gettz("US/Eastern"),
|
| 299 |
+
"US/Eastern",
|
| 300 |
+
"dateutil/US/Eastern",
|
| 301 |
+
],
|
| 302 |
+
)
|
| 303 |
+
def test_timestamp_add_timedelta_push_over_dst_boundary(self, tz):
|
| 304 |
+
# GH#1389
|
| 305 |
+
|
| 306 |
+
# 4 hours before DST transition
|
| 307 |
+
stamp = Timestamp("3/10/2012 22:00", tz=tz)
|
| 308 |
+
|
| 309 |
+
result = stamp + timedelta(hours=6)
|
| 310 |
+
|
| 311 |
+
# spring forward, + "7" hours
|
| 312 |
+
expected = Timestamp("3/11/2012 05:00", tz=tz)
|
| 313 |
+
|
| 314 |
+
assert result == expected
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
class SubDatetime(datetime):
|
| 318 |
+
pass
|
| 319 |
+
|
| 320 |
+
|
| 321 |
+
@pytest.mark.parametrize(
|
| 322 |
+
"lh,rh",
|
| 323 |
+
[
|
| 324 |
+
(SubDatetime(2000, 1, 1), Timedelta(hours=1)),
|
| 325 |
+
(Timedelta(hours=1), SubDatetime(2000, 1, 1)),
|
| 326 |
+
],
|
| 327 |
+
)
|
| 328 |
+
def test_dt_subclass_add_timedelta(lh, rh):
|
| 329 |
+
# GH#25851
|
| 330 |
+
# ensure that subclassed datetime works for
|
| 331 |
+
# Timedelta operations
|
| 332 |
+
result = lh + rh
|
| 333 |
+
expected = SubDatetime(2000, 1, 1, 1)
|
| 334 |
+
assert result == expected
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/test_comparisons.py
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import (
|
| 2 |
+
datetime,
|
| 3 |
+
timedelta,
|
| 4 |
+
)
|
| 5 |
+
import operator
|
| 6 |
+
|
| 7 |
+
import numpy as np
|
| 8 |
+
import pytest
|
| 9 |
+
|
| 10 |
+
from pandas import Timestamp
|
| 11 |
+
import pandas._testing as tm
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class TestTimestampComparison:
|
| 15 |
+
def test_compare_non_nano_dt64(self):
|
| 16 |
+
# don't raise when converting dt64 to Timestamp in __richcmp__
|
| 17 |
+
dt = np.datetime64("1066-10-14")
|
| 18 |
+
ts = Timestamp(dt)
|
| 19 |
+
|
| 20 |
+
assert dt == ts
|
| 21 |
+
|
| 22 |
+
def test_comparison_dt64_ndarray(self):
|
| 23 |
+
ts = Timestamp("2021-01-01")
|
| 24 |
+
ts2 = Timestamp("2019-04-05")
|
| 25 |
+
arr = np.array([[ts.asm8, ts2.asm8]], dtype="M8[ns]")
|
| 26 |
+
|
| 27 |
+
result = ts == arr
|
| 28 |
+
expected = np.array([[True, False]], dtype=bool)
|
| 29 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 30 |
+
|
| 31 |
+
result = arr == ts
|
| 32 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 33 |
+
|
| 34 |
+
result = ts != arr
|
| 35 |
+
tm.assert_numpy_array_equal(result, ~expected)
|
| 36 |
+
|
| 37 |
+
result = arr != ts
|
| 38 |
+
tm.assert_numpy_array_equal(result, ~expected)
|
| 39 |
+
|
| 40 |
+
result = ts2 < arr
|
| 41 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 42 |
+
|
| 43 |
+
result = arr < ts2
|
| 44 |
+
tm.assert_numpy_array_equal(result, np.array([[False, False]], dtype=bool))
|
| 45 |
+
|
| 46 |
+
result = ts2 <= arr
|
| 47 |
+
tm.assert_numpy_array_equal(result, np.array([[True, True]], dtype=bool))
|
| 48 |
+
|
| 49 |
+
result = arr <= ts2
|
| 50 |
+
tm.assert_numpy_array_equal(result, ~expected)
|
| 51 |
+
|
| 52 |
+
result = ts >= arr
|
| 53 |
+
tm.assert_numpy_array_equal(result, np.array([[True, True]], dtype=bool))
|
| 54 |
+
|
| 55 |
+
result = arr >= ts
|
| 56 |
+
tm.assert_numpy_array_equal(result, np.array([[True, False]], dtype=bool))
|
| 57 |
+
|
| 58 |
+
@pytest.mark.parametrize("reverse", [True, False])
|
| 59 |
+
def test_comparison_dt64_ndarray_tzaware(self, reverse, comparison_op):
|
| 60 |
+
ts = Timestamp("2021-01-01 00:00:00.00000", tz="UTC")
|
| 61 |
+
arr = np.array([ts.asm8, ts.asm8], dtype="M8[ns]")
|
| 62 |
+
|
| 63 |
+
left, right = ts, arr
|
| 64 |
+
if reverse:
|
| 65 |
+
left, right = arr, ts
|
| 66 |
+
|
| 67 |
+
if comparison_op is operator.eq:
|
| 68 |
+
expected = np.array([False, False], dtype=bool)
|
| 69 |
+
result = comparison_op(left, right)
|
| 70 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 71 |
+
elif comparison_op is operator.ne:
|
| 72 |
+
expected = np.array([True, True], dtype=bool)
|
| 73 |
+
result = comparison_op(left, right)
|
| 74 |
+
tm.assert_numpy_array_equal(result, expected)
|
| 75 |
+
else:
|
| 76 |
+
msg = "Cannot compare tz-naive and tz-aware timestamps"
|
| 77 |
+
with pytest.raises(TypeError, match=msg):
|
| 78 |
+
comparison_op(left, right)
|
| 79 |
+
|
| 80 |
+
def test_comparison_object_array(self):
|
| 81 |
+
# GH#15183
|
| 82 |
+
ts = Timestamp("2011-01-03 00:00:00-0500", tz="US/Eastern")
|
| 83 |
+
other = Timestamp("2011-01-01 00:00:00-0500", tz="US/Eastern")
|
| 84 |
+
naive = Timestamp("2011-01-01 00:00:00")
|
| 85 |
+
|
| 86 |
+
arr = np.array([other, ts], dtype=object)
|
| 87 |
+
res = arr == ts
|
| 88 |
+
expected = np.array([False, True], dtype=bool)
|
| 89 |
+
assert (res == expected).all()
|
| 90 |
+
|
| 91 |
+
# 2D case
|
| 92 |
+
arr = np.array([[other, ts], [ts, other]], dtype=object)
|
| 93 |
+
res = arr != ts
|
| 94 |
+
expected = np.array([[True, False], [False, True]], dtype=bool)
|
| 95 |
+
assert res.shape == expected.shape
|
| 96 |
+
assert (res == expected).all()
|
| 97 |
+
|
| 98 |
+
# tzaware mismatch
|
| 99 |
+
arr = np.array([naive], dtype=object)
|
| 100 |
+
msg = "Cannot compare tz-naive and tz-aware timestamps"
|
| 101 |
+
with pytest.raises(TypeError, match=msg):
|
| 102 |
+
arr < ts
|
| 103 |
+
|
| 104 |
+
def test_comparison(self):
|
| 105 |
+
# 5-18-2012 00:00:00.000
|
| 106 |
+
stamp = 1337299200000000000
|
| 107 |
+
|
| 108 |
+
val = Timestamp(stamp)
|
| 109 |
+
|
| 110 |
+
assert val == val
|
| 111 |
+
assert not val != val
|
| 112 |
+
assert not val < val
|
| 113 |
+
assert val <= val
|
| 114 |
+
assert not val > val
|
| 115 |
+
assert val >= val
|
| 116 |
+
|
| 117 |
+
other = datetime(2012, 5, 18)
|
| 118 |
+
assert val == other
|
| 119 |
+
assert not val != other
|
| 120 |
+
assert not val < other
|
| 121 |
+
assert val <= other
|
| 122 |
+
assert not val > other
|
| 123 |
+
assert val >= other
|
| 124 |
+
|
| 125 |
+
other = Timestamp(stamp + 100)
|
| 126 |
+
|
| 127 |
+
assert val != other
|
| 128 |
+
assert val != other
|
| 129 |
+
assert val < other
|
| 130 |
+
assert val <= other
|
| 131 |
+
assert other > val
|
| 132 |
+
assert other >= val
|
| 133 |
+
|
| 134 |
+
def test_compare_invalid(self):
|
| 135 |
+
# GH#8058
|
| 136 |
+
val = Timestamp("20130101 12:01:02")
|
| 137 |
+
assert not val == "foo"
|
| 138 |
+
assert not val == 10.0
|
| 139 |
+
assert not val == 1
|
| 140 |
+
assert not val == []
|
| 141 |
+
assert not val == {"foo": 1}
|
| 142 |
+
assert not val == np.float64(1)
|
| 143 |
+
assert not val == np.int64(1)
|
| 144 |
+
|
| 145 |
+
assert val != "foo"
|
| 146 |
+
assert val != 10.0
|
| 147 |
+
assert val != 1
|
| 148 |
+
assert val != []
|
| 149 |
+
assert val != {"foo": 1}
|
| 150 |
+
assert val != np.float64(1)
|
| 151 |
+
assert val != np.int64(1)
|
| 152 |
+
|
| 153 |
+
@pytest.mark.parametrize("tz", [None, "US/Pacific"])
|
| 154 |
+
def test_compare_date(self, tz):
|
| 155 |
+
# GH#36131 comparing Timestamp with date object is deprecated
|
| 156 |
+
ts = Timestamp("2021-01-01 00:00:00.00000", tz=tz)
|
| 157 |
+
dt = ts.to_pydatetime().date()
|
| 158 |
+
# in 2.0 we disallow comparing pydate objects with Timestamps,
|
| 159 |
+
# following the stdlib datetime behavior.
|
| 160 |
+
|
| 161 |
+
msg = "Cannot compare Timestamp with datetime.date"
|
| 162 |
+
for left, right in [(ts, dt), (dt, ts)]:
|
| 163 |
+
assert not left == right
|
| 164 |
+
assert left != right
|
| 165 |
+
|
| 166 |
+
with pytest.raises(TypeError, match=msg):
|
| 167 |
+
left < right
|
| 168 |
+
with pytest.raises(TypeError, match=msg):
|
| 169 |
+
left <= right
|
| 170 |
+
with pytest.raises(TypeError, match=msg):
|
| 171 |
+
left > right
|
| 172 |
+
with pytest.raises(TypeError, match=msg):
|
| 173 |
+
left >= right
|
| 174 |
+
|
| 175 |
+
def test_cant_compare_tz_naive_w_aware(self, utc_fixture):
|
| 176 |
+
# see GH#1404
|
| 177 |
+
a = Timestamp("3/12/2012")
|
| 178 |
+
b = Timestamp("3/12/2012", tz=utc_fixture)
|
| 179 |
+
|
| 180 |
+
msg = "Cannot compare tz-naive and tz-aware timestamps"
|
| 181 |
+
assert not a == b
|
| 182 |
+
assert a != b
|
| 183 |
+
with pytest.raises(TypeError, match=msg):
|
| 184 |
+
a < b
|
| 185 |
+
with pytest.raises(TypeError, match=msg):
|
| 186 |
+
a <= b
|
| 187 |
+
with pytest.raises(TypeError, match=msg):
|
| 188 |
+
a > b
|
| 189 |
+
with pytest.raises(TypeError, match=msg):
|
| 190 |
+
a >= b
|
| 191 |
+
|
| 192 |
+
assert not b == a
|
| 193 |
+
assert b != a
|
| 194 |
+
with pytest.raises(TypeError, match=msg):
|
| 195 |
+
b < a
|
| 196 |
+
with pytest.raises(TypeError, match=msg):
|
| 197 |
+
b <= a
|
| 198 |
+
with pytest.raises(TypeError, match=msg):
|
| 199 |
+
b > a
|
| 200 |
+
with pytest.raises(TypeError, match=msg):
|
| 201 |
+
b >= a
|
| 202 |
+
|
| 203 |
+
assert not a == b.to_pydatetime()
|
| 204 |
+
assert not a.to_pydatetime() == b
|
| 205 |
+
|
| 206 |
+
def test_timestamp_compare_scalars(self):
|
| 207 |
+
# case where ndim == 0
|
| 208 |
+
lhs = np.datetime64(datetime(2013, 12, 6))
|
| 209 |
+
rhs = Timestamp("now")
|
| 210 |
+
nat = Timestamp("nat")
|
| 211 |
+
|
| 212 |
+
ops = {"gt": "lt", "lt": "gt", "ge": "le", "le": "ge", "eq": "eq", "ne": "ne"}
|
| 213 |
+
|
| 214 |
+
for left, right in ops.items():
|
| 215 |
+
left_f = getattr(operator, left)
|
| 216 |
+
right_f = getattr(operator, right)
|
| 217 |
+
expected = left_f(lhs, rhs)
|
| 218 |
+
|
| 219 |
+
result = right_f(rhs, lhs)
|
| 220 |
+
assert result == expected
|
| 221 |
+
|
| 222 |
+
expected = left_f(rhs, nat)
|
| 223 |
+
result = right_f(nat, rhs)
|
| 224 |
+
assert result == expected
|
| 225 |
+
|
| 226 |
+
def test_timestamp_compare_with_early_datetime(self):
|
| 227 |
+
# e.g. datetime.min
|
| 228 |
+
stamp = Timestamp("2012-01-01")
|
| 229 |
+
|
| 230 |
+
assert not stamp == datetime.min
|
| 231 |
+
assert not stamp == datetime(1600, 1, 1)
|
| 232 |
+
assert not stamp == datetime(2700, 1, 1)
|
| 233 |
+
assert stamp != datetime.min
|
| 234 |
+
assert stamp != datetime(1600, 1, 1)
|
| 235 |
+
assert stamp != datetime(2700, 1, 1)
|
| 236 |
+
assert stamp > datetime(1600, 1, 1)
|
| 237 |
+
assert stamp >= datetime(1600, 1, 1)
|
| 238 |
+
assert stamp < datetime(2700, 1, 1)
|
| 239 |
+
assert stamp <= datetime(2700, 1, 1)
|
| 240 |
+
|
| 241 |
+
other = Timestamp.min.to_pydatetime(warn=False)
|
| 242 |
+
assert other - timedelta(microseconds=1) < Timestamp.min
|
| 243 |
+
|
| 244 |
+
def test_timestamp_compare_oob_dt64(self):
|
| 245 |
+
us = np.timedelta64(1, "us")
|
| 246 |
+
other = np.datetime64(Timestamp.min).astype("M8[us]")
|
| 247 |
+
|
| 248 |
+
# This may change if the implementation bound is dropped to match
|
| 249 |
+
# DatetimeArray/DatetimeIndex GH#24124
|
| 250 |
+
assert Timestamp.min > other
|
| 251 |
+
# Note: numpy gets the reversed comparison wrong
|
| 252 |
+
|
| 253 |
+
other = np.datetime64(Timestamp.max).astype("M8[us]")
|
| 254 |
+
assert Timestamp.max > other # not actually OOB
|
| 255 |
+
assert other < Timestamp.max
|
| 256 |
+
|
| 257 |
+
assert Timestamp.max < other + us
|
| 258 |
+
# Note: numpy gets the reversed comparison wrong
|
| 259 |
+
|
| 260 |
+
# GH-42794
|
| 261 |
+
other = datetime(9999, 9, 9)
|
| 262 |
+
assert Timestamp.min < other
|
| 263 |
+
assert other > Timestamp.min
|
| 264 |
+
assert Timestamp.max < other
|
| 265 |
+
assert other > Timestamp.max
|
| 266 |
+
|
| 267 |
+
other = datetime(1, 1, 1)
|
| 268 |
+
assert Timestamp.max > other
|
| 269 |
+
assert other < Timestamp.max
|
| 270 |
+
assert Timestamp.min > other
|
| 271 |
+
assert other < Timestamp.min
|
| 272 |
+
|
| 273 |
+
def test_compare_zerodim_array(self, fixed_now_ts):
|
| 274 |
+
# GH#26916
|
| 275 |
+
ts = fixed_now_ts
|
| 276 |
+
dt64 = np.datetime64("2016-01-01", "ns")
|
| 277 |
+
arr = np.array(dt64)
|
| 278 |
+
assert arr.ndim == 0
|
| 279 |
+
|
| 280 |
+
result = arr < ts
|
| 281 |
+
assert result is np.bool_(True)
|
| 282 |
+
result = arr > ts
|
| 283 |
+
assert result is np.bool_(False)
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
def test_rich_comparison_with_unsupported_type():
|
| 287 |
+
# Comparisons with unsupported objects should return NotImplemented
|
| 288 |
+
# (it previously raised TypeError, see #24011)
|
| 289 |
+
|
| 290 |
+
class Inf:
|
| 291 |
+
def __lt__(self, o):
|
| 292 |
+
return False
|
| 293 |
+
|
| 294 |
+
def __le__(self, o):
|
| 295 |
+
return isinstance(o, Inf)
|
| 296 |
+
|
| 297 |
+
def __gt__(self, o):
|
| 298 |
+
return not isinstance(o, Inf)
|
| 299 |
+
|
| 300 |
+
def __ge__(self, o):
|
| 301 |
+
return True
|
| 302 |
+
|
| 303 |
+
def __eq__(self, other) -> bool:
|
| 304 |
+
return isinstance(other, Inf)
|
| 305 |
+
|
| 306 |
+
inf = Inf()
|
| 307 |
+
timestamp = Timestamp("2018-11-30")
|
| 308 |
+
|
| 309 |
+
for left, right in [(inf, timestamp), (timestamp, inf)]:
|
| 310 |
+
assert left > right or left < right
|
| 311 |
+
assert left >= right or left <= right
|
| 312 |
+
assert not left == right # pylint: disable=unneeded-not
|
| 313 |
+
assert left != right
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/test_constructors.py
ADDED
|
@@ -0,0 +1,1077 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import calendar
|
| 2 |
+
from datetime import (
|
| 3 |
+
date,
|
| 4 |
+
datetime,
|
| 5 |
+
timedelta,
|
| 6 |
+
timezone,
|
| 7 |
+
)
|
| 8 |
+
import zoneinfo
|
| 9 |
+
|
| 10 |
+
import dateutil.tz
|
| 11 |
+
from dateutil.tz import (
|
| 12 |
+
gettz,
|
| 13 |
+
tzoffset,
|
| 14 |
+
tzutc,
|
| 15 |
+
)
|
| 16 |
+
import numpy as np
|
| 17 |
+
import pytest
|
| 18 |
+
import pytz
|
| 19 |
+
|
| 20 |
+
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
| 21 |
+
from pandas.compat import (
|
| 22 |
+
PY310,
|
| 23 |
+
PY314,
|
| 24 |
+
)
|
| 25 |
+
from pandas.errors import OutOfBoundsDatetime
|
| 26 |
+
|
| 27 |
+
from pandas import (
|
| 28 |
+
NA,
|
| 29 |
+
NaT,
|
| 30 |
+
Period,
|
| 31 |
+
Timedelta,
|
| 32 |
+
Timestamp,
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class TestTimestampConstructorUnitKeyword:
|
| 37 |
+
@pytest.mark.parametrize("typ", [int, float])
|
| 38 |
+
def test_constructor_int_float_with_YM_unit(self, typ):
|
| 39 |
+
# GH#47266 avoid the conversions in cast_from_unit
|
| 40 |
+
val = typ(150)
|
| 41 |
+
|
| 42 |
+
ts = Timestamp(val, unit="Y")
|
| 43 |
+
expected = Timestamp("2120-01-01")
|
| 44 |
+
assert ts == expected
|
| 45 |
+
|
| 46 |
+
ts = Timestamp(val, unit="M")
|
| 47 |
+
expected = Timestamp("1982-07-01")
|
| 48 |
+
assert ts == expected
|
| 49 |
+
|
| 50 |
+
@pytest.mark.parametrize("typ", [int, float])
|
| 51 |
+
def test_construct_from_int_float_with_unit_out_of_bound_raises(self, typ):
|
| 52 |
+
# GH#50870 make sure we get a OutOfBoundsDatetime instead of OverflowError
|
| 53 |
+
val = typ(150000000000000)
|
| 54 |
+
|
| 55 |
+
msg = f"cannot convert input {val} with the unit 'D'"
|
| 56 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 57 |
+
Timestamp(val, unit="D")
|
| 58 |
+
|
| 59 |
+
def test_constructor_float_not_round_with_YM_unit_raises(self):
|
| 60 |
+
# GH#47267 avoid the conversions in cast_from-unit
|
| 61 |
+
|
| 62 |
+
msg = "Conversion of non-round float with unit=[MY] is ambiguous"
|
| 63 |
+
with pytest.raises(ValueError, match=msg):
|
| 64 |
+
Timestamp(150.5, unit="Y")
|
| 65 |
+
|
| 66 |
+
with pytest.raises(ValueError, match=msg):
|
| 67 |
+
Timestamp(150.5, unit="M")
|
| 68 |
+
|
| 69 |
+
@pytest.mark.parametrize(
|
| 70 |
+
"value, check_kwargs",
|
| 71 |
+
[
|
| 72 |
+
[946688461000000000, {}],
|
| 73 |
+
[946688461000000000 / 1000, {"unit": "us"}],
|
| 74 |
+
[946688461000000000 / 1_000_000, {"unit": "ms"}],
|
| 75 |
+
[946688461000000000 / 1_000_000_000, {"unit": "s"}],
|
| 76 |
+
[10957, {"unit": "D", "h": 0}],
|
| 77 |
+
[
|
| 78 |
+
(946688461000000000 + 500000) / 1000000000,
|
| 79 |
+
{"unit": "s", "us": 499, "ns": 964},
|
| 80 |
+
],
|
| 81 |
+
[
|
| 82 |
+
(946688461000000000 + 500000000) / 1000000000,
|
| 83 |
+
{"unit": "s", "us": 500000},
|
| 84 |
+
],
|
| 85 |
+
[(946688461000000000 + 500000) / 1000000, {"unit": "ms", "us": 500}],
|
| 86 |
+
[(946688461000000000 + 500000) / 1000, {"unit": "us", "us": 500}],
|
| 87 |
+
[(946688461000000000 + 500000000) / 1000000, {"unit": "ms", "us": 500000}],
|
| 88 |
+
[946688461000000000 / 1000.0 + 5, {"unit": "us", "us": 5}],
|
| 89 |
+
[946688461000000000 / 1000.0 + 5000, {"unit": "us", "us": 5000}],
|
| 90 |
+
[946688461000000000 / 1000000.0 + 0.5, {"unit": "ms", "us": 500}],
|
| 91 |
+
[946688461000000000 / 1000000.0 + 0.005, {"unit": "ms", "us": 5, "ns": 5}],
|
| 92 |
+
[946688461000000000 / 1000000000.0 + 0.5, {"unit": "s", "us": 500000}],
|
| 93 |
+
[10957 + 0.5, {"unit": "D", "h": 12}],
|
| 94 |
+
],
|
| 95 |
+
)
|
| 96 |
+
def test_construct_with_unit(self, value, check_kwargs):
|
| 97 |
+
def check(value, unit=None, h=1, s=1, us=0, ns=0):
|
| 98 |
+
stamp = Timestamp(value, unit=unit)
|
| 99 |
+
assert stamp.year == 2000
|
| 100 |
+
assert stamp.month == 1
|
| 101 |
+
assert stamp.day == 1
|
| 102 |
+
assert stamp.hour == h
|
| 103 |
+
if unit != "D":
|
| 104 |
+
assert stamp.minute == 1
|
| 105 |
+
assert stamp.second == s
|
| 106 |
+
assert stamp.microsecond == us
|
| 107 |
+
else:
|
| 108 |
+
assert stamp.minute == 0
|
| 109 |
+
assert stamp.second == 0
|
| 110 |
+
assert stamp.microsecond == 0
|
| 111 |
+
assert stamp.nanosecond == ns
|
| 112 |
+
|
| 113 |
+
check(value, **check_kwargs)
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
class TestTimestampConstructorFoldKeyword:
|
| 117 |
+
def test_timestamp_constructor_invalid_fold_raise(self):
|
| 118 |
+
# Test for GH#25057
|
| 119 |
+
# Valid fold values are only [None, 0, 1]
|
| 120 |
+
msg = "Valid values for the fold argument are None, 0, or 1."
|
| 121 |
+
with pytest.raises(ValueError, match=msg):
|
| 122 |
+
Timestamp(123, fold=2)
|
| 123 |
+
|
| 124 |
+
def test_timestamp_constructor_pytz_fold_raise(self):
|
| 125 |
+
# Test for GH#25057
|
| 126 |
+
# pytz doesn't support fold. Check that we raise
|
| 127 |
+
# if fold is passed with pytz
|
| 128 |
+
msg = "pytz timezones do not support fold. Please use dateutil timezones."
|
| 129 |
+
tz = pytz.timezone("Europe/London")
|
| 130 |
+
with pytest.raises(ValueError, match=msg):
|
| 131 |
+
Timestamp(datetime(2019, 10, 27, 0, 30, 0, 0), tz=tz, fold=0)
|
| 132 |
+
|
| 133 |
+
@pytest.mark.parametrize("fold", [0, 1])
|
| 134 |
+
@pytest.mark.parametrize(
|
| 135 |
+
"ts_input",
|
| 136 |
+
[
|
| 137 |
+
1572136200000000000,
|
| 138 |
+
1572136200000000000.0,
|
| 139 |
+
np.datetime64(1572136200000000000, "ns"),
|
| 140 |
+
"2019-10-27 01:30:00+01:00",
|
| 141 |
+
datetime(2019, 10, 27, 0, 30, 0, 0, tzinfo=timezone.utc),
|
| 142 |
+
],
|
| 143 |
+
)
|
| 144 |
+
def test_timestamp_constructor_fold_conflict(self, ts_input, fold):
|
| 145 |
+
# Test for GH#25057
|
| 146 |
+
# Check that we raise on fold conflict
|
| 147 |
+
msg = (
|
| 148 |
+
"Cannot pass fold with possibly unambiguous input: int, float, "
|
| 149 |
+
"numpy.datetime64, str, or timezone-aware datetime-like. "
|
| 150 |
+
"Pass naive datetime-like or build Timestamp from components."
|
| 151 |
+
)
|
| 152 |
+
with pytest.raises(ValueError, match=msg):
|
| 153 |
+
Timestamp(ts_input=ts_input, fold=fold)
|
| 154 |
+
|
| 155 |
+
@pytest.mark.parametrize("tz", ["dateutil/Europe/London", None])
|
| 156 |
+
@pytest.mark.parametrize("fold", [0, 1])
|
| 157 |
+
def test_timestamp_constructor_retain_fold(self, tz, fold):
|
| 158 |
+
# Test for GH#25057
|
| 159 |
+
# Check that we retain fold
|
| 160 |
+
ts = Timestamp(year=2019, month=10, day=27, hour=1, minute=30, tz=tz, fold=fold)
|
| 161 |
+
result = ts.fold
|
| 162 |
+
expected = fold
|
| 163 |
+
assert result == expected
|
| 164 |
+
|
| 165 |
+
try:
|
| 166 |
+
_tzs = [
|
| 167 |
+
"dateutil/Europe/London",
|
| 168 |
+
zoneinfo.ZoneInfo("Europe/London"),
|
| 169 |
+
]
|
| 170 |
+
except zoneinfo.ZoneInfoNotFoundError:
|
| 171 |
+
_tzs = ["dateutil/Europe/London"]
|
| 172 |
+
|
| 173 |
+
@pytest.mark.parametrize("tz", _tzs)
|
| 174 |
+
@pytest.mark.parametrize(
|
| 175 |
+
"ts_input,fold_out",
|
| 176 |
+
[
|
| 177 |
+
(1572136200000000000, 0),
|
| 178 |
+
(1572139800000000000, 1),
|
| 179 |
+
("2019-10-27 01:30:00+01:00", 0),
|
| 180 |
+
("2019-10-27 01:30:00+00:00", 1),
|
| 181 |
+
(datetime(2019, 10, 27, 1, 30, 0, 0, fold=0), 0),
|
| 182 |
+
(datetime(2019, 10, 27, 1, 30, 0, 0, fold=1), 1),
|
| 183 |
+
],
|
| 184 |
+
)
|
| 185 |
+
def test_timestamp_constructor_infer_fold_from_value(self, tz, ts_input, fold_out):
|
| 186 |
+
# Test for GH#25057
|
| 187 |
+
# Check that we infer fold correctly based on timestamps since utc
|
| 188 |
+
# or strings
|
| 189 |
+
ts = Timestamp(ts_input, tz=tz)
|
| 190 |
+
result = ts.fold
|
| 191 |
+
expected = fold_out
|
| 192 |
+
assert result == expected
|
| 193 |
+
|
| 194 |
+
@pytest.mark.parametrize("tz", ["dateutil/Europe/London"])
|
| 195 |
+
@pytest.mark.parametrize(
|
| 196 |
+
"ts_input,fold,value_out",
|
| 197 |
+
[
|
| 198 |
+
(datetime(2019, 10, 27, 1, 30, 0, 0), 0, 1572136200000000),
|
| 199 |
+
(datetime(2019, 10, 27, 1, 30, 0, 0), 1, 1572139800000000),
|
| 200 |
+
],
|
| 201 |
+
)
|
| 202 |
+
def test_timestamp_constructor_adjust_value_for_fold(
|
| 203 |
+
self, tz, ts_input, fold, value_out
|
| 204 |
+
):
|
| 205 |
+
# Test for GH#25057
|
| 206 |
+
# Check that we adjust value for fold correctly
|
| 207 |
+
# based on timestamps since utc
|
| 208 |
+
ts = Timestamp(ts_input, tz=tz, fold=fold)
|
| 209 |
+
result = ts._value
|
| 210 |
+
expected = value_out
|
| 211 |
+
assert result == expected
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
class TestTimestampConstructorPositionalAndKeywordSupport:
|
| 215 |
+
def test_constructor_positional(self):
|
| 216 |
+
# see GH#10758
|
| 217 |
+
msg = (
|
| 218 |
+
"'NoneType' object cannot be interpreted as an integer"
|
| 219 |
+
if PY310
|
| 220 |
+
else "an integer is required"
|
| 221 |
+
)
|
| 222 |
+
with pytest.raises(TypeError, match=msg):
|
| 223 |
+
Timestamp(2000, 1)
|
| 224 |
+
|
| 225 |
+
msg = "month must be in 1..12"
|
| 226 |
+
with pytest.raises(ValueError, match=msg):
|
| 227 |
+
Timestamp(2000, 0, 1)
|
| 228 |
+
with pytest.raises(ValueError, match=msg):
|
| 229 |
+
Timestamp(2000, 13, 1)
|
| 230 |
+
|
| 231 |
+
if PY314:
|
| 232 |
+
msg = "must be in range 1..31 for month 1 in year 2000"
|
| 233 |
+
else:
|
| 234 |
+
msg = "day is out of range for month"
|
| 235 |
+
with pytest.raises(ValueError, match=msg):
|
| 236 |
+
Timestamp(2000, 1, 0)
|
| 237 |
+
with pytest.raises(ValueError, match=msg):
|
| 238 |
+
Timestamp(2000, 1, 32)
|
| 239 |
+
|
| 240 |
+
# see gh-11630
|
| 241 |
+
assert repr(Timestamp(2015, 11, 12)) == repr(Timestamp("20151112"))
|
| 242 |
+
assert repr(Timestamp(2015, 11, 12, 1, 2, 3, 999999)) == repr(
|
| 243 |
+
Timestamp("2015-11-12 01:02:03.999999")
|
| 244 |
+
)
|
| 245 |
+
|
| 246 |
+
def test_constructor_keyword(self):
|
| 247 |
+
# GH#10758
|
| 248 |
+
msg = "function missing required argument 'day'|Required argument 'day'"
|
| 249 |
+
with pytest.raises(TypeError, match=msg):
|
| 250 |
+
Timestamp(year=2000, month=1)
|
| 251 |
+
|
| 252 |
+
msg = "month must be in 1..12"
|
| 253 |
+
with pytest.raises(ValueError, match=msg):
|
| 254 |
+
Timestamp(year=2000, month=0, day=1)
|
| 255 |
+
with pytest.raises(ValueError, match=msg):
|
| 256 |
+
Timestamp(year=2000, month=13, day=1)
|
| 257 |
+
|
| 258 |
+
if PY314:
|
| 259 |
+
msg = "must be in range 1..31 for month 1 in year 2000"
|
| 260 |
+
else:
|
| 261 |
+
msg = "day is out of range for month"
|
| 262 |
+
with pytest.raises(ValueError, match=msg):
|
| 263 |
+
Timestamp(year=2000, month=1, day=0)
|
| 264 |
+
with pytest.raises(ValueError, match=msg):
|
| 265 |
+
Timestamp(year=2000, month=1, day=32)
|
| 266 |
+
|
| 267 |
+
assert repr(Timestamp(year=2015, month=11, day=12)) == repr(
|
| 268 |
+
Timestamp("20151112")
|
| 269 |
+
)
|
| 270 |
+
|
| 271 |
+
assert repr(
|
| 272 |
+
Timestamp(
|
| 273 |
+
year=2015,
|
| 274 |
+
month=11,
|
| 275 |
+
day=12,
|
| 276 |
+
hour=1,
|
| 277 |
+
minute=2,
|
| 278 |
+
second=3,
|
| 279 |
+
microsecond=999999,
|
| 280 |
+
)
|
| 281 |
+
) == repr(Timestamp("2015-11-12 01:02:03.999999"))
|
| 282 |
+
|
| 283 |
+
@pytest.mark.parametrize(
|
| 284 |
+
"arg",
|
| 285 |
+
[
|
| 286 |
+
"year",
|
| 287 |
+
"month",
|
| 288 |
+
"day",
|
| 289 |
+
"hour",
|
| 290 |
+
"minute",
|
| 291 |
+
"second",
|
| 292 |
+
"microsecond",
|
| 293 |
+
"nanosecond",
|
| 294 |
+
],
|
| 295 |
+
)
|
| 296 |
+
def test_invalid_date_kwarg_with_string_input(self, arg):
|
| 297 |
+
kwarg = {arg: 1}
|
| 298 |
+
msg = "Cannot pass a date attribute keyword argument"
|
| 299 |
+
with pytest.raises(ValueError, match=msg):
|
| 300 |
+
Timestamp("2010-10-10 12:59:59.999999999", **kwarg)
|
| 301 |
+
|
| 302 |
+
@pytest.mark.parametrize("kwargs", [{}, {"year": 2020}, {"year": 2020, "month": 1}])
|
| 303 |
+
def test_constructor_missing_keyword(self, kwargs):
|
| 304 |
+
# GH#31200
|
| 305 |
+
|
| 306 |
+
# The exact error message of datetime() depends on its version
|
| 307 |
+
msg1 = r"function missing required argument '(year|month|day)' \(pos [123]\)"
|
| 308 |
+
msg2 = r"Required argument '(year|month|day)' \(pos [123]\) not found"
|
| 309 |
+
msg = "|".join([msg1, msg2])
|
| 310 |
+
|
| 311 |
+
with pytest.raises(TypeError, match=msg):
|
| 312 |
+
Timestamp(**kwargs)
|
| 313 |
+
|
| 314 |
+
def test_constructor_positional_with_tzinfo(self):
|
| 315 |
+
# GH#31929
|
| 316 |
+
ts = Timestamp(2020, 12, 31, tzinfo=timezone.utc)
|
| 317 |
+
expected = Timestamp("2020-12-31", tzinfo=timezone.utc)
|
| 318 |
+
assert ts == expected
|
| 319 |
+
|
| 320 |
+
@pytest.mark.parametrize("kwd", ["nanosecond", "microsecond", "second", "minute"])
|
| 321 |
+
def test_constructor_positional_keyword_mixed_with_tzinfo(self, kwd, request):
|
| 322 |
+
# TODO: if we passed microsecond with a keyword we would mess up
|
| 323 |
+
# xref GH#45307
|
| 324 |
+
if kwd != "nanosecond":
|
| 325 |
+
# nanosecond is keyword-only as of 2.0, others are not
|
| 326 |
+
mark = pytest.mark.xfail(reason="GH#45307")
|
| 327 |
+
request.applymarker(mark)
|
| 328 |
+
|
| 329 |
+
kwargs = {kwd: 4}
|
| 330 |
+
ts = Timestamp(2020, 12, 31, tzinfo=timezone.utc, **kwargs)
|
| 331 |
+
|
| 332 |
+
td_kwargs = {kwd + "s": 4}
|
| 333 |
+
td = Timedelta(**td_kwargs)
|
| 334 |
+
expected = Timestamp("2020-12-31", tz=timezone.utc) + td
|
| 335 |
+
assert ts == expected
|
| 336 |
+
|
| 337 |
+
|
| 338 |
+
class TestTimestampClassMethodConstructors:
|
| 339 |
+
# Timestamp constructors other than __new__
|
| 340 |
+
|
| 341 |
+
def test_constructor_strptime(self):
|
| 342 |
+
# GH#25016
|
| 343 |
+
# Test support for Timestamp.strptime
|
| 344 |
+
fmt = "%Y%m%d-%H%M%S-%f%z"
|
| 345 |
+
ts = "20190129-235348-000001+0000"
|
| 346 |
+
msg = r"Timestamp.strptime\(\) is not implemented"
|
| 347 |
+
with pytest.raises(NotImplementedError, match=msg):
|
| 348 |
+
Timestamp.strptime(ts, fmt)
|
| 349 |
+
|
| 350 |
+
def test_constructor_fromisocalendar(self):
|
| 351 |
+
# GH#30395
|
| 352 |
+
expected_timestamp = Timestamp("2000-01-03 00:00:00")
|
| 353 |
+
expected_stdlib = datetime.fromisocalendar(2000, 1, 1)
|
| 354 |
+
result = Timestamp.fromisocalendar(2000, 1, 1)
|
| 355 |
+
assert result == expected_timestamp
|
| 356 |
+
assert result == expected_stdlib
|
| 357 |
+
assert isinstance(result, Timestamp)
|
| 358 |
+
|
| 359 |
+
def test_constructor_fromordinal(self):
|
| 360 |
+
base = datetime(2000, 1, 1)
|
| 361 |
+
|
| 362 |
+
ts = Timestamp.fromordinal(base.toordinal())
|
| 363 |
+
assert base == ts
|
| 364 |
+
assert base.toordinal() == ts.toordinal()
|
| 365 |
+
|
| 366 |
+
ts = Timestamp.fromordinal(base.toordinal(), tz="US/Eastern")
|
| 367 |
+
assert Timestamp("2000-01-01", tz="US/Eastern") == ts
|
| 368 |
+
assert base.toordinal() == ts.toordinal()
|
| 369 |
+
|
| 370 |
+
# GH#3042
|
| 371 |
+
dt = datetime(2011, 4, 16, 0, 0)
|
| 372 |
+
ts = Timestamp.fromordinal(dt.toordinal())
|
| 373 |
+
assert ts.to_pydatetime() == dt
|
| 374 |
+
|
| 375 |
+
# with a tzinfo
|
| 376 |
+
stamp = Timestamp("2011-4-16", tz="US/Eastern")
|
| 377 |
+
dt_tz = stamp.to_pydatetime()
|
| 378 |
+
ts = Timestamp.fromordinal(dt_tz.toordinal(), tz="US/Eastern")
|
| 379 |
+
assert ts.to_pydatetime() == dt_tz
|
| 380 |
+
|
| 381 |
+
def test_now(self):
|
| 382 |
+
# GH#9000
|
| 383 |
+
ts_from_string = Timestamp("now")
|
| 384 |
+
ts_from_method = Timestamp.now()
|
| 385 |
+
ts_datetime = datetime.now()
|
| 386 |
+
|
| 387 |
+
ts_from_string_tz = Timestamp("now", tz="US/Eastern")
|
| 388 |
+
ts_from_method_tz = Timestamp.now(tz="US/Eastern")
|
| 389 |
+
|
| 390 |
+
# Check that the delta between the times is less than 1s (arbitrarily
|
| 391 |
+
# small)
|
| 392 |
+
delta = Timedelta(seconds=1)
|
| 393 |
+
assert abs(ts_from_method - ts_from_string) < delta
|
| 394 |
+
assert abs(ts_datetime - ts_from_method) < delta
|
| 395 |
+
assert abs(ts_from_method_tz - ts_from_string_tz) < delta
|
| 396 |
+
assert (
|
| 397 |
+
abs(
|
| 398 |
+
ts_from_string_tz.tz_localize(None)
|
| 399 |
+
- ts_from_method_tz.tz_localize(None)
|
| 400 |
+
)
|
| 401 |
+
< delta
|
| 402 |
+
)
|
| 403 |
+
|
| 404 |
+
def test_today(self):
|
| 405 |
+
ts_from_string = Timestamp("today")
|
| 406 |
+
ts_from_method = Timestamp.today()
|
| 407 |
+
ts_datetime = datetime.today()
|
| 408 |
+
|
| 409 |
+
ts_from_string_tz = Timestamp("today", tz="US/Eastern")
|
| 410 |
+
ts_from_method_tz = Timestamp.today(tz="US/Eastern")
|
| 411 |
+
|
| 412 |
+
# Check that the delta between the times is less than 1s (arbitrarily
|
| 413 |
+
# small)
|
| 414 |
+
delta = Timedelta(seconds=1)
|
| 415 |
+
assert abs(ts_from_method - ts_from_string) < delta
|
| 416 |
+
assert abs(ts_datetime - ts_from_method) < delta
|
| 417 |
+
assert abs(ts_from_method_tz - ts_from_string_tz) < delta
|
| 418 |
+
assert (
|
| 419 |
+
abs(
|
| 420 |
+
ts_from_string_tz.tz_localize(None)
|
| 421 |
+
- ts_from_method_tz.tz_localize(None)
|
| 422 |
+
)
|
| 423 |
+
< delta
|
| 424 |
+
)
|
| 425 |
+
|
| 426 |
+
|
| 427 |
+
class TestTimestampResolutionInference:
|
| 428 |
+
def test_construct_from_time_unit(self):
|
| 429 |
+
# GH#54097 only passing a time component, no date
|
| 430 |
+
ts = Timestamp("01:01:01.111")
|
| 431 |
+
assert ts.unit == "ms"
|
| 432 |
+
|
| 433 |
+
def test_constructor_str_infer_reso(self):
|
| 434 |
+
# non-iso8601 path
|
| 435 |
+
|
| 436 |
+
# _parse_delimited_date path
|
| 437 |
+
ts = Timestamp("01/30/2023")
|
| 438 |
+
assert ts.unit == "s"
|
| 439 |
+
|
| 440 |
+
# _parse_dateabbr_string path
|
| 441 |
+
ts = Timestamp("2015Q1")
|
| 442 |
+
assert ts.unit == "s"
|
| 443 |
+
|
| 444 |
+
# dateutil_parse path
|
| 445 |
+
ts = Timestamp("2016-01-01 1:30:01 PM")
|
| 446 |
+
assert ts.unit == "s"
|
| 447 |
+
|
| 448 |
+
ts = Timestamp("2016 June 3 15:25:01.345")
|
| 449 |
+
assert ts.unit == "ms"
|
| 450 |
+
|
| 451 |
+
ts = Timestamp("300-01-01")
|
| 452 |
+
assert ts.unit == "s"
|
| 453 |
+
|
| 454 |
+
ts = Timestamp("300 June 1:30:01.300")
|
| 455 |
+
assert ts.unit == "ms"
|
| 456 |
+
|
| 457 |
+
# dateutil path -> don't drop trailing zeros
|
| 458 |
+
ts = Timestamp("01-01-2013T00:00:00.000000000+0000")
|
| 459 |
+
assert ts.unit == "ns"
|
| 460 |
+
|
| 461 |
+
ts = Timestamp("2016/01/02 03:04:05.001000 UTC")
|
| 462 |
+
assert ts.unit == "us"
|
| 463 |
+
|
| 464 |
+
# higher-than-nanosecond -> we drop the trailing bits
|
| 465 |
+
ts = Timestamp("01-01-2013T00:00:00.000000002100+0000")
|
| 466 |
+
assert ts == Timestamp("01-01-2013T00:00:00.000000002+0000")
|
| 467 |
+
assert ts.unit == "ns"
|
| 468 |
+
|
| 469 |
+
# GH#56208 minute reso through the ISO8601 path with tz offset
|
| 470 |
+
ts = Timestamp("2020-01-01 00:00+00:00")
|
| 471 |
+
assert ts.unit == "s"
|
| 472 |
+
|
| 473 |
+
ts = Timestamp("2020-01-01 00+00:00")
|
| 474 |
+
assert ts.unit == "s"
|
| 475 |
+
|
| 476 |
+
@pytest.mark.parametrize("method", ["now", "today"])
|
| 477 |
+
def test_now_today_unit(self, method):
|
| 478 |
+
# GH#55879
|
| 479 |
+
ts_from_method = getattr(Timestamp, method)()
|
| 480 |
+
ts_from_string = Timestamp(method)
|
| 481 |
+
assert ts_from_method.unit == ts_from_string.unit == "us"
|
| 482 |
+
|
| 483 |
+
|
| 484 |
+
class TestTimestampConstructors:
|
| 485 |
+
def test_weekday_but_no_day_raises(self):
|
| 486 |
+
# GH#52659
|
| 487 |
+
msg = "Parsing datetimes with weekday but no day information is not supported"
|
| 488 |
+
with pytest.raises(ValueError, match=msg):
|
| 489 |
+
Timestamp("2023 Sept Thu")
|
| 490 |
+
|
| 491 |
+
def test_construct_from_string_invalid_raises(self):
|
| 492 |
+
# dateutil (weirdly) parses "200622-12-31" as
|
| 493 |
+
# datetime(2022, 6, 20, 12, 0, tzinfo=tzoffset(None, -111600)
|
| 494 |
+
# which besides being mis-parsed, is a tzoffset that will cause
|
| 495 |
+
# str(ts) to raise ValueError. Ensure we raise in the constructor
|
| 496 |
+
# instead.
|
| 497 |
+
# see test_to_datetime_malformed_raise for analogous to_datetime test
|
| 498 |
+
with pytest.raises(ValueError, match="gives an invalid tzoffset"):
|
| 499 |
+
Timestamp("200622-12-31")
|
| 500 |
+
|
| 501 |
+
def test_constructor_from_iso8601_str_with_offset_reso(self):
|
| 502 |
+
# GH#49737
|
| 503 |
+
ts = Timestamp("2016-01-01 04:05:06-01:00")
|
| 504 |
+
assert ts.unit == "s"
|
| 505 |
+
|
| 506 |
+
ts = Timestamp("2016-01-01 04:05:06.000-01:00")
|
| 507 |
+
assert ts.unit == "ms"
|
| 508 |
+
|
| 509 |
+
ts = Timestamp("2016-01-01 04:05:06.000000-01:00")
|
| 510 |
+
assert ts.unit == "us"
|
| 511 |
+
|
| 512 |
+
ts = Timestamp("2016-01-01 04:05:06.000000001-01:00")
|
| 513 |
+
assert ts.unit == "ns"
|
| 514 |
+
|
| 515 |
+
def test_constructor_from_date_second_reso(self):
|
| 516 |
+
# GH#49034 constructing from a pydate object gets lowest supported
|
| 517 |
+
# reso, i.e. seconds
|
| 518 |
+
obj = date(2012, 9, 1)
|
| 519 |
+
ts = Timestamp(obj)
|
| 520 |
+
assert ts.unit == "s"
|
| 521 |
+
|
| 522 |
+
def test_constructor_datetime64_with_tz(self):
|
| 523 |
+
# GH#42288, GH#24559
|
| 524 |
+
dt = np.datetime64("1970-01-01 05:00:00")
|
| 525 |
+
tzstr = "UTC+05:00"
|
| 526 |
+
|
| 527 |
+
# pre-2.0 this interpreted dt as a UTC time. in 2.0 this is treated
|
| 528 |
+
# as a wall-time, consistent with DatetimeIndex behavior
|
| 529 |
+
ts = Timestamp(dt, tz=tzstr)
|
| 530 |
+
|
| 531 |
+
alt = Timestamp(dt).tz_localize(tzstr)
|
| 532 |
+
assert ts == alt
|
| 533 |
+
assert ts.hour == 5
|
| 534 |
+
|
| 535 |
+
def test_constructor(self):
|
| 536 |
+
base_str = "2014-07-01 09:00"
|
| 537 |
+
base_dt = datetime(2014, 7, 1, 9)
|
| 538 |
+
base_expected = 1_404_205_200_000_000_000
|
| 539 |
+
|
| 540 |
+
# confirm base representation is correct
|
| 541 |
+
assert calendar.timegm(base_dt.timetuple()) * 1_000_000_000 == base_expected
|
| 542 |
+
|
| 543 |
+
tests = [
|
| 544 |
+
(base_str, base_dt, base_expected),
|
| 545 |
+
(
|
| 546 |
+
"2014-07-01 10:00",
|
| 547 |
+
datetime(2014, 7, 1, 10),
|
| 548 |
+
base_expected + 3600 * 1_000_000_000,
|
| 549 |
+
),
|
| 550 |
+
(
|
| 551 |
+
"2014-07-01 09:00:00.000008000",
|
| 552 |
+
datetime(2014, 7, 1, 9, 0, 0, 8),
|
| 553 |
+
base_expected + 8000,
|
| 554 |
+
),
|
| 555 |
+
(
|
| 556 |
+
"2014-07-01 09:00:00.000000005",
|
| 557 |
+
Timestamp("2014-07-01 09:00:00.000000005"),
|
| 558 |
+
base_expected + 5,
|
| 559 |
+
),
|
| 560 |
+
]
|
| 561 |
+
|
| 562 |
+
timezones = [
|
| 563 |
+
(None, 0),
|
| 564 |
+
("UTC", 0),
|
| 565 |
+
(pytz.utc, 0),
|
| 566 |
+
("Asia/Tokyo", 9),
|
| 567 |
+
("US/Eastern", -4),
|
| 568 |
+
("dateutil/US/Pacific", -7),
|
| 569 |
+
(pytz.FixedOffset(-180), -3),
|
| 570 |
+
(dateutil.tz.tzoffset(None, 18000), 5),
|
| 571 |
+
]
|
| 572 |
+
|
| 573 |
+
for date_str, date_obj, expected in tests:
|
| 574 |
+
for result in [Timestamp(date_str), Timestamp(date_obj)]:
|
| 575 |
+
result = result.as_unit("ns") # test originally written before non-nano
|
| 576 |
+
# only with timestring
|
| 577 |
+
assert result.as_unit("ns")._value == expected
|
| 578 |
+
|
| 579 |
+
# re-creation shouldn't affect to internal value
|
| 580 |
+
result = Timestamp(result)
|
| 581 |
+
assert result.as_unit("ns")._value == expected
|
| 582 |
+
|
| 583 |
+
# with timezone
|
| 584 |
+
for tz, offset in timezones:
|
| 585 |
+
for result in [Timestamp(date_str, tz=tz), Timestamp(date_obj, tz=tz)]:
|
| 586 |
+
result = result.as_unit(
|
| 587 |
+
"ns"
|
| 588 |
+
) # test originally written before non-nano
|
| 589 |
+
expected_tz = expected - offset * 3600 * 1_000_000_000
|
| 590 |
+
assert result.as_unit("ns")._value == expected_tz
|
| 591 |
+
|
| 592 |
+
# should preserve tz
|
| 593 |
+
result = Timestamp(result)
|
| 594 |
+
assert result.as_unit("ns")._value == expected_tz
|
| 595 |
+
|
| 596 |
+
# should convert to UTC
|
| 597 |
+
if tz is not None:
|
| 598 |
+
result = Timestamp(result).tz_convert("UTC")
|
| 599 |
+
else:
|
| 600 |
+
result = Timestamp(result, tz="UTC")
|
| 601 |
+
expected_utc = expected - offset * 3600 * 1_000_000_000
|
| 602 |
+
assert result.as_unit("ns")._value == expected_utc
|
| 603 |
+
|
| 604 |
+
def test_constructor_with_stringoffset(self):
|
| 605 |
+
# GH 7833
|
| 606 |
+
base_str = "2014-07-01 11:00:00+02:00"
|
| 607 |
+
base_dt = datetime(2014, 7, 1, 9)
|
| 608 |
+
base_expected = 1_404_205_200_000_000_000
|
| 609 |
+
|
| 610 |
+
# confirm base representation is correct
|
| 611 |
+
assert calendar.timegm(base_dt.timetuple()) * 1_000_000_000 == base_expected
|
| 612 |
+
|
| 613 |
+
tests = [
|
| 614 |
+
(base_str, base_expected),
|
| 615 |
+
("2014-07-01 12:00:00+02:00", base_expected + 3600 * 1_000_000_000),
|
| 616 |
+
("2014-07-01 11:00:00.000008000+02:00", base_expected + 8000),
|
| 617 |
+
("2014-07-01 11:00:00.000000005+02:00", base_expected + 5),
|
| 618 |
+
]
|
| 619 |
+
|
| 620 |
+
timezones = [
|
| 621 |
+
(None, 0),
|
| 622 |
+
("UTC", 0),
|
| 623 |
+
(pytz.utc, 0),
|
| 624 |
+
("Asia/Tokyo", 9),
|
| 625 |
+
("US/Eastern", -4),
|
| 626 |
+
("dateutil/US/Pacific", -7),
|
| 627 |
+
(pytz.FixedOffset(-180), -3),
|
| 628 |
+
(dateutil.tz.tzoffset(None, 18000), 5),
|
| 629 |
+
]
|
| 630 |
+
|
| 631 |
+
for date_str, expected in tests:
|
| 632 |
+
for result in [Timestamp(date_str)]:
|
| 633 |
+
# only with timestring
|
| 634 |
+
assert result.as_unit("ns")._value == expected
|
| 635 |
+
|
| 636 |
+
# re-creation shouldn't affect to internal value
|
| 637 |
+
result = Timestamp(result)
|
| 638 |
+
assert result.as_unit("ns")._value == expected
|
| 639 |
+
|
| 640 |
+
# with timezone
|
| 641 |
+
for tz, offset in timezones:
|
| 642 |
+
result = Timestamp(date_str, tz=tz)
|
| 643 |
+
expected_tz = expected
|
| 644 |
+
assert result.as_unit("ns")._value == expected_tz
|
| 645 |
+
|
| 646 |
+
# should preserve tz
|
| 647 |
+
result = Timestamp(result)
|
| 648 |
+
assert result.as_unit("ns")._value == expected_tz
|
| 649 |
+
|
| 650 |
+
# should convert to UTC
|
| 651 |
+
result = Timestamp(result).tz_convert("UTC")
|
| 652 |
+
expected_utc = expected
|
| 653 |
+
assert result.as_unit("ns")._value == expected_utc
|
| 654 |
+
|
| 655 |
+
# This should be 2013-11-01 05:00 in UTC
|
| 656 |
+
# converted to Chicago tz
|
| 657 |
+
result = Timestamp("2013-11-01 00:00:00-0500", tz="America/Chicago")
|
| 658 |
+
assert result._value == Timestamp("2013-11-01 05:00")._value
|
| 659 |
+
expected = "Timestamp('2013-11-01 00:00:00-0500', tz='America/Chicago')"
|
| 660 |
+
assert repr(result) == expected
|
| 661 |
+
assert result == eval(repr(result))
|
| 662 |
+
|
| 663 |
+
# This should be 2013-11-01 05:00 in UTC
|
| 664 |
+
# converted to Tokyo tz (+09:00)
|
| 665 |
+
result = Timestamp("2013-11-01 00:00:00-0500", tz="Asia/Tokyo")
|
| 666 |
+
assert result._value == Timestamp("2013-11-01 05:00")._value
|
| 667 |
+
expected = "Timestamp('2013-11-01 14:00:00+0900', tz='Asia/Tokyo')"
|
| 668 |
+
assert repr(result) == expected
|
| 669 |
+
assert result == eval(repr(result))
|
| 670 |
+
|
| 671 |
+
# GH11708
|
| 672 |
+
# This should be 2015-11-18 10:00 in UTC
|
| 673 |
+
# converted to Asia/Katmandu
|
| 674 |
+
result = Timestamp("2015-11-18 15:45:00+05:45", tz="Asia/Katmandu")
|
| 675 |
+
assert result._value == Timestamp("2015-11-18 10:00")._value
|
| 676 |
+
expected = "Timestamp('2015-11-18 15:45:00+0545', tz='Asia/Katmandu')"
|
| 677 |
+
assert repr(result) == expected
|
| 678 |
+
assert result == eval(repr(result))
|
| 679 |
+
|
| 680 |
+
# This should be 2015-11-18 10:00 in UTC
|
| 681 |
+
# converted to Asia/Kolkata
|
| 682 |
+
result = Timestamp("2015-11-18 15:30:00+05:30", tz="Asia/Kolkata")
|
| 683 |
+
assert result._value == Timestamp("2015-11-18 10:00")._value
|
| 684 |
+
expected = "Timestamp('2015-11-18 15:30:00+0530', tz='Asia/Kolkata')"
|
| 685 |
+
assert repr(result) == expected
|
| 686 |
+
assert result == eval(repr(result))
|
| 687 |
+
|
| 688 |
+
def test_constructor_invalid(self):
|
| 689 |
+
msg = "Cannot convert input"
|
| 690 |
+
with pytest.raises(TypeError, match=msg):
|
| 691 |
+
Timestamp(slice(2))
|
| 692 |
+
msg = "Cannot convert Period"
|
| 693 |
+
with pytest.raises(ValueError, match=msg):
|
| 694 |
+
Timestamp(Period("1000-01-01"))
|
| 695 |
+
|
| 696 |
+
def test_constructor_invalid_tz(self):
|
| 697 |
+
# GH#17690
|
| 698 |
+
msg = (
|
| 699 |
+
"Argument 'tzinfo' has incorrect type "
|
| 700 |
+
r"\(expected datetime.tzinfo, got str\)"
|
| 701 |
+
)
|
| 702 |
+
with pytest.raises(TypeError, match=msg):
|
| 703 |
+
Timestamp("2017-10-22", tzinfo="US/Eastern")
|
| 704 |
+
|
| 705 |
+
msg = "at most one of"
|
| 706 |
+
with pytest.raises(ValueError, match=msg):
|
| 707 |
+
Timestamp("2017-10-22", tzinfo=pytz.utc, tz="UTC")
|
| 708 |
+
|
| 709 |
+
msg = "Cannot pass a date attribute keyword argument when passing a date string"
|
| 710 |
+
with pytest.raises(ValueError, match=msg):
|
| 711 |
+
# GH#5168
|
| 712 |
+
# case where user tries to pass tz as an arg, not kwarg, gets
|
| 713 |
+
# interpreted as `year`
|
| 714 |
+
Timestamp("2012-01-01", "US/Pacific")
|
| 715 |
+
|
| 716 |
+
def test_constructor_tz_or_tzinfo(self):
|
| 717 |
+
# GH#17943, GH#17690, GH#5168
|
| 718 |
+
stamps = [
|
| 719 |
+
Timestamp(year=2017, month=10, day=22, tz="UTC"),
|
| 720 |
+
Timestamp(year=2017, month=10, day=22, tzinfo=pytz.utc),
|
| 721 |
+
Timestamp(year=2017, month=10, day=22, tz=pytz.utc),
|
| 722 |
+
Timestamp(datetime(2017, 10, 22), tzinfo=pytz.utc),
|
| 723 |
+
Timestamp(datetime(2017, 10, 22), tz="UTC"),
|
| 724 |
+
Timestamp(datetime(2017, 10, 22), tz=pytz.utc),
|
| 725 |
+
]
|
| 726 |
+
assert all(ts == stamps[0] for ts in stamps)
|
| 727 |
+
|
| 728 |
+
@pytest.mark.parametrize(
|
| 729 |
+
"result",
|
| 730 |
+
[
|
| 731 |
+
Timestamp(datetime(2000, 1, 2, 3, 4, 5, 6), nanosecond=1),
|
| 732 |
+
Timestamp(
|
| 733 |
+
year=2000,
|
| 734 |
+
month=1,
|
| 735 |
+
day=2,
|
| 736 |
+
hour=3,
|
| 737 |
+
minute=4,
|
| 738 |
+
second=5,
|
| 739 |
+
microsecond=6,
|
| 740 |
+
nanosecond=1,
|
| 741 |
+
),
|
| 742 |
+
Timestamp(
|
| 743 |
+
year=2000,
|
| 744 |
+
month=1,
|
| 745 |
+
day=2,
|
| 746 |
+
hour=3,
|
| 747 |
+
minute=4,
|
| 748 |
+
second=5,
|
| 749 |
+
microsecond=6,
|
| 750 |
+
nanosecond=1,
|
| 751 |
+
tz="UTC",
|
| 752 |
+
),
|
| 753 |
+
Timestamp(2000, 1, 2, 3, 4, 5, 6, None, nanosecond=1),
|
| 754 |
+
Timestamp(2000, 1, 2, 3, 4, 5, 6, tz=pytz.UTC, nanosecond=1),
|
| 755 |
+
],
|
| 756 |
+
)
|
| 757 |
+
def test_constructor_nanosecond(self, result):
|
| 758 |
+
# GH 18898
|
| 759 |
+
# As of 2.0 (GH 49416), nanosecond should not be accepted positionally
|
| 760 |
+
expected = Timestamp(datetime(2000, 1, 2, 3, 4, 5, 6), tz=result.tz)
|
| 761 |
+
expected = expected + Timedelta(nanoseconds=1)
|
| 762 |
+
assert result == expected
|
| 763 |
+
|
| 764 |
+
@pytest.mark.parametrize("z", ["Z0", "Z00"])
|
| 765 |
+
def test_constructor_invalid_Z0_isostring(self, z):
|
| 766 |
+
# GH 8910
|
| 767 |
+
msg = f"Unknown datetime string format, unable to parse: 2014-11-02 01:00{z}"
|
| 768 |
+
with pytest.raises(ValueError, match=msg):
|
| 769 |
+
Timestamp(f"2014-11-02 01:00{z}")
|
| 770 |
+
|
| 771 |
+
def test_out_of_bounds_integer_value(self):
|
| 772 |
+
# GH#26651 check that we raise OutOfBoundsDatetime, not OverflowError
|
| 773 |
+
msg = str(Timestamp.max._value * 2)
|
| 774 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 775 |
+
Timestamp(Timestamp.max._value * 2)
|
| 776 |
+
msg = str(Timestamp.min._value * 2)
|
| 777 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 778 |
+
Timestamp(Timestamp.min._value * 2)
|
| 779 |
+
|
| 780 |
+
def test_out_of_bounds_value(self):
|
| 781 |
+
one_us = np.timedelta64(1).astype("timedelta64[us]")
|
| 782 |
+
|
| 783 |
+
# By definition we can't go out of bounds in [ns], so we
|
| 784 |
+
# convert the datetime64s to [us] so we can go out of bounds
|
| 785 |
+
min_ts_us = np.datetime64(Timestamp.min).astype("M8[us]") + one_us
|
| 786 |
+
max_ts_us = np.datetime64(Timestamp.max).astype("M8[us]")
|
| 787 |
+
|
| 788 |
+
# No error for the min/max datetimes
|
| 789 |
+
Timestamp(min_ts_us)
|
| 790 |
+
Timestamp(max_ts_us)
|
| 791 |
+
|
| 792 |
+
# We used to raise on these before supporting non-nano
|
| 793 |
+
us_val = NpyDatetimeUnit.NPY_FR_us.value
|
| 794 |
+
assert Timestamp(min_ts_us - one_us)._creso == us_val
|
| 795 |
+
assert Timestamp(max_ts_us + one_us)._creso == us_val
|
| 796 |
+
|
| 797 |
+
# https://github.com/numpy/numpy/issues/22346 for why
|
| 798 |
+
# we can't use the same construction as above with minute resolution
|
| 799 |
+
|
| 800 |
+
# too_low, too_high are the _just_ outside the range of M8[s]
|
| 801 |
+
too_low = np.datetime64("-292277022657-01-27T08:29", "m")
|
| 802 |
+
too_high = np.datetime64("292277026596-12-04T15:31", "m")
|
| 803 |
+
|
| 804 |
+
msg = "Out of bounds"
|
| 805 |
+
# One us less than the minimum is an error
|
| 806 |
+
with pytest.raises(ValueError, match=msg):
|
| 807 |
+
Timestamp(too_low)
|
| 808 |
+
|
| 809 |
+
# One us more than the maximum is an error
|
| 810 |
+
with pytest.raises(ValueError, match=msg):
|
| 811 |
+
Timestamp(too_high)
|
| 812 |
+
|
| 813 |
+
def test_out_of_bounds_string(self):
|
| 814 |
+
msg = "Cannot cast .* to unit='ns' without overflow"
|
| 815 |
+
with pytest.raises(ValueError, match=msg):
|
| 816 |
+
Timestamp("1676-01-01").as_unit("ns")
|
| 817 |
+
with pytest.raises(ValueError, match=msg):
|
| 818 |
+
Timestamp("2263-01-01").as_unit("ns")
|
| 819 |
+
|
| 820 |
+
ts = Timestamp("2263-01-01")
|
| 821 |
+
assert ts.unit == "s"
|
| 822 |
+
|
| 823 |
+
ts = Timestamp("1676-01-01")
|
| 824 |
+
assert ts.unit == "s"
|
| 825 |
+
|
| 826 |
+
def test_barely_out_of_bounds(self):
|
| 827 |
+
# GH#19529
|
| 828 |
+
# GH#19382 close enough to bounds that dropping nanos would result
|
| 829 |
+
# in an in-bounds datetime
|
| 830 |
+
msg = "Out of bounds nanosecond timestamp: 2262-04-11 23:47:16"
|
| 831 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 832 |
+
Timestamp("2262-04-11 23:47:16.854775808")
|
| 833 |
+
|
| 834 |
+
@pytest.mark.skip_ubsan
|
| 835 |
+
def test_bounds_with_different_units(self):
|
| 836 |
+
out_of_bounds_dates = ("1677-09-21", "2262-04-12")
|
| 837 |
+
|
| 838 |
+
time_units = ("D", "h", "m", "s", "ms", "us")
|
| 839 |
+
|
| 840 |
+
for date_string in out_of_bounds_dates:
|
| 841 |
+
for unit in time_units:
|
| 842 |
+
dt64 = np.datetime64(date_string, unit)
|
| 843 |
+
ts = Timestamp(dt64)
|
| 844 |
+
if unit in ["s", "ms", "us"]:
|
| 845 |
+
# We can preserve the input unit
|
| 846 |
+
assert ts._value == dt64.view("i8")
|
| 847 |
+
else:
|
| 848 |
+
# we chose the closest unit that we _do_ support
|
| 849 |
+
assert ts._creso == NpyDatetimeUnit.NPY_FR_s.value
|
| 850 |
+
|
| 851 |
+
# With more extreme cases, we can't even fit inside second resolution
|
| 852 |
+
info = np.iinfo(np.int64)
|
| 853 |
+
msg = "Out of bounds second timestamp:"
|
| 854 |
+
for value in [info.min + 1, info.max]:
|
| 855 |
+
for unit in ["D", "h", "m"]:
|
| 856 |
+
dt64 = np.datetime64(value, unit)
|
| 857 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 858 |
+
Timestamp(dt64)
|
| 859 |
+
|
| 860 |
+
in_bounds_dates = ("1677-09-23", "2262-04-11")
|
| 861 |
+
|
| 862 |
+
for date_string in in_bounds_dates:
|
| 863 |
+
for unit in time_units:
|
| 864 |
+
dt64 = np.datetime64(date_string, unit)
|
| 865 |
+
Timestamp(dt64)
|
| 866 |
+
|
| 867 |
+
@pytest.mark.parametrize("arg", ["001-01-01", "0001-01-01"])
|
| 868 |
+
def test_out_of_bounds_string_consistency(self, arg):
|
| 869 |
+
# GH 15829
|
| 870 |
+
msg = "Cannot cast 0001-01-01 00:00:00 to unit='ns' without overflow"
|
| 871 |
+
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
| 872 |
+
Timestamp(arg).as_unit("ns")
|
| 873 |
+
|
| 874 |
+
ts = Timestamp(arg)
|
| 875 |
+
assert ts.unit == "s"
|
| 876 |
+
assert ts.year == ts.month == ts.day == 1
|
| 877 |
+
|
| 878 |
+
def test_min_valid(self):
|
| 879 |
+
# Ensure that Timestamp.min is a valid Timestamp
|
| 880 |
+
Timestamp(Timestamp.min)
|
| 881 |
+
|
| 882 |
+
def test_max_valid(self):
|
| 883 |
+
# Ensure that Timestamp.max is a valid Timestamp
|
| 884 |
+
Timestamp(Timestamp.max)
|
| 885 |
+
|
| 886 |
+
@pytest.mark.parametrize("offset", ["+0300", "+0200"])
|
| 887 |
+
def test_construct_timestamp_near_dst(self, offset):
|
| 888 |
+
# GH 20854
|
| 889 |
+
expected = Timestamp(f"2016-10-30 03:00:00{offset}", tz="Europe/Helsinki")
|
| 890 |
+
result = Timestamp(expected).tz_convert("Europe/Helsinki")
|
| 891 |
+
assert result == expected
|
| 892 |
+
|
| 893 |
+
@pytest.mark.parametrize(
|
| 894 |
+
"arg", ["2013/01/01 00:00:00+09:00", "2013-01-01 00:00:00+09:00"]
|
| 895 |
+
)
|
| 896 |
+
def test_construct_with_different_string_format(self, arg):
|
| 897 |
+
# GH 12064
|
| 898 |
+
result = Timestamp(arg)
|
| 899 |
+
expected = Timestamp(datetime(2013, 1, 1), tz=pytz.FixedOffset(540))
|
| 900 |
+
assert result == expected
|
| 901 |
+
|
| 902 |
+
@pytest.mark.parametrize("box", [datetime, Timestamp])
|
| 903 |
+
def test_raise_tz_and_tzinfo_in_datetime_input(self, box):
|
| 904 |
+
# GH 23579
|
| 905 |
+
kwargs = {"year": 2018, "month": 1, "day": 1, "tzinfo": pytz.utc}
|
| 906 |
+
msg = "Cannot pass a datetime or Timestamp"
|
| 907 |
+
with pytest.raises(ValueError, match=msg):
|
| 908 |
+
Timestamp(box(**kwargs), tz="US/Pacific")
|
| 909 |
+
msg = "Cannot pass a datetime or Timestamp"
|
| 910 |
+
with pytest.raises(ValueError, match=msg):
|
| 911 |
+
Timestamp(box(**kwargs), tzinfo=pytz.timezone("US/Pacific"))
|
| 912 |
+
|
| 913 |
+
def test_dont_convert_dateutil_utc_to_pytz_utc(self):
|
| 914 |
+
result = Timestamp(datetime(2018, 1, 1), tz=tzutc())
|
| 915 |
+
expected = Timestamp(datetime(2018, 1, 1)).tz_localize(tzutc())
|
| 916 |
+
assert result == expected
|
| 917 |
+
|
| 918 |
+
def test_constructor_subclassed_datetime(self):
|
| 919 |
+
# GH 25851
|
| 920 |
+
# ensure that subclassed datetime works for
|
| 921 |
+
# Timestamp creation
|
| 922 |
+
class SubDatetime(datetime):
|
| 923 |
+
pass
|
| 924 |
+
|
| 925 |
+
data = SubDatetime(2000, 1, 1)
|
| 926 |
+
result = Timestamp(data)
|
| 927 |
+
expected = Timestamp(2000, 1, 1)
|
| 928 |
+
assert result == expected
|
| 929 |
+
|
| 930 |
+
def test_timestamp_constructor_tz_utc(self):
|
| 931 |
+
utc_stamp = Timestamp("3/11/2012 05:00", tz="utc")
|
| 932 |
+
assert utc_stamp.tzinfo is timezone.utc
|
| 933 |
+
assert utc_stamp.hour == 5
|
| 934 |
+
|
| 935 |
+
utc_stamp = Timestamp("3/11/2012 05:00").tz_localize("utc")
|
| 936 |
+
assert utc_stamp.hour == 5
|
| 937 |
+
|
| 938 |
+
def test_timestamp_to_datetime_tzoffset(self):
|
| 939 |
+
tzinfo = tzoffset(None, 7200)
|
| 940 |
+
expected = Timestamp("3/11/2012 04:00", tz=tzinfo)
|
| 941 |
+
result = Timestamp(expected.to_pydatetime())
|
| 942 |
+
assert expected == result
|
| 943 |
+
|
| 944 |
+
def test_timestamp_constructor_near_dst_boundary(self):
|
| 945 |
+
# GH#11481 & GH#15777
|
| 946 |
+
# Naive string timestamps were being localized incorrectly
|
| 947 |
+
# with tz_convert_from_utc_single instead of tz_localize_to_utc
|
| 948 |
+
|
| 949 |
+
for tz in ["Europe/Brussels", "Europe/Prague"]:
|
| 950 |
+
result = Timestamp("2015-10-25 01:00", tz=tz)
|
| 951 |
+
expected = Timestamp("2015-10-25 01:00").tz_localize(tz)
|
| 952 |
+
assert result == expected
|
| 953 |
+
|
| 954 |
+
msg = "Cannot infer dst time from 2015-10-25 02:00:00"
|
| 955 |
+
with pytest.raises(pytz.AmbiguousTimeError, match=msg):
|
| 956 |
+
Timestamp("2015-10-25 02:00", tz=tz)
|
| 957 |
+
|
| 958 |
+
result = Timestamp("2017-03-26 01:00", tz="Europe/Paris")
|
| 959 |
+
expected = Timestamp("2017-03-26 01:00").tz_localize("Europe/Paris")
|
| 960 |
+
assert result == expected
|
| 961 |
+
|
| 962 |
+
msg = "2017-03-26 02:00"
|
| 963 |
+
with pytest.raises(pytz.NonExistentTimeError, match=msg):
|
| 964 |
+
Timestamp("2017-03-26 02:00", tz="Europe/Paris")
|
| 965 |
+
|
| 966 |
+
# GH#11708
|
| 967 |
+
naive = Timestamp("2015-11-18 10:00:00")
|
| 968 |
+
result = naive.tz_localize("UTC").tz_convert("Asia/Kolkata")
|
| 969 |
+
expected = Timestamp("2015-11-18 15:30:00+0530", tz="Asia/Kolkata")
|
| 970 |
+
assert result == expected
|
| 971 |
+
|
| 972 |
+
# GH#15823
|
| 973 |
+
result = Timestamp("2017-03-26 00:00", tz="Europe/Paris")
|
| 974 |
+
expected = Timestamp("2017-03-26 00:00:00+0100", tz="Europe/Paris")
|
| 975 |
+
assert result == expected
|
| 976 |
+
|
| 977 |
+
result = Timestamp("2017-03-26 01:00", tz="Europe/Paris")
|
| 978 |
+
expected = Timestamp("2017-03-26 01:00:00+0100", tz="Europe/Paris")
|
| 979 |
+
assert result == expected
|
| 980 |
+
|
| 981 |
+
msg = "2017-03-26 02:00"
|
| 982 |
+
with pytest.raises(pytz.NonExistentTimeError, match=msg):
|
| 983 |
+
Timestamp("2017-03-26 02:00", tz="Europe/Paris")
|
| 984 |
+
|
| 985 |
+
result = Timestamp("2017-03-26 02:00:00+0100", tz="Europe/Paris")
|
| 986 |
+
naive = Timestamp(result.as_unit("ns")._value)
|
| 987 |
+
expected = naive.tz_localize("UTC").tz_convert("Europe/Paris")
|
| 988 |
+
assert result == expected
|
| 989 |
+
|
| 990 |
+
result = Timestamp("2017-03-26 03:00", tz="Europe/Paris")
|
| 991 |
+
expected = Timestamp("2017-03-26 03:00:00+0200", tz="Europe/Paris")
|
| 992 |
+
assert result == expected
|
| 993 |
+
|
| 994 |
+
@pytest.mark.parametrize(
|
| 995 |
+
"tz",
|
| 996 |
+
[
|
| 997 |
+
pytz.timezone("US/Eastern"),
|
| 998 |
+
gettz("US/Eastern"),
|
| 999 |
+
"US/Eastern",
|
| 1000 |
+
"dateutil/US/Eastern",
|
| 1001 |
+
],
|
| 1002 |
+
)
|
| 1003 |
+
def test_timestamp_constructed_by_date_and_tz(self, tz):
|
| 1004 |
+
# GH#2993, Timestamp cannot be constructed by datetime.date
|
| 1005 |
+
# and tz correctly
|
| 1006 |
+
|
| 1007 |
+
result = Timestamp(date(2012, 3, 11), tz=tz)
|
| 1008 |
+
|
| 1009 |
+
expected = Timestamp("3/11/2012", tz=tz)
|
| 1010 |
+
assert result.hour == expected.hour
|
| 1011 |
+
assert result == expected
|
| 1012 |
+
|
| 1013 |
+
|
| 1014 |
+
def test_constructor_ambiguous_dst():
|
| 1015 |
+
# GH 24329
|
| 1016 |
+
# Make sure that calling Timestamp constructor
|
| 1017 |
+
# on Timestamp created from ambiguous time
|
| 1018 |
+
# doesn't change Timestamp.value
|
| 1019 |
+
ts = Timestamp(1382835600000000000, tz="dateutil/Europe/London")
|
| 1020 |
+
expected = ts._value
|
| 1021 |
+
result = Timestamp(ts)._value
|
| 1022 |
+
assert result == expected
|
| 1023 |
+
|
| 1024 |
+
|
| 1025 |
+
@pytest.mark.parametrize("epoch", [1552211999999999872, 1552211999999999999])
|
| 1026 |
+
def test_constructor_before_dst_switch(epoch):
|
| 1027 |
+
# GH 31043
|
| 1028 |
+
# Make sure that calling Timestamp constructor
|
| 1029 |
+
# on time just before DST switch doesn't lead to
|
| 1030 |
+
# nonexistent time or value change
|
| 1031 |
+
ts = Timestamp(epoch, tz="dateutil/America/Los_Angeles")
|
| 1032 |
+
result = ts.tz.dst(ts)
|
| 1033 |
+
expected = timedelta(seconds=0)
|
| 1034 |
+
assert Timestamp(ts)._value == epoch
|
| 1035 |
+
assert result == expected
|
| 1036 |
+
|
| 1037 |
+
|
| 1038 |
+
def test_timestamp_constructor_identity():
|
| 1039 |
+
# Test for #30543
|
| 1040 |
+
expected = Timestamp("2017-01-01T12")
|
| 1041 |
+
result = Timestamp(expected)
|
| 1042 |
+
assert result is expected
|
| 1043 |
+
|
| 1044 |
+
|
| 1045 |
+
@pytest.mark.parametrize("nano", [-1, 1000])
|
| 1046 |
+
def test_timestamp_nano_range(nano):
|
| 1047 |
+
# GH 48255
|
| 1048 |
+
with pytest.raises(ValueError, match="nanosecond must be in 0..999"):
|
| 1049 |
+
Timestamp(year=2022, month=1, day=1, nanosecond=nano)
|
| 1050 |
+
|
| 1051 |
+
|
| 1052 |
+
def test_non_nano_value():
|
| 1053 |
+
# https://github.com/pandas-dev/pandas/issues/49076
|
| 1054 |
+
result = Timestamp("1800-01-01", unit="s").value
|
| 1055 |
+
# `.value` shows nanoseconds, even though unit is 's'
|
| 1056 |
+
assert result == -5364662400000000000
|
| 1057 |
+
|
| 1058 |
+
# out-of-nanoseconds-bounds `.value` raises informative message
|
| 1059 |
+
msg = (
|
| 1060 |
+
r"Cannot convert Timestamp to nanoseconds without overflow. "
|
| 1061 |
+
r"Use `.asm8.view\('i8'\)` to cast represent Timestamp in its "
|
| 1062 |
+
r"own unit \(here, s\).$"
|
| 1063 |
+
)
|
| 1064 |
+
ts = Timestamp("0300-01-01")
|
| 1065 |
+
with pytest.raises(OverflowError, match=msg):
|
| 1066 |
+
ts.value
|
| 1067 |
+
# check that the suggested workaround actually works
|
| 1068 |
+
result = ts.asm8.view("i8")
|
| 1069 |
+
assert result == -52700112000
|
| 1070 |
+
|
| 1071 |
+
|
| 1072 |
+
@pytest.mark.parametrize("na_value", [None, np.nan, np.datetime64("NaT"), NaT, NA])
|
| 1073 |
+
def test_timestamp_constructor_na_value(na_value):
|
| 1074 |
+
# GH45481
|
| 1075 |
+
result = Timestamp(na_value)
|
| 1076 |
+
expected = NaT
|
| 1077 |
+
assert result is expected
|
py311/lib/python3.11/site-packages/pandas/tests/scalar/timestamp/test_timestamp.py
ADDED
|
@@ -0,0 +1,928 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" test the scalar Timestamp """
|
| 2 |
+
|
| 3 |
+
import calendar
|
| 4 |
+
from datetime import (
|
| 5 |
+
datetime,
|
| 6 |
+
timedelta,
|
| 7 |
+
timezone,
|
| 8 |
+
)
|
| 9 |
+
import locale
|
| 10 |
+
import time
|
| 11 |
+
import unicodedata
|
| 12 |
+
|
| 13 |
+
from dateutil.tz import (
|
| 14 |
+
tzlocal,
|
| 15 |
+
tzutc,
|
| 16 |
+
)
|
| 17 |
+
from hypothesis import (
|
| 18 |
+
given,
|
| 19 |
+
strategies as st,
|
| 20 |
+
)
|
| 21 |
+
import numpy as np
|
| 22 |
+
import pytest
|
| 23 |
+
import pytz
|
| 24 |
+
from pytz import utc
|
| 25 |
+
|
| 26 |
+
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
| 27 |
+
from pandas._libs.tslibs.timezones import (
|
| 28 |
+
dateutil_gettz as gettz,
|
| 29 |
+
get_timezone,
|
| 30 |
+
maybe_get_tz,
|
| 31 |
+
tz_compare,
|
| 32 |
+
)
|
| 33 |
+
from pandas.compat import IS64
|
| 34 |
+
|
| 35 |
+
from pandas import (
|
| 36 |
+
NaT,
|
| 37 |
+
Timedelta,
|
| 38 |
+
Timestamp,
|
| 39 |
+
)
|
| 40 |
+
import pandas._testing as tm
|
| 41 |
+
|
| 42 |
+
from pandas.tseries import offsets
|
| 43 |
+
from pandas.tseries.frequencies import to_offset
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
class TestTimestampProperties:
|
| 47 |
+
def test_properties_business(self):
|
| 48 |
+
freq = to_offset("B")
|
| 49 |
+
|
| 50 |
+
ts = Timestamp("2017-10-01")
|
| 51 |
+
assert ts.dayofweek == 6
|
| 52 |
+
assert ts.day_of_week == 6
|
| 53 |
+
assert ts.is_month_start # not a weekday
|
| 54 |
+
assert not freq.is_month_start(ts)
|
| 55 |
+
assert freq.is_month_start(ts + Timedelta(days=1))
|
| 56 |
+
assert not freq.is_quarter_start(ts)
|
| 57 |
+
assert freq.is_quarter_start(ts + Timedelta(days=1))
|
| 58 |
+
|
| 59 |
+
ts = Timestamp("2017-09-30")
|
| 60 |
+
assert ts.dayofweek == 5
|
| 61 |
+
assert ts.day_of_week == 5
|
| 62 |
+
assert ts.is_month_end
|
| 63 |
+
assert not freq.is_month_end(ts)
|
| 64 |
+
assert freq.is_month_end(ts - Timedelta(days=1))
|
| 65 |
+
assert ts.is_quarter_end
|
| 66 |
+
assert not freq.is_quarter_end(ts)
|
| 67 |
+
assert freq.is_quarter_end(ts - Timedelta(days=1))
|
| 68 |
+
|
| 69 |
+
@pytest.mark.parametrize(
|
| 70 |
+
"attr, expected",
|
| 71 |
+
[
|
| 72 |
+
["year", 2014],
|
| 73 |
+
["month", 12],
|
| 74 |
+
["day", 31],
|
| 75 |
+
["hour", 23],
|
| 76 |
+
["minute", 59],
|
| 77 |
+
["second", 0],
|
| 78 |
+
["microsecond", 0],
|
| 79 |
+
["nanosecond", 0],
|
| 80 |
+
["dayofweek", 2],
|
| 81 |
+
["day_of_week", 2],
|
| 82 |
+
["quarter", 4],
|
| 83 |
+
["dayofyear", 365],
|
| 84 |
+
["day_of_year", 365],
|
| 85 |
+
["week", 1],
|
| 86 |
+
["daysinmonth", 31],
|
| 87 |
+
],
|
| 88 |
+
)
|
| 89 |
+
@pytest.mark.parametrize("tz", [None, "US/Eastern"])
|
| 90 |
+
def test_fields(self, attr, expected, tz):
|
| 91 |
+
# GH 10050
|
| 92 |
+
# GH 13303
|
| 93 |
+
ts = Timestamp("2014-12-31 23:59:00", tz=tz)
|
| 94 |
+
result = getattr(ts, attr)
|
| 95 |
+
# that we are int like
|
| 96 |
+
assert isinstance(result, int)
|
| 97 |
+
assert result == expected
|
| 98 |
+
|
| 99 |
+
@pytest.mark.parametrize("tz", [None, "US/Eastern"])
|
| 100 |
+
def test_millisecond_raises(self, tz):
|
| 101 |
+
ts = Timestamp("2014-12-31 23:59:00", tz=tz)
|
| 102 |
+
msg = "'Timestamp' object has no attribute 'millisecond'"
|
| 103 |
+
with pytest.raises(AttributeError, match=msg):
|
| 104 |
+
ts.millisecond
|
| 105 |
+
|
| 106 |
+
@pytest.mark.parametrize(
|
| 107 |
+
"start", ["is_month_start", "is_quarter_start", "is_year_start"]
|
| 108 |
+
)
|
| 109 |
+
@pytest.mark.parametrize("tz", [None, "US/Eastern"])
|
| 110 |
+
def test_is_start(self, start, tz):
|
| 111 |
+
ts = Timestamp("2014-01-01 00:00:00", tz=tz)
|
| 112 |
+
assert getattr(ts, start)
|
| 113 |
+
|
| 114 |
+
@pytest.mark.parametrize("end", ["is_month_end", "is_year_end", "is_quarter_end"])
|
| 115 |
+
@pytest.mark.parametrize("tz", [None, "US/Eastern"])
|
| 116 |
+
def test_is_end(self, end, tz):
|
| 117 |
+
ts = Timestamp("2014-12-31 23:59:59", tz=tz)
|
| 118 |
+
assert getattr(ts, end)
|
| 119 |
+
|
| 120 |
+
# GH 12806
|
| 121 |
+
@pytest.mark.parametrize(
|
| 122 |
+
"data",
|
| 123 |
+
[Timestamp("2017-08-28 23:00:00"), Timestamp("2017-08-28 23:00:00", tz="EST")],
|
| 124 |
+
)
|
| 125 |
+
# error: Unsupported operand types for + ("List[None]" and "List[str]")
|
| 126 |
+
@pytest.mark.parametrize(
|
| 127 |
+
"time_locale", [None] + tm.get_locales() # type: ignore[operator]
|
| 128 |
+
)
|
| 129 |
+
def test_names(self, data, time_locale):
|
| 130 |
+
# GH 17354
|
| 131 |
+
# Test .day_name(), .month_name
|
| 132 |
+
if time_locale is None:
|
| 133 |
+
expected_day = "Monday"
|
| 134 |
+
expected_month = "August"
|
| 135 |
+
else:
|
| 136 |
+
with tm.set_locale(time_locale, locale.LC_TIME):
|
| 137 |
+
expected_day = calendar.day_name[0].capitalize()
|
| 138 |
+
expected_month = calendar.month_name[8].capitalize()
|
| 139 |
+
|
| 140 |
+
result_day = data.day_name(time_locale)
|
| 141 |
+
result_month = data.month_name(time_locale)
|
| 142 |
+
|
| 143 |
+
# Work around https://github.com/pandas-dev/pandas/issues/22342
|
| 144 |
+
# different normalizations
|
| 145 |
+
expected_day = unicodedata.normalize("NFD", expected_day)
|
| 146 |
+
expected_month = unicodedata.normalize("NFD", expected_month)
|
| 147 |
+
|
| 148 |
+
result_day = unicodedata.normalize("NFD", result_day)
|
| 149 |
+
result_month = unicodedata.normalize("NFD", result_month)
|
| 150 |
+
|
| 151 |
+
assert result_day == expected_day
|
| 152 |
+
assert result_month == expected_month
|
| 153 |
+
|
| 154 |
+
# Test NaT
|
| 155 |
+
nan_ts = Timestamp(NaT)
|
| 156 |
+
assert np.isnan(nan_ts.day_name(time_locale))
|
| 157 |
+
assert np.isnan(nan_ts.month_name(time_locale))
|
| 158 |
+
|
| 159 |
+
def test_is_leap_year(self, tz_naive_fixture):
|
| 160 |
+
tz = tz_naive_fixture
|
| 161 |
+
if not IS64 and tz == tzlocal():
|
| 162 |
+
# https://github.com/dateutil/dateutil/issues/197
|
| 163 |
+
pytest.skip(
|
| 164 |
+
"tzlocal() on a 32 bit platform causes internal overflow errors"
|
| 165 |
+
)
|
| 166 |
+
# GH 13727
|
| 167 |
+
dt = Timestamp("2000-01-01 00:00:00", tz=tz)
|
| 168 |
+
assert dt.is_leap_year
|
| 169 |
+
assert isinstance(dt.is_leap_year, bool)
|
| 170 |
+
|
| 171 |
+
dt = Timestamp("1999-01-01 00:00:00", tz=tz)
|
| 172 |
+
assert not dt.is_leap_year
|
| 173 |
+
|
| 174 |
+
dt = Timestamp("2004-01-01 00:00:00", tz=tz)
|
| 175 |
+
assert dt.is_leap_year
|
| 176 |
+
|
| 177 |
+
dt = Timestamp("2100-01-01 00:00:00", tz=tz)
|
| 178 |
+
assert not dt.is_leap_year
|
| 179 |
+
|
| 180 |
+
def test_woy_boundary(self):
|
| 181 |
+
# make sure weeks at year boundaries are correct
|
| 182 |
+
d = datetime(2013, 12, 31)
|
| 183 |
+
result = Timestamp(d).week
|
| 184 |
+
expected = 1 # ISO standard
|
| 185 |
+
assert result == expected
|
| 186 |
+
|
| 187 |
+
d = datetime(2008, 12, 28)
|
| 188 |
+
result = Timestamp(d).week
|
| 189 |
+
expected = 52 # ISO standard
|
| 190 |
+
assert result == expected
|
| 191 |
+
|
| 192 |
+
d = datetime(2009, 12, 31)
|
| 193 |
+
result = Timestamp(d).week
|
| 194 |
+
expected = 53 # ISO standard
|
| 195 |
+
assert result == expected
|
| 196 |
+
|
| 197 |
+
d = datetime(2010, 1, 1)
|
| 198 |
+
result = Timestamp(d).week
|
| 199 |
+
expected = 53 # ISO standard
|
| 200 |
+
assert result == expected
|
| 201 |
+
|
| 202 |
+
d = datetime(2010, 1, 3)
|
| 203 |
+
result = Timestamp(d).week
|
| 204 |
+
expected = 53 # ISO standard
|
| 205 |
+
assert result == expected
|
| 206 |
+
|
| 207 |
+
result = np.array(
|
| 208 |
+
[
|
| 209 |
+
Timestamp(datetime(*args)).week
|
| 210 |
+
for args in [(2000, 1, 1), (2000, 1, 2), (2005, 1, 1), (2005, 1, 2)]
|
| 211 |
+
]
|
| 212 |
+
)
|
| 213 |
+
assert (result == [52, 52, 53, 53]).all()
|
| 214 |
+
|
| 215 |
+
def test_resolution(self):
|
| 216 |
+
# GH#21336, GH#21365
|
| 217 |
+
dt = Timestamp("2100-01-01 00:00:00.000000000")
|
| 218 |
+
assert dt.resolution == Timedelta(nanoseconds=1)
|
| 219 |
+
|
| 220 |
+
# Check that the attribute is available on the class, mirroring
|
| 221 |
+
# the stdlib datetime behavior
|
| 222 |
+
assert Timestamp.resolution == Timedelta(nanoseconds=1)
|
| 223 |
+
|
| 224 |
+
assert dt.as_unit("us").resolution == Timedelta(microseconds=1)
|
| 225 |
+
assert dt.as_unit("ms").resolution == Timedelta(milliseconds=1)
|
| 226 |
+
assert dt.as_unit("s").resolution == Timedelta(seconds=1)
|
| 227 |
+
|
| 228 |
+
@pytest.mark.parametrize(
|
| 229 |
+
"date_string, expected",
|
| 230 |
+
[
|
| 231 |
+
("0000-2-29", 1),
|
| 232 |
+
("0000-3-1", 2),
|
| 233 |
+
("1582-10-14", 3),
|
| 234 |
+
("-0040-1-1", 4),
|
| 235 |
+
("2023-06-18", 6),
|
| 236 |
+
],
|
| 237 |
+
)
|
| 238 |
+
def test_dow_historic(self, date_string, expected):
|
| 239 |
+
# GH 53738
|
| 240 |
+
ts = Timestamp(date_string)
|
| 241 |
+
dow = ts.weekday()
|
| 242 |
+
assert dow == expected
|
| 243 |
+
|
| 244 |
+
@given(
|
| 245 |
+
ts=st.datetimes(),
|
| 246 |
+
sign=st.sampled_from(["-", ""]),
|
| 247 |
+
)
|
| 248 |
+
def test_dow_parametric(self, ts, sign):
|
| 249 |
+
# GH 53738
|
| 250 |
+
ts = (
|
| 251 |
+
f"{sign}{str(ts.year).zfill(4)}"
|
| 252 |
+
f"-{str(ts.month).zfill(2)}"
|
| 253 |
+
f"-{str(ts.day).zfill(2)}"
|
| 254 |
+
)
|
| 255 |
+
result = Timestamp(ts).weekday()
|
| 256 |
+
expected = (
|
| 257 |
+
(np.datetime64(ts) - np.datetime64("1970-01-01")).astype("int64") - 4
|
| 258 |
+
) % 7
|
| 259 |
+
assert result == expected
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
class TestTimestamp:
|
| 263 |
+
@pytest.mark.parametrize("tz", [None, pytz.timezone("US/Pacific")])
|
| 264 |
+
def test_disallow_setting_tz(self, tz):
|
| 265 |
+
# GH#3746
|
| 266 |
+
ts = Timestamp("2010")
|
| 267 |
+
msg = "Cannot directly set timezone"
|
| 268 |
+
with pytest.raises(AttributeError, match=msg):
|
| 269 |
+
ts.tz = tz
|
| 270 |
+
|
| 271 |
+
def test_default_to_stdlib_utc(self):
|
| 272 |
+
assert Timestamp.utcnow().tz is timezone.utc
|
| 273 |
+
assert Timestamp.now("UTC").tz is timezone.utc
|
| 274 |
+
assert Timestamp("2016-01-01", tz="UTC").tz is timezone.utc
|
| 275 |
+
|
| 276 |
+
def test_tz(self):
|
| 277 |
+
tstr = "2014-02-01 09:00"
|
| 278 |
+
ts = Timestamp(tstr)
|
| 279 |
+
local = ts.tz_localize("Asia/Tokyo")
|
| 280 |
+
assert local.hour == 9
|
| 281 |
+
assert local == Timestamp(tstr, tz="Asia/Tokyo")
|
| 282 |
+
conv = local.tz_convert("US/Eastern")
|
| 283 |
+
assert conv == Timestamp("2014-01-31 19:00", tz="US/Eastern")
|
| 284 |
+
assert conv.hour == 19
|
| 285 |
+
|
| 286 |
+
# preserves nanosecond
|
| 287 |
+
ts = Timestamp(tstr) + offsets.Nano(5)
|
| 288 |
+
local = ts.tz_localize("Asia/Tokyo")
|
| 289 |
+
assert local.hour == 9
|
| 290 |
+
assert local.nanosecond == 5
|
| 291 |
+
conv = local.tz_convert("US/Eastern")
|
| 292 |
+
assert conv.nanosecond == 5
|
| 293 |
+
assert conv.hour == 19
|
| 294 |
+
|
| 295 |
+
def test_utc_z_designator(self):
|
| 296 |
+
assert get_timezone(Timestamp("2014-11-02 01:00Z").tzinfo) is timezone.utc
|
| 297 |
+
|
| 298 |
+
def test_asm8(self):
|
| 299 |
+
ns = [Timestamp.min._value, Timestamp.max._value, 1000]
|
| 300 |
+
|
| 301 |
+
for n in ns:
|
| 302 |
+
assert (
|
| 303 |
+
Timestamp(n).asm8.view("i8") == np.datetime64(n, "ns").view("i8") == n
|
| 304 |
+
)
|
| 305 |
+
|
| 306 |
+
assert Timestamp("nat").asm8.view("i8") == np.datetime64("nat", "ns").view("i8")
|
| 307 |
+
|
| 308 |
+
def test_class_ops(self):
|
| 309 |
+
def compare(x, y):
|
| 310 |
+
assert int((Timestamp(x)._value - Timestamp(y)._value) / 1e9) == 0
|
| 311 |
+
|
| 312 |
+
compare(Timestamp.now(), datetime.now())
|
| 313 |
+
compare(Timestamp.now("UTC"), datetime.now(pytz.timezone("UTC")))
|
| 314 |
+
compare(Timestamp.now("UTC"), datetime.now(tzutc()))
|
| 315 |
+
compare(Timestamp.utcnow(), datetime.now(timezone.utc))
|
| 316 |
+
compare(Timestamp.today(), datetime.today())
|
| 317 |
+
current_time = calendar.timegm(datetime.now().utctimetuple())
|
| 318 |
+
|
| 319 |
+
ts_utc = Timestamp.utcfromtimestamp(current_time)
|
| 320 |
+
assert ts_utc.timestamp() == current_time
|
| 321 |
+
compare(
|
| 322 |
+
Timestamp.fromtimestamp(current_time), datetime.fromtimestamp(current_time)
|
| 323 |
+
)
|
| 324 |
+
compare(
|
| 325 |
+
# Support tz kwarg in Timestamp.fromtimestamp
|
| 326 |
+
Timestamp.fromtimestamp(current_time, "UTC"),
|
| 327 |
+
datetime.fromtimestamp(current_time, utc),
|
| 328 |
+
)
|
| 329 |
+
compare(
|
| 330 |
+
# Support tz kwarg in Timestamp.fromtimestamp
|
| 331 |
+
Timestamp.fromtimestamp(current_time, tz="UTC"),
|
| 332 |
+
datetime.fromtimestamp(current_time, utc),
|
| 333 |
+
)
|
| 334 |
+
|
| 335 |
+
date_component = datetime.now(timezone.utc)
|
| 336 |
+
time_component = (date_component + timedelta(minutes=10)).time()
|
| 337 |
+
compare(
|
| 338 |
+
Timestamp.combine(date_component, time_component),
|
| 339 |
+
datetime.combine(date_component, time_component),
|
| 340 |
+
)
|
| 341 |
+
|
| 342 |
+
def test_basics_nanos(self):
|
| 343 |
+
val = np.int64(946_684_800_000_000_000).view("M8[ns]")
|
| 344 |
+
stamp = Timestamp(val.view("i8") + 500)
|
| 345 |
+
assert stamp.year == 2000
|
| 346 |
+
assert stamp.month == 1
|
| 347 |
+
assert stamp.microsecond == 0
|
| 348 |
+
assert stamp.nanosecond == 500
|
| 349 |
+
|
| 350 |
+
# GH 14415
|
| 351 |
+
val = np.iinfo(np.int64).min + 80_000_000_000_000
|
| 352 |
+
stamp = Timestamp(val)
|
| 353 |
+
assert stamp.year == 1677
|
| 354 |
+
assert stamp.month == 9
|
| 355 |
+
assert stamp.day == 21
|
| 356 |
+
assert stamp.microsecond == 145224
|
| 357 |
+
assert stamp.nanosecond == 192
|
| 358 |
+
|
| 359 |
+
def test_roundtrip(self):
|
| 360 |
+
# test value to string and back conversions
|
| 361 |
+
# further test accessors
|
| 362 |
+
base = Timestamp("20140101 00:00:00").as_unit("ns")
|
| 363 |
+
|
| 364 |
+
result = Timestamp(base._value + Timedelta("5ms")._value)
|
| 365 |
+
assert result == Timestamp(f"{base}.005000")
|
| 366 |
+
assert result.microsecond == 5000
|
| 367 |
+
|
| 368 |
+
result = Timestamp(base._value + Timedelta("5us")._value)
|
| 369 |
+
assert result == Timestamp(f"{base}.000005")
|
| 370 |
+
assert result.microsecond == 5
|
| 371 |
+
|
| 372 |
+
result = Timestamp(base._value + Timedelta("5ns")._value)
|
| 373 |
+
assert result == Timestamp(f"{base}.000000005")
|
| 374 |
+
assert result.nanosecond == 5
|
| 375 |
+
assert result.microsecond == 0
|
| 376 |
+
|
| 377 |
+
result = Timestamp(base._value + Timedelta("6ms 5us")._value)
|
| 378 |
+
assert result == Timestamp(f"{base}.006005")
|
| 379 |
+
assert result.microsecond == 5 + 6 * 1000
|
| 380 |
+
|
| 381 |
+
result = Timestamp(base._value + Timedelta("200ms 5us")._value)
|
| 382 |
+
assert result == Timestamp(f"{base}.200005")
|
| 383 |
+
assert result.microsecond == 5 + 200 * 1000
|
| 384 |
+
|
| 385 |
+
def test_hash_equivalent(self):
|
| 386 |
+
d = {datetime(2011, 1, 1): 5}
|
| 387 |
+
stamp = Timestamp(datetime(2011, 1, 1))
|
| 388 |
+
assert d[stamp] == 5
|
| 389 |
+
|
| 390 |
+
@pytest.mark.parametrize(
|
| 391 |
+
"timezone, year, month, day, hour",
|
| 392 |
+
[["America/Chicago", 2013, 11, 3, 1], ["America/Santiago", 2021, 4, 3, 23]],
|
| 393 |
+
)
|
| 394 |
+
def test_hash_timestamp_with_fold(self, timezone, year, month, day, hour):
|
| 395 |
+
# see gh-33931
|
| 396 |
+
test_timezone = gettz(timezone)
|
| 397 |
+
transition_1 = Timestamp(
|
| 398 |
+
year=year,
|
| 399 |
+
month=month,
|
| 400 |
+
day=day,
|
| 401 |
+
hour=hour,
|
| 402 |
+
minute=0,
|
| 403 |
+
fold=0,
|
| 404 |
+
tzinfo=test_timezone,
|
| 405 |
+
)
|
| 406 |
+
transition_2 = Timestamp(
|
| 407 |
+
year=year,
|
| 408 |
+
month=month,
|
| 409 |
+
day=day,
|
| 410 |
+
hour=hour,
|
| 411 |
+
minute=0,
|
| 412 |
+
fold=1,
|
| 413 |
+
tzinfo=test_timezone,
|
| 414 |
+
)
|
| 415 |
+
assert hash(transition_1) == hash(transition_2)
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
class TestTimestampNsOperations:
|
| 419 |
+
def test_nanosecond_string_parsing(self):
|
| 420 |
+
ts = Timestamp("2013-05-01 07:15:45.123456789")
|
| 421 |
+
# GH 7878
|
| 422 |
+
expected_repr = "2013-05-01 07:15:45.123456789"
|
| 423 |
+
expected_value = 1_367_392_545_123_456_789
|
| 424 |
+
assert ts._value == expected_value
|
| 425 |
+
assert expected_repr in repr(ts)
|
| 426 |
+
|
| 427 |
+
ts = Timestamp("2013-05-01 07:15:45.123456789+09:00", tz="Asia/Tokyo")
|
| 428 |
+
assert ts._value == expected_value - 9 * 3600 * 1_000_000_000
|
| 429 |
+
assert expected_repr in repr(ts)
|
| 430 |
+
|
| 431 |
+
ts = Timestamp("2013-05-01 07:15:45.123456789", tz="UTC")
|
| 432 |
+
assert ts._value == expected_value
|
| 433 |
+
assert expected_repr in repr(ts)
|
| 434 |
+
|
| 435 |
+
ts = Timestamp("2013-05-01 07:15:45.123456789", tz="US/Eastern")
|
| 436 |
+
assert ts._value == expected_value + 4 * 3600 * 1_000_000_000
|
| 437 |
+
assert expected_repr in repr(ts)
|
| 438 |
+
|
| 439 |
+
# GH 10041
|
| 440 |
+
ts = Timestamp("20130501T071545.123456789")
|
| 441 |
+
assert ts._value == expected_value
|
| 442 |
+
assert expected_repr in repr(ts)
|
| 443 |
+
|
| 444 |
+
def test_nanosecond_timestamp(self):
|
| 445 |
+
# GH 7610
|
| 446 |
+
expected = 1_293_840_000_000_000_005
|
| 447 |
+
t = Timestamp("2011-01-01") + offsets.Nano(5)
|
| 448 |
+
assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')"
|
| 449 |
+
assert t._value == expected
|
| 450 |
+
assert t.nanosecond == 5
|
| 451 |
+
|
| 452 |
+
t = Timestamp(t)
|
| 453 |
+
assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')"
|
| 454 |
+
assert t._value == expected
|
| 455 |
+
assert t.nanosecond == 5
|
| 456 |
+
|
| 457 |
+
t = Timestamp("2011-01-01 00:00:00.000000005")
|
| 458 |
+
assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')"
|
| 459 |
+
assert t._value == expected
|
| 460 |
+
assert t.nanosecond == 5
|
| 461 |
+
|
| 462 |
+
expected = 1_293_840_000_000_000_010
|
| 463 |
+
t = t + offsets.Nano(5)
|
| 464 |
+
assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')"
|
| 465 |
+
assert t._value == expected
|
| 466 |
+
assert t.nanosecond == 10
|
| 467 |
+
|
| 468 |
+
t = Timestamp(t)
|
| 469 |
+
assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')"
|
| 470 |
+
assert t._value == expected
|
| 471 |
+
assert t.nanosecond == 10
|
| 472 |
+
|
| 473 |
+
t = Timestamp("2011-01-01 00:00:00.000000010")
|
| 474 |
+
assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')"
|
| 475 |
+
assert t._value == expected
|
| 476 |
+
assert t.nanosecond == 10
|
| 477 |
+
|
| 478 |
+
|
| 479 |
+
class TestTimestampConversion:
|
| 480 |
+
def test_conversion(self):
|
| 481 |
+
# GH#9255
|
| 482 |
+
ts = Timestamp("2000-01-01").as_unit("ns")
|
| 483 |
+
|
| 484 |
+
result = ts.to_pydatetime()
|
| 485 |
+
expected = datetime(2000, 1, 1)
|
| 486 |
+
assert result == expected
|
| 487 |
+
assert type(result) == type(expected)
|
| 488 |
+
|
| 489 |
+
result = ts.to_datetime64()
|
| 490 |
+
expected = np.datetime64(ts._value, "ns")
|
| 491 |
+
assert result == expected
|
| 492 |
+
assert type(result) == type(expected)
|
| 493 |
+
assert result.dtype == expected.dtype
|
| 494 |
+
|
| 495 |
+
def test_to_period_tz_warning(self):
|
| 496 |
+
# GH#21333 make sure a warning is issued when timezone
|
| 497 |
+
# info is lost
|
| 498 |
+
ts = Timestamp("2009-04-15 16:17:18", tz="US/Eastern")
|
| 499 |
+
with tm.assert_produces_warning(UserWarning):
|
| 500 |
+
# warning that timezone info will be lost
|
| 501 |
+
ts.to_period("D")
|
| 502 |
+
|
| 503 |
+
def test_to_numpy_alias(self):
|
| 504 |
+
# GH 24653: alias .to_numpy() for scalars
|
| 505 |
+
ts = Timestamp(datetime.now())
|
| 506 |
+
assert ts.to_datetime64() == ts.to_numpy()
|
| 507 |
+
|
| 508 |
+
# GH#44460
|
| 509 |
+
msg = "dtype and copy arguments are ignored"
|
| 510 |
+
with pytest.raises(ValueError, match=msg):
|
| 511 |
+
ts.to_numpy("M8[s]")
|
| 512 |
+
with pytest.raises(ValueError, match=msg):
|
| 513 |
+
ts.to_numpy(copy=True)
|
| 514 |
+
|
| 515 |
+
|
| 516 |
+
class TestNonNano:
|
| 517 |
+
@pytest.fixture(params=["s", "ms", "us"])
|
| 518 |
+
def reso(self, request):
|
| 519 |
+
return request.param
|
| 520 |
+
|
| 521 |
+
@pytest.fixture
|
| 522 |
+
def dt64(self, reso):
|
| 523 |
+
# cases that are in-bounds for nanosecond, so we can compare against
|
| 524 |
+
# the existing implementation.
|
| 525 |
+
return np.datetime64("2016-01-01", reso)
|
| 526 |
+
|
| 527 |
+
@pytest.fixture
|
| 528 |
+
def ts(self, dt64):
|
| 529 |
+
return Timestamp._from_dt64(dt64)
|
| 530 |
+
|
| 531 |
+
@pytest.fixture
|
| 532 |
+
def ts_tz(self, ts, tz_aware_fixture):
|
| 533 |
+
tz = maybe_get_tz(tz_aware_fixture)
|
| 534 |
+
return Timestamp._from_value_and_reso(ts._value, ts._creso, tz)
|
| 535 |
+
|
| 536 |
+
def test_non_nano_construction(self, dt64, ts, reso):
|
| 537 |
+
assert ts._value == dt64.view("i8")
|
| 538 |
+
|
| 539 |
+
if reso == "s":
|
| 540 |
+
assert ts._creso == NpyDatetimeUnit.NPY_FR_s.value
|
| 541 |
+
elif reso == "ms":
|
| 542 |
+
assert ts._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
| 543 |
+
elif reso == "us":
|
| 544 |
+
assert ts._creso == NpyDatetimeUnit.NPY_FR_us.value
|
| 545 |
+
|
| 546 |
+
def test_non_nano_fields(self, dt64, ts):
|
| 547 |
+
alt = Timestamp(dt64)
|
| 548 |
+
|
| 549 |
+
assert ts.year == alt.year
|
| 550 |
+
assert ts.month == alt.month
|
| 551 |
+
assert ts.day == alt.day
|
| 552 |
+
assert ts.hour == ts.minute == ts.second == ts.microsecond == 0
|
| 553 |
+
assert ts.nanosecond == 0
|
| 554 |
+
|
| 555 |
+
assert ts.to_julian_date() == alt.to_julian_date()
|
| 556 |
+
assert ts.weekday() == alt.weekday()
|
| 557 |
+
assert ts.isoweekday() == alt.isoweekday()
|
| 558 |
+
|
| 559 |
+
def test_start_end_fields(self, ts):
|
| 560 |
+
assert ts.is_year_start
|
| 561 |
+
assert ts.is_quarter_start
|
| 562 |
+
assert ts.is_month_start
|
| 563 |
+
assert not ts.is_year_end
|
| 564 |
+
assert not ts.is_month_end
|
| 565 |
+
assert not ts.is_month_end
|
| 566 |
+
|
| 567 |
+
# 2016-01-01 is a Friday, so is year/quarter/month start with this freq
|
| 568 |
+
assert ts.is_year_start
|
| 569 |
+
assert ts.is_quarter_start
|
| 570 |
+
assert ts.is_month_start
|
| 571 |
+
assert not ts.is_year_end
|
| 572 |
+
assert not ts.is_month_end
|
| 573 |
+
assert not ts.is_month_end
|
| 574 |
+
|
| 575 |
+
def test_day_name(self, dt64, ts):
|
| 576 |
+
alt = Timestamp(dt64)
|
| 577 |
+
assert ts.day_name() == alt.day_name()
|
| 578 |
+
|
| 579 |
+
def test_month_name(self, dt64, ts):
|
| 580 |
+
alt = Timestamp(dt64)
|
| 581 |
+
assert ts.month_name() == alt.month_name()
|
| 582 |
+
|
| 583 |
+
def test_tz_convert(self, ts):
|
| 584 |
+
ts = Timestamp._from_value_and_reso(ts._value, ts._creso, utc)
|
| 585 |
+
|
| 586 |
+
tz = pytz.timezone("US/Pacific")
|
| 587 |
+
result = ts.tz_convert(tz)
|
| 588 |
+
|
| 589 |
+
assert isinstance(result, Timestamp)
|
| 590 |
+
assert result._creso == ts._creso
|
| 591 |
+
assert tz_compare(result.tz, tz)
|
| 592 |
+
|
| 593 |
+
def test_repr(self, dt64, ts):
|
| 594 |
+
alt = Timestamp(dt64)
|
| 595 |
+
|
| 596 |
+
assert str(ts) == str(alt)
|
| 597 |
+
assert repr(ts) == repr(alt)
|
| 598 |
+
|
| 599 |
+
def test_comparison(self, dt64, ts):
|
| 600 |
+
alt = Timestamp(dt64)
|
| 601 |
+
|
| 602 |
+
assert ts == dt64
|
| 603 |
+
assert dt64 == ts
|
| 604 |
+
assert ts == alt
|
| 605 |
+
assert alt == ts
|
| 606 |
+
|
| 607 |
+
assert not ts != dt64
|
| 608 |
+
assert not dt64 != ts
|
| 609 |
+
assert not ts != alt
|
| 610 |
+
assert not alt != ts
|
| 611 |
+
|
| 612 |
+
assert not ts < dt64
|
| 613 |
+
assert not dt64 < ts
|
| 614 |
+
assert not ts < alt
|
| 615 |
+
assert not alt < ts
|
| 616 |
+
|
| 617 |
+
assert not ts > dt64
|
| 618 |
+
assert not dt64 > ts
|
| 619 |
+
assert not ts > alt
|
| 620 |
+
assert not alt > ts
|
| 621 |
+
|
| 622 |
+
assert ts >= dt64
|
| 623 |
+
assert dt64 >= ts
|
| 624 |
+
assert ts >= alt
|
| 625 |
+
assert alt >= ts
|
| 626 |
+
|
| 627 |
+
assert ts <= dt64
|
| 628 |
+
assert dt64 <= ts
|
| 629 |
+
assert ts <= alt
|
| 630 |
+
assert alt <= ts
|
| 631 |
+
|
| 632 |
+
def test_cmp_cross_reso(self):
|
| 633 |
+
# numpy gets this wrong because of silent overflow
|
| 634 |
+
dt64 = np.datetime64(9223372800, "s") # won't fit in M8[ns]
|
| 635 |
+
ts = Timestamp._from_dt64(dt64)
|
| 636 |
+
|
| 637 |
+
# subtracting 3600*24 gives a datetime64 that _can_ fit inside the
|
| 638 |
+
# nanosecond implementation bounds.
|
| 639 |
+
other = Timestamp(dt64 - 3600 * 24).as_unit("ns")
|
| 640 |
+
assert other < ts
|
| 641 |
+
assert other.asm8 > ts.asm8 # <- numpy gets this wrong
|
| 642 |
+
assert ts > other
|
| 643 |
+
assert ts.asm8 < other.asm8 # <- numpy gets this wrong
|
| 644 |
+
assert not other == ts
|
| 645 |
+
assert ts != other
|
| 646 |
+
|
| 647 |
+
@pytest.mark.xfail(reason="Dispatches to np.datetime64 which is wrong")
|
| 648 |
+
def test_cmp_cross_reso_reversed_dt64(self):
|
| 649 |
+
dt64 = np.datetime64(106752, "D") # won't fit in M8[ns]
|
| 650 |
+
ts = Timestamp._from_dt64(dt64)
|
| 651 |
+
other = Timestamp(dt64 - 1)
|
| 652 |
+
|
| 653 |
+
assert other.asm8 < ts
|
| 654 |
+
|
| 655 |
+
def test_pickle(self, ts, tz_aware_fixture):
|
| 656 |
+
tz = tz_aware_fixture
|
| 657 |
+
tz = maybe_get_tz(tz)
|
| 658 |
+
ts = Timestamp._from_value_and_reso(ts._value, ts._creso, tz)
|
| 659 |
+
rt = tm.round_trip_pickle(ts)
|
| 660 |
+
assert rt._creso == ts._creso
|
| 661 |
+
assert rt == ts
|
| 662 |
+
|
| 663 |
+
def test_normalize(self, dt64, ts):
|
| 664 |
+
alt = Timestamp(dt64)
|
| 665 |
+
result = ts.normalize()
|
| 666 |
+
assert result._creso == ts._creso
|
| 667 |
+
assert result == alt.normalize()
|
| 668 |
+
|
| 669 |
+
def test_asm8(self, dt64, ts):
|
| 670 |
+
rt = ts.asm8
|
| 671 |
+
assert rt == dt64
|
| 672 |
+
assert rt.dtype == dt64.dtype
|
| 673 |
+
|
| 674 |
+
def test_to_numpy(self, dt64, ts):
|
| 675 |
+
res = ts.to_numpy()
|
| 676 |
+
assert res == dt64
|
| 677 |
+
assert res.dtype == dt64.dtype
|
| 678 |
+
|
| 679 |
+
def test_to_datetime64(self, dt64, ts):
|
| 680 |
+
res = ts.to_datetime64()
|
| 681 |
+
assert res == dt64
|
| 682 |
+
assert res.dtype == dt64.dtype
|
| 683 |
+
|
| 684 |
+
def test_timestamp(self, dt64, ts):
|
| 685 |
+
alt = Timestamp(dt64)
|
| 686 |
+
assert ts.timestamp() == alt.timestamp()
|
| 687 |
+
|
| 688 |
+
def test_to_period(self, dt64, ts):
|
| 689 |
+
alt = Timestamp(dt64)
|
| 690 |
+
assert ts.to_period("D") == alt.to_period("D")
|
| 691 |
+
|
| 692 |
+
@pytest.mark.parametrize(
|
| 693 |
+
"td", [timedelta(days=4), Timedelta(days=4), np.timedelta64(4, "D")]
|
| 694 |
+
)
|
| 695 |
+
def test_addsub_timedeltalike_non_nano(self, dt64, ts, td):
|
| 696 |
+
exp_reso = max(ts._creso, Timedelta(td)._creso)
|
| 697 |
+
|
| 698 |
+
result = ts - td
|
| 699 |
+
expected = Timestamp(dt64) - td
|
| 700 |
+
assert isinstance(result, Timestamp)
|
| 701 |
+
assert result._creso == exp_reso
|
| 702 |
+
assert result == expected
|
| 703 |
+
|
| 704 |
+
result = ts + td
|
| 705 |
+
expected = Timestamp(dt64) + td
|
| 706 |
+
assert isinstance(result, Timestamp)
|
| 707 |
+
assert result._creso == exp_reso
|
| 708 |
+
assert result == expected
|
| 709 |
+
|
| 710 |
+
result = td + ts
|
| 711 |
+
expected = td + Timestamp(dt64)
|
| 712 |
+
assert isinstance(result, Timestamp)
|
| 713 |
+
assert result._creso == exp_reso
|
| 714 |
+
assert result == expected
|
| 715 |
+
|
| 716 |
+
def test_addsub_offset(self, ts_tz):
|
| 717 |
+
# specifically non-Tick offset
|
| 718 |
+
off = offsets.YearEnd(1)
|
| 719 |
+
result = ts_tz + off
|
| 720 |
+
|
| 721 |
+
assert isinstance(result, Timestamp)
|
| 722 |
+
assert result._creso == ts_tz._creso
|
| 723 |
+
if ts_tz.month == 12 and ts_tz.day == 31:
|
| 724 |
+
assert result.year == ts_tz.year + 1
|
| 725 |
+
else:
|
| 726 |
+
assert result.year == ts_tz.year
|
| 727 |
+
assert result.day == 31
|
| 728 |
+
assert result.month == 12
|
| 729 |
+
assert tz_compare(result.tz, ts_tz.tz)
|
| 730 |
+
|
| 731 |
+
result = ts_tz - off
|
| 732 |
+
|
| 733 |
+
assert isinstance(result, Timestamp)
|
| 734 |
+
assert result._creso == ts_tz._creso
|
| 735 |
+
assert result.year == ts_tz.year - 1
|
| 736 |
+
assert result.day == 31
|
| 737 |
+
assert result.month == 12
|
| 738 |
+
assert tz_compare(result.tz, ts_tz.tz)
|
| 739 |
+
|
| 740 |
+
def test_sub_datetimelike_mismatched_reso(self, ts_tz):
|
| 741 |
+
# case with non-lossy rounding
|
| 742 |
+
ts = ts_tz
|
| 743 |
+
|
| 744 |
+
# choose a unit for `other` that doesn't match ts_tz's;
|
| 745 |
+
# this construction ensures we get cases with other._creso < ts._creso
|
| 746 |
+
# and cases with other._creso > ts._creso
|
| 747 |
+
unit = {
|
| 748 |
+
NpyDatetimeUnit.NPY_FR_us.value: "ms",
|
| 749 |
+
NpyDatetimeUnit.NPY_FR_ms.value: "s",
|
| 750 |
+
NpyDatetimeUnit.NPY_FR_s.value: "us",
|
| 751 |
+
}[ts._creso]
|
| 752 |
+
other = ts.as_unit(unit)
|
| 753 |
+
assert other._creso != ts._creso
|
| 754 |
+
|
| 755 |
+
result = ts - other
|
| 756 |
+
assert isinstance(result, Timedelta)
|
| 757 |
+
assert result._value == 0
|
| 758 |
+
assert result._creso == max(ts._creso, other._creso)
|
| 759 |
+
|
| 760 |
+
result = other - ts
|
| 761 |
+
assert isinstance(result, Timedelta)
|
| 762 |
+
assert result._value == 0
|
| 763 |
+
assert result._creso == max(ts._creso, other._creso)
|
| 764 |
+
|
| 765 |
+
if ts._creso < other._creso:
|
| 766 |
+
# Case where rounding is lossy
|
| 767 |
+
other2 = other + Timedelta._from_value_and_reso(1, other._creso)
|
| 768 |
+
exp = ts.as_unit(other.unit) - other2
|
| 769 |
+
|
| 770 |
+
res = ts - other2
|
| 771 |
+
assert res == exp
|
| 772 |
+
assert res._creso == max(ts._creso, other._creso)
|
| 773 |
+
|
| 774 |
+
res = other2 - ts
|
| 775 |
+
assert res == -exp
|
| 776 |
+
assert res._creso == max(ts._creso, other._creso)
|
| 777 |
+
else:
|
| 778 |
+
ts2 = ts + Timedelta._from_value_and_reso(1, ts._creso)
|
| 779 |
+
exp = ts2 - other.as_unit(ts2.unit)
|
| 780 |
+
|
| 781 |
+
res = ts2 - other
|
| 782 |
+
assert res == exp
|
| 783 |
+
assert res._creso == max(ts._creso, other._creso)
|
| 784 |
+
res = other - ts2
|
| 785 |
+
assert res == -exp
|
| 786 |
+
assert res._creso == max(ts._creso, other._creso)
|
| 787 |
+
|
| 788 |
+
def test_sub_timedeltalike_mismatched_reso(self, ts_tz):
|
| 789 |
+
# case with non-lossy rounding
|
| 790 |
+
ts = ts_tz
|
| 791 |
+
|
| 792 |
+
# choose a unit for `other` that doesn't match ts_tz's;
|
| 793 |
+
# this construction ensures we get cases with other._creso < ts._creso
|
| 794 |
+
# and cases with other._creso > ts._creso
|
| 795 |
+
unit = {
|
| 796 |
+
NpyDatetimeUnit.NPY_FR_us.value: "ms",
|
| 797 |
+
NpyDatetimeUnit.NPY_FR_ms.value: "s",
|
| 798 |
+
NpyDatetimeUnit.NPY_FR_s.value: "us",
|
| 799 |
+
}[ts._creso]
|
| 800 |
+
other = Timedelta(0).as_unit(unit)
|
| 801 |
+
assert other._creso != ts._creso
|
| 802 |
+
|
| 803 |
+
result = ts + other
|
| 804 |
+
assert isinstance(result, Timestamp)
|
| 805 |
+
assert result == ts
|
| 806 |
+
assert result._creso == max(ts._creso, other._creso)
|
| 807 |
+
|
| 808 |
+
result = other + ts
|
| 809 |
+
assert isinstance(result, Timestamp)
|
| 810 |
+
assert result == ts
|
| 811 |
+
assert result._creso == max(ts._creso, other._creso)
|
| 812 |
+
|
| 813 |
+
if ts._creso < other._creso:
|
| 814 |
+
# Case where rounding is lossy
|
| 815 |
+
other2 = other + Timedelta._from_value_and_reso(1, other._creso)
|
| 816 |
+
exp = ts.as_unit(other.unit) + other2
|
| 817 |
+
res = ts + other2
|
| 818 |
+
assert res == exp
|
| 819 |
+
assert res._creso == max(ts._creso, other._creso)
|
| 820 |
+
res = other2 + ts
|
| 821 |
+
assert res == exp
|
| 822 |
+
assert res._creso == max(ts._creso, other._creso)
|
| 823 |
+
else:
|
| 824 |
+
ts2 = ts + Timedelta._from_value_and_reso(1, ts._creso)
|
| 825 |
+
exp = ts2 + other.as_unit(ts2.unit)
|
| 826 |
+
|
| 827 |
+
res = ts2 + other
|
| 828 |
+
assert res == exp
|
| 829 |
+
assert res._creso == max(ts._creso, other._creso)
|
| 830 |
+
res = other + ts2
|
| 831 |
+
assert res == exp
|
| 832 |
+
assert res._creso == max(ts._creso, other._creso)
|
| 833 |
+
|
| 834 |
+
def test_addition_doesnt_downcast_reso(self):
|
| 835 |
+
# https://github.com/pandas-dev/pandas/pull/48748#pullrequestreview-1122635413
|
| 836 |
+
ts = Timestamp(year=2022, month=1, day=1, microsecond=999999).as_unit("us")
|
| 837 |
+
td = Timedelta(microseconds=1).as_unit("us")
|
| 838 |
+
res = ts + td
|
| 839 |
+
assert res._creso == ts._creso
|
| 840 |
+
|
| 841 |
+
def test_sub_timedelta64_mismatched_reso(self, ts_tz):
|
| 842 |
+
ts = ts_tz
|
| 843 |
+
|
| 844 |
+
res = ts + np.timedelta64(1, "ns")
|
| 845 |
+
exp = ts.as_unit("ns") + np.timedelta64(1, "ns")
|
| 846 |
+
assert exp == res
|
| 847 |
+
assert exp._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
| 848 |
+
|
| 849 |
+
def test_min(self, ts):
|
| 850 |
+
assert ts.min <= ts
|
| 851 |
+
assert ts.min._creso == ts._creso
|
| 852 |
+
assert ts.min._value == NaT._value + 1
|
| 853 |
+
|
| 854 |
+
def test_max(self, ts):
|
| 855 |
+
assert ts.max >= ts
|
| 856 |
+
assert ts.max._creso == ts._creso
|
| 857 |
+
assert ts.max._value == np.iinfo(np.int64).max
|
| 858 |
+
|
| 859 |
+
def test_resolution(self, ts):
|
| 860 |
+
expected = Timedelta._from_value_and_reso(1, ts._creso)
|
| 861 |
+
result = ts.resolution
|
| 862 |
+
assert result == expected
|
| 863 |
+
assert result._creso == expected._creso
|
| 864 |
+
|
| 865 |
+
def test_out_of_ns_bounds(self):
|
| 866 |
+
# https://github.com/pandas-dev/pandas/issues/51060
|
| 867 |
+
result = Timestamp(-52700112000, unit="s")
|
| 868 |
+
assert result == Timestamp("0300-01-01")
|
| 869 |
+
assert result.to_numpy() == np.datetime64("0300-01-01T00:00:00", "s")
|
| 870 |
+
|
| 871 |
+
|
| 872 |
+
def test_timestamp_class_min_max_resolution():
|
| 873 |
+
# when accessed on the class (as opposed to an instance), we default
|
| 874 |
+
# to nanoseconds
|
| 875 |
+
assert Timestamp.min == Timestamp(NaT._value + 1)
|
| 876 |
+
assert Timestamp.min._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
| 877 |
+
|
| 878 |
+
assert Timestamp.max == Timestamp(np.iinfo(np.int64).max)
|
| 879 |
+
assert Timestamp.max._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
| 880 |
+
|
| 881 |
+
assert Timestamp.resolution == Timedelta(1)
|
| 882 |
+
assert Timestamp.resolution._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
| 883 |
+
|
| 884 |
+
|
| 885 |
+
def test_delimited_date():
|
| 886 |
+
# https://github.com/pandas-dev/pandas/issues/50231
|
| 887 |
+
with tm.assert_produces_warning(None):
|
| 888 |
+
result = Timestamp("13-01-2000")
|
| 889 |
+
expected = Timestamp(2000, 1, 13)
|
| 890 |
+
assert result == expected
|
| 891 |
+
|
| 892 |
+
|
| 893 |
+
def test_utctimetuple():
|
| 894 |
+
# GH 32174
|
| 895 |
+
ts = Timestamp("2000-01-01", tz="UTC")
|
| 896 |
+
result = ts.utctimetuple()
|
| 897 |
+
expected = time.struct_time((2000, 1, 1, 0, 0, 0, 5, 1, 0))
|
| 898 |
+
assert result == expected
|
| 899 |
+
|
| 900 |
+
|
| 901 |
+
def test_negative_dates():
|
| 902 |
+
# https://github.com/pandas-dev/pandas/issues/50787
|
| 903 |
+
ts = Timestamp("-2000-01-01")
|
| 904 |
+
msg = (
|
| 905 |
+
" not yet supported on Timestamps which are outside the range of "
|
| 906 |
+
"Python's standard library. For now, please call the components you need "
|
| 907 |
+
r"\(such as `.year` and `.month`\) and construct your string from there.$"
|
| 908 |
+
)
|
| 909 |
+
func = "^strftime"
|
| 910 |
+
with pytest.raises(NotImplementedError, match=func + msg):
|
| 911 |
+
ts.strftime("%Y")
|
| 912 |
+
|
| 913 |
+
msg = (
|
| 914 |
+
" not yet supported on Timestamps which "
|
| 915 |
+
"are outside the range of Python's standard library. "
|
| 916 |
+
)
|
| 917 |
+
func = "^date"
|
| 918 |
+
with pytest.raises(NotImplementedError, match=func + msg):
|
| 919 |
+
ts.date()
|
| 920 |
+
func = "^isocalendar"
|
| 921 |
+
with pytest.raises(NotImplementedError, match=func + msg):
|
| 922 |
+
ts.isocalendar()
|
| 923 |
+
func = "^timetuple"
|
| 924 |
+
with pytest.raises(NotImplementedError, match=func + msg):
|
| 925 |
+
ts.timetuple()
|
| 926 |
+
func = "^toordinal"
|
| 927 |
+
with pytest.raises(NotImplementedError, match=func + msg):
|
| 928 |
+
ts.toordinal()
|