def test_compare_mismatched_resolutions(self, comparison_op): # comparison that numpy gets wrong bc of silent overflows op = comparison_op iinfo = np.iinfo(np.int64) vals = np.array([iinfo.min, iinfo.min + 1, iinfo.max], dtype=np.int64) # Construct so that arr2[1] < arr[1] < arr[2] < arr2[2] arr = np.array(vals).view("M8[ns]") arr2 = arr.view("M8[s]") left = DatetimeArray._simple_new(arr, dtype=arr.dtype) right = DatetimeArray._simple_new(arr2, dtype=arr2.dtype) if comparison_op is operator.eq: expected = np.array([False, False, False]) elif comparison_op is operator.ne: expected = np.array([True, True, True]) elif comparison_op in [operator.lt, operator.le]: expected = np.array([False, False, True]) else: expected = np.array([False, True, False]) result = op(left, right) tm.assert_numpy_array_equal(result, expected) result = op(left[1], right) tm.assert_numpy_array_equal(result, expected) if op not in [operator.eq, operator.ne]: # check that numpy still gets this wrong; if it is fixed we may be # able to remove compare_mismatched_resolutions np_res = op(left._ndarray, right._ndarray) tm.assert_numpy_array_equal(np_res[1:], ~expected[1:])
def test_simple_new_requires_match(self, unit): arr = np.arange(5, dtype=np.int64).view(f"M8[{unit}]") dtype = DatetimeTZDtype(unit, "UTC") dta = DatetimeArray._simple_new(arr, dtype=dtype) assert dta.dtype == dtype wrong = DatetimeTZDtype("ns", "UTC") with pytest.raises(AssertionError, match=""): DatetimeArray._simple_new(arr, dtype=wrong)
def test_normalize(self, unit): dti = pd.date_range("2016-01-01 06:00:00", periods=55, freq="D") arr = np.asarray(dti).astype(f"M8[{unit}]") dta = DatetimeArray._simple_new(arr, dtype=arr.dtype) assert not dta.is_normalized # TODO: simplify once we can just .astype to other unit exp = np.asarray(dti.normalize()).astype(f"M8[{unit}]") expected = DatetimeArray._simple_new(exp, dtype=exp.dtype) res = dta.normalize() tm.assert_extension_array_equal(res, expected)
def make_block( values, placement, klass=None, ndim=None, dtype: Optional[Dtype] = None ) -> Block: """ This is a pseudo-public analogue to blocks.new_block. We ask that downstream libraries use this rather than any fully-internal APIs, including but not limited to: - core.internals.blocks.make_block - Block.make_block - Block.make_block_same_class - Block.__init__ """ values, dtype = extract_pandas_array(values, dtype, ndim) if klass is None: dtype = dtype or values.dtype klass = get_block_type(values, dtype) elif klass is DatetimeTZBlock and not is_datetime64tz_dtype(values.dtype): # pyarrow calls get here values = DatetimeArray._simple_new(values, dtype=dtype) if not isinstance(placement, BlockPlacement): placement = BlockPlacement(placement) ndim = _maybe_infer_ndim(values, placement, ndim) check_ndim(values, placement, ndim) return klass(values, ndim=ndim, placement=placement)
def test_non_nano(self, unit, reso, dtype): arr = np.arange(5, dtype=np.int64).view(f"M8[{unit}]") dta = DatetimeArray._simple_new(arr, dtype=dtype) assert dta.dtype == dtype assert dta[0]._reso == reso assert tz_compare(dta.tz, dta[0].tz) assert (dta[0] == dta[:1]).all()
def test_fields(self, unit, reso, field): dti = pd.date_range("2016-01-01", periods=55, freq="D") arr = np.asarray(dti).astype(f"M8[{unit}]") dta = DatetimeArray._simple_new(arr, dtype=arr.dtype) res = getattr(dta, field) expected = getattr(dti._data, field) tm.assert_numpy_array_equal(res, expected)
def test_infer_freq_non_nano(): arr = np.arange(10).astype(np.int64).view("M8[s]") dta = DatetimeArray._simple_new(arr, dtype=arr.dtype) res = frequencies.infer_freq(dta) assert res == "S" arr2 = arr.view("m8[ms]") tda = TimedeltaArray._simple_new(arr2, dtype=arr2.dtype) res2 = frequencies.infer_freq(tda) assert res2 == "L"
def test_std_non_nano(self, unit): dti = pd.date_range("2016-01-01", periods=55, freq="D") arr = np.asarray(dti).astype(f"M8[{unit}]") dta = DatetimeArray._simple_new(arr, dtype=arr.dtype) # we should match the nano-reso std, but floored to our reso. res = dta.std() assert res._reso == dta._reso assert res == dti.std().floor(unit)
def dta_dti(self, unit, dtype): tz = getattr(dtype, "tz", None) dti = pd.date_range("2016-01-01", periods=55, freq="D", tz=tz) if tz is None: arr = np.asarray(dti).astype(f"M8[{unit}]") else: arr = np.asarray(dti.tz_convert("UTC").tz_localize(None)).astype( f"M8[{unit}]" ) dta = DatetimeArray._simple_new(arr, dtype=dtype) return dta, dti
def make_block(values, placement, klass=None, ndim=None, dtype: Optional[Dtype] = None) -> Block: """ This is a pseudo-public analogue to blocks.new_block. We ask that downstream libraries use this rather than any fully-internal APIs, including but not limited to: - core.internals.blocks.make_block - Block.make_block - Block.make_block_same_class - Block.__init__ """ if isinstance(values, ABCPandasArray): # Ensure that we don't allow PandasArray / PandasDtype in internals. # For now, blocks should be backed by ndarrays when possible. values = values.to_numpy() if ndim and ndim > 1: # TODO(EA2D): special case not needed with 2D EAs values = np.atleast_2d(values) if isinstance(dtype, PandasDtype): dtype = dtype.numpy_dtype if klass is None: dtype = dtype or values.dtype klass = get_block_type(values, dtype) elif klass is DatetimeTZBlock and not is_datetime64tz_dtype(values.dtype): # TODO: This is no longer hit internally; does it need to be retained # for e.g. pyarrow? values = DatetimeArray._simple_new(values, dtype=dtype) if not isinstance(placement, BlockPlacement): placement = BlockPlacement(placement) if ndim is None: # GH#38134 Block constructor now assumes ndim is not None if not isinstance(values.dtype, np.dtype): if len(placement) != 1: ndim = 1 else: ndim = 2 else: ndim = values.ndim return klass(values, ndim=ndim, placement=placement)
def make_block(values, placement, klass=None, ndim=None, dtype: Dtype | None = None) -> Block: """ This is a pseudo-public analogue to blocks.new_block. We ask that downstream libraries use this rather than any fully-internal APIs, including but not limited to: - core.internals.blocks.make_block - Block.make_block - Block.make_block_same_class - Block.__init__ """ if dtype is not None: dtype = pandas_dtype(dtype) values, dtype = extract_pandas_array(values, dtype, ndim) if klass is ExtensionBlock and is_period_dtype(values.dtype): # GH-44681 changed PeriodArray to be stored in the 2D # NDArrayBackedExtensionBlock instead of ExtensionBlock # -> still allow ExtensionBlock to be passed in this case for back compat klass = None if klass is None: dtype = dtype or values.dtype klass = get_block_type(dtype) elif klass is DatetimeTZBlock and not is_datetime64tz_dtype(values.dtype): # pyarrow calls get here values = DatetimeArray._simple_new(values, dtype=dtype) if not isinstance(placement, BlockPlacement): placement = BlockPlacement(placement) ndim = maybe_infer_ndim(values, placement, ndim) if is_datetime64tz_dtype(values.dtype) or is_period_dtype(values.dtype): # GH#41168 ensure we can pass 1D dt64tz values # More generally, any EA dtype that isn't is_1d_only_ea_dtype values = extract_array(values, extract_numpy=True) values = ensure_block_shape(values, ndim) check_ndim(values, placement, ndim) values = maybe_coerce_values(values) return klass(values, ndim=ndim, placement=placement)
def make_block(values, placement, klass=None, ndim=None, dtype: Dtype | None = None) -> Block: """ This is a pseudo-public analogue to blocks.new_block. We ask that downstream libraries use this rather than any fully-internal APIs, including but not limited to: - core.internals.blocks.make_block - Block.make_block - Block.make_block_same_class - Block.__init__ """ if dtype is not None: dtype = pandas_dtype(dtype) values, dtype = extract_pandas_array(values, dtype, ndim) if klass is None: dtype = dtype or values.dtype klass = get_block_type(values, dtype) elif klass is DatetimeTZBlock and not is_datetime64tz_dtype(values.dtype): # pyarrow calls get here values = DatetimeArray._simple_new(values, dtype=dtype) if not isinstance(placement, BlockPlacement): placement = BlockPlacement(placement) ndim = maybe_infer_ndim(values, placement, ndim) if is_datetime64tz_dtype(values.dtype): # GH#41168 ensure we can pass 1D dt64tz values values = extract_array(values, extract_numpy=True) values = ensure_block_shape(values, ndim) check_ndim(values, placement, ndim) values = maybe_coerce_values(values) return klass(values, ndim=ndim, placement=placement)
def make_block(values, placement, klass=None, ndim=None, dtype: Optional[Dtype] = None) -> Block: """ This is a pseudo-public analogue to blocks.new_block. We ask that downstream libraries use this rather than any fully-internal APIs, including but not limited to: - core.internals.blocks.make_block - Block.make_block - Block.make_block_same_class - Block.__init__ """ # error: Argument 2 to "extract_pandas_array" has incompatible type # "Union[ExtensionDtype, str, dtype[Any], Type[str], Type[float], Type[int], # Type[complex], Type[bool], Type[object], None]"; expected "Union[dtype[Any], # ExtensionDtype, None]" values, dtype = extract_pandas_array(values, dtype, ndim) # type: ignore[arg-type] if klass is None: dtype = dtype or values.dtype klass = get_block_type(values, dtype) elif klass is DatetimeTZBlock and not is_datetime64tz_dtype(values.dtype): # pyarrow calls get here values = DatetimeArray._simple_new(values, dtype=dtype) if not isinstance(placement, BlockPlacement): placement = BlockPlacement(placement) ndim = _maybe_infer_ndim(values, placement, ndim) check_ndim(values, placement, ndim) values = maybe_coerce_values(values) return klass(values, ndim=ndim, placement=placement)
def test_add_datetimelike_scalar(self, tda, tz_naive_fixture): ts = pd.Timestamp("2016-01-01", tz=tz_naive_fixture) msg = "with mis-matched resolutions" with pytest.raises(NotImplementedError, match=msg): # mismatched reso -> check that we don't give an incorrect result tda + ts with pytest.raises(NotImplementedError, match=msg): # mismatched reso -> check that we don't give an incorrect result ts + tda ts = ts._as_unit(tda._unit) exp_values = tda._ndarray + ts.asm8 expected = (DatetimeArray._simple_new( exp_values, dtype=exp_values.dtype).tz_localize("UTC").tz_convert(ts.tz)) result = tda + ts tm.assert_extension_array_equal(result, expected) result = ts + tda tm.assert_extension_array_equal(result, expected)
def test_non_nano(self, unit, reso): arr = np.arange(5, dtype=np.int64).view(f"M8[{unit}]") dta = DatetimeArray._simple_new(arr, dtype=arr.dtype) assert dta.dtype == arr.dtype assert dta[0]._reso == reso