Skip to content

Commit 6783f67

Browse files
committed
Merge remote-tracking branch 'upstream/master' into ci/simplify_deps
2 parents 86d0f15 + ccb25ab commit 6783f67

File tree

8 files changed

+73
-27
lines changed

8 files changed

+73
-27
lines changed

doc/source/whatsnew/v1.4.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,7 @@ Timedelta
734734
^^^^^^^^^
735735
- Bug in division of all-``NaT`` :class:`TimeDeltaIndex`, :class:`Series` or :class:`DataFrame` column with object-dtype arraylike of numbers failing to infer the result as timedelta64-dtype (:issue:`39750`)
736736
- Bug in floor division of ``timedelta64[ns]`` data with a scalar returning garbage values (:issue:`44466`)
737-
- Bug in :class:`Timedelta` now properly taking into account any nanoseconds contribution of any kwarg (:issue:`43764`)
737+
- Bug in :class:`Timedelta` now properly taking into account any nanoseconds contribution of any kwarg (:issue:`43764`, :issue:`45227`)
738738

739739
Timezones
740740
^^^^^^^^^

doc/source/whatsnew/v1.5.0.rst

-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ Datetimelike
124124
Timedelta
125125
^^^^^^^^^
126126
-
127-
-
128127

129128
Timezones
130129
^^^^^^^^^

pandas/_libs/tslibs/timedeltas.pyx

+4-1
Original file line numberDiff line numberDiff line change
@@ -1289,7 +1289,10 @@ class Timedelta(_Timedelta):
12891289
"(days,seconds....)")
12901290

12911291
kwargs = {key: _to_py_int_float(kwargs[key]) for key in kwargs}
1292-
if not cls._req_any_kwargs_new.intersection(kwargs):
1292+
1293+
unsupported_kwargs = set(kwargs)
1294+
unsupported_kwargs.difference_update(cls._req_any_kwargs_new)
1295+
if unsupported_kwargs or not cls._req_any_kwargs_new.intersection(kwargs):
12931296
raise ValueError(
12941297
"cannot construct a Timedelta from the passed arguments, "
12951298
"allowed keywords are "

pandas/core/arrays/sparse/array.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1704,13 +1704,14 @@ def _cmp_method(self, other, op) -> SparseArray:
17041704
op_name = op.__name__.strip("_")
17051705
return _sparse_array_op(self, other, op, op_name)
17061706
else:
1707+
# scalar
17071708
with np.errstate(all="ignore"):
17081709
fill_value = op(self.fill_value, other)
1709-
result = op(self.sp_values, other)
1710+
result = np.full(len(self), fill_value, dtype=np.bool_)
1711+
result[self.sp_index.indices] = op(self.sp_values, other)
17101712

17111713
return type(self)(
17121714
result,
1713-
sparse_index=self.sp_index,
17141715
fill_value=fill_value,
17151716
dtype=np.bool_,
17161717
)

pandas/tests/arrays/sparse/test_arithmetics.py

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class TestSparseArrayArithmetics:
3232
_klass = SparseArray
3333

3434
def _assert(self, a, b):
35+
# We have to use tm.assert_sp_array_equal. See GH #45126
3536
tm.assert_numpy_array_equal(a, b)
3637

3738
def _check_numeric_ops(self, a, b, a_dense, b_dense, mix: bool, op):

pandas/tests/arrays/sparse/test_array.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,8 @@ def test_scalar_with_index_infer_dtype(self, scalar, dtype):
248248
assert arr.dtype == dtype
249249
assert exp.dtype == dtype
250250

251-
# GH 23122
252251
def test_getitem_bool_sparse_array(self):
252+
# GH 23122
253253
spar_bool = SparseArray([False, True] * 5, dtype=np.bool8, fill_value=True)
254254
exp = SparseArray([np.nan, 2, np.nan, 5, 6])
255255
tm.assert_sp_array_equal(self.arr[spar_bool], exp)
@@ -266,6 +266,13 @@ def test_getitem_bool_sparse_array(self):
266266
exp = SparseArray([np.nan, 3, 5])
267267
tm.assert_sp_array_equal(res, exp)
268268

269+
def test_getitem_bool_sparse_array_as_comparison(self):
270+
# GH 45110
271+
arr = SparseArray([1, 2, 3, 4, np.nan, np.nan], fill_value=np.nan)
272+
res = arr[arr > 2]
273+
exp = SparseArray([3.0, 4.0], fill_value=np.nan)
274+
tm.assert_sp_array_equal(res, exp)
275+
269276
def test_get_item(self):
270277

271278
assert np.isnan(self.arr[1])

pandas/tests/extension/test_sparse.py

+39-21
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ def data_for_grouping(request):
100100
return SparseArray([1, 1, np.nan, np.nan, 2, 2, 1, 3], fill_value=request.param)
101101

102102

103+
@pytest.fixture(params=[0, np.nan])
104+
def data_for_compare(request):
105+
return SparseArray([0, 0, np.nan, -2, -1, 4, 2, 3, 0, 0], fill_value=request.param)
106+
107+
103108
class BaseSparseTests:
104109
def _check_unsupported(self, data):
105110
if data.dtype == SparseDtype(int, 0):
@@ -461,32 +466,45 @@ def _check_divmod_op(self, ser, op, other, exc=NotImplementedError):
461466
super()._check_divmod_op(ser, op, other, exc=None)
462467

463468

464-
class TestComparisonOps(BaseSparseTests, base.BaseComparisonOpsTests):
465-
def _compare_other(self, s, data, comparison_op, other):
469+
class TestComparisonOps(BaseSparseTests):
470+
def _compare_other(self, data_for_compare: SparseArray, comparison_op, other):
466471
op = comparison_op
467472

468-
# array
469-
result = pd.Series(op(data, other))
470-
# hard to test the fill value, since we don't know what expected
471-
# is in general.
472-
# Rely on tests in `tests/sparse` to validate that.
473-
assert isinstance(result.dtype, SparseDtype)
474-
assert result.dtype.subtype == np.dtype("bool")
475-
476-
with np.errstate(all="ignore"):
477-
expected = pd.Series(
478-
SparseArray(
479-
op(np.asarray(data), np.asarray(other)),
480-
fill_value=result.values.fill_value,
481-
)
473+
result = op(data_for_compare, other)
474+
assert isinstance(result, SparseArray)
475+
assert result.dtype.subtype == np.bool_
476+
477+
if isinstance(other, SparseArray):
478+
fill_value = op(data_for_compare.fill_value, other.fill_value)
479+
else:
480+
fill_value = np.all(
481+
op(np.asarray(data_for_compare.fill_value), np.asarray(other))
482482
)
483483

484-
tm.assert_series_equal(result, expected)
484+
expected = SparseArray(
485+
op(data_for_compare.to_dense(), np.asarray(other)),
486+
fill_value=fill_value,
487+
dtype=np.bool_,
488+
)
489+
tm.assert_sp_array_equal(result, expected)
485490

486-
# series
487-
ser = pd.Series(data)
488-
result = op(ser, other)
489-
tm.assert_series_equal(result, expected)
491+
def test_scalar(self, data_for_compare: SparseArray, comparison_op):
492+
self._compare_other(data_for_compare, comparison_op, 0)
493+
self._compare_other(data_for_compare, comparison_op, 1)
494+
self._compare_other(data_for_compare, comparison_op, -1)
495+
self._compare_other(data_for_compare, comparison_op, np.nan)
496+
497+
@pytest.mark.xfail(reason="Wrong indices")
498+
def test_array(self, data_for_compare: SparseArray, comparison_op):
499+
arr = np.linspace(-4, 5, 10)
500+
self._compare_other(data_for_compare, comparison_op, arr)
501+
502+
@pytest.mark.xfail(reason="Wrong indices")
503+
def test_sparse_array(self, data_for_compare: SparseArray, comparison_op):
504+
arr = data_for_compare + 1
505+
self._compare_other(data_for_compare, comparison_op, arr)
506+
arr = data_for_compare * 2
507+
self._compare_other(data_for_compare, comparison_op, arr)
490508

491509

492510
class TestPrinting(BaseSparseTests, base.BasePrintingTests):

pandas/tests/tslibs/test_timedeltas.py

+17
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import re
2+
13
import numpy as np
24
import pytest
35

@@ -46,3 +48,18 @@ def test_huge_nanoseconds_overflow():
4648
# GH 32402
4749
assert delta_to_nanoseconds(Timedelta(1e10)) == 1e10
4850
assert delta_to_nanoseconds(Timedelta(nanoseconds=1e10)) == 1e10
51+
52+
53+
@pytest.mark.parametrize(
54+
"kwargs", [{"Seconds": 1}, {"seconds": 1, "Nanoseconds": 1}, {"Foo": 2}]
55+
)
56+
def test_kwarg_assertion(kwargs):
57+
err_message = (
58+
"cannot construct a Timedelta from the passed arguments, "
59+
"allowed keywords are "
60+
"[weeks, days, hours, minutes, seconds, "
61+
"milliseconds, microseconds, nanoseconds]"
62+
)
63+
64+
with pytest.raises(ValueError, match=re.escape(err_message)):
65+
Timedelta(**kwargs)

0 commit comments

Comments
 (0)