Skip to content

TST: parameterize more tests #45131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 41 additions & 45 deletions pandas/tests/arithmetic/test_datetime64.py
Original file line number Diff line number Diff line change
Expand Up @@ -1810,55 +1810,51 @@ def test_dt64ser_sub_datetime_dtype(self):
# TODO: This next block of tests came from tests.series.test_operators,
# needs to be de-duplicated and parametrized over `box` classes

def test_operators_datetimelike_invalid(self, all_arithmetic_operators):
# these are all TypeEror ops
@pytest.mark.parametrize(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont think this improves the clarity of this test. id suggest something like:

def test_whatever(all_arithmetic_operators):
    op_str = all_arithmetic_operators
    if op_str in [add, sub, radd, rsub]:
        return # or skip, or use a different fixture; these are tested elsewhere

    tdser = [timedelta(minutes=5, seconds=3), NaT, timedelta(minutes=5, seconds=3)]
    dt64ser = [Timestamp("20111230"), Timestamp("20120101"), NaT]
    dt64tz_ser = ...

    for iter_over_pairs([tdser, dt64ser, dt64tz_ser]):
         with pytest.raises...
               op(whatever)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm at least with this version an error with any of the dtlike-ser won't prevent the other dtlike-ser from being tested due to the parameterize

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. I might be overly-wary of over-parametrizing in the arithmetic tests since there's a ton of them

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully with a second pass we can remove the need for this test entirely.

"left, right, op_fail",
[
[
[Timestamp("20111230"), Timestamp("20120101"), NaT],
[Timestamp("20111231"), Timestamp("20120102"), Timestamp("20120104")],
["__sub__", "__rsub__"],
],
[
[Timestamp("20111230"), Timestamp("20120101"), NaT],
[timedelta(minutes=5, seconds=3), timedelta(minutes=5, seconds=3), NaT],
["__add__", "__radd__", "__sub__"],
],
[
[
Timestamp("20111230", tz="US/Eastern"),
Timestamp("20111230", tz="US/Eastern"),
NaT,
],
[timedelta(minutes=5, seconds=3), NaT, timedelta(minutes=5, seconds=3)],
["__add__", "__radd__", "__sub__"],
],
],
)
def test_operators_datetimelike_invalid(
self, left, right, op_fail, all_arithmetic_operators
):
# these are all TypeError ops
op_str = all_arithmetic_operators

def check(get_ser, test_ser):

# check that we are getting a TypeError
# with 'operate' (from core/ops.py) for the ops that are not
# defined
op = getattr(get_ser, op_str, None)
# Previously, _validate_for_numeric_binop in core/indexes/base.py
# did this for us.
arg1 = Series(left)
arg2 = Series(right)
# check that we are getting a TypeError
# with 'operate' (from core/ops.py) for the ops that are not
# defined
op = getattr(arg1, op_str, None)
# Previously, _validate_for_numeric_binop in core/indexes/base.py
# did this for us.
if op_str not in op_fail:
with pytest.raises(
TypeError, match="operate|[cC]annot|unsupported operand"
):
op(test_ser)

# ## timedelta64 ###
td1 = Series([timedelta(minutes=5, seconds=3)] * 3)
td1.iloc[2] = np.nan

# ## datetime64 ###
dt1 = Series(
[Timestamp("20111230"), Timestamp("20120101"), Timestamp("20120103")]
)
dt1.iloc[2] = np.nan
dt2 = Series(
[Timestamp("20111231"), Timestamp("20120102"), Timestamp("20120104")]
)
if op_str not in ["__sub__", "__rsub__"]:
check(dt1, dt2)

# ## datetime64 with timetimedelta ###
# TODO(jreback) __rsub__ should raise?
if op_str not in ["__add__", "__radd__", "__sub__"]:
check(dt1, td1)

# 8260, 10763
# datetime64 with tz
tz = "US/Eastern"
dt1 = Series(date_range("2000-01-01 09:00:00", periods=5, tz=tz), name="foo")
dt2 = dt1.copy()
dt2.iloc[2] = np.nan
td1 = Series(pd.timedelta_range("1 days 1 min", periods=5, freq="H"))
td2 = td1.copy()
td2.iloc[1] = np.nan

if op_str not in ["__add__", "__radd__", "__sub__", "__rsub__"]:
check(dt2, td2)
op(arg2)
else:
# Smoke test
op(arg2)

def test_sub_single_tz(self):
# GH#12290
Expand Down
122 changes: 55 additions & 67 deletions pandas/tests/arithmetic/test_numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ def adjust_negative_zero(zero, expected):
return expected


def compare_op(series, other, op):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

something like this you might want o make a first class assert_* function (but ok for now)

left = np.abs(series) if op in (ops.rpow, operator.pow) else series
right = np.abs(other) if op in (ops.rpow, operator.pow) else other

cython_or_numpy = op(left, right)
python = left.combine(right, op)
if isinstance(other, Series) and not other.index.equals(series.index):
python.index = python.index._with_freq(None)
tm.assert_series_equal(cython_or_numpy, python)


# TODO: remove this kludge once mypy stops giving false positives here
# List comprehension has incompatible type List[PandasObject]; expected List[RangeIndex]
# See GH#29725
Expand Down Expand Up @@ -959,77 +970,54 @@ def test_frame_operators(self, float_frame):
assert (df + df).equals(df)
tm.assert_frame_equal(df + df, df)

# TODO: taken from tests.series.test_operators; needs cleanup
def test_series_operators(self):
def _check_op(series, other, op, pos_only=False):
left = np.abs(series) if pos_only else series
right = np.abs(other) if pos_only else other

cython_or_numpy = op(left, right)
python = left.combine(right, op)
if isinstance(other, Series) and not other.index.equals(series.index):
python.index = python.index._with_freq(None)
tm.assert_series_equal(cython_or_numpy, python)

def check(series, other):
simple_ops = ["add", "sub", "mul", "truediv", "floordiv", "mod"]

for opname in simple_ops:
_check_op(series, other, getattr(operator, opname))
@pytest.mark.parametrize(
"func",
[lambda x: x * 2, lambda x: x[::2], lambda x: 5],
ids=["multiply", "slice", "constant"],
)
def test_series_operators_arithmetic(self, all_arithmetic_functions, func):
op = all_arithmetic_functions
series = tm.makeTimeSeries().rename("ts")
other = func(series)
compare_op(series, other, op)

_check_op(series, other, operator.pow, pos_only=True)
@pytest.mark.parametrize(
"func", [lambda x: x + 1, lambda x: 5], ids=["add", "constant"]
)
def test_series_operators_compare(self, comparison_op, func):
op = comparison_op
series = tm.makeTimeSeries().rename("ts")
other = func(series)
compare_op(series, other, op)

_check_op(series, other, ops.radd)
_check_op(series, other, ops.rsub)
_check_op(series, other, ops.rtruediv)
_check_op(series, other, ops.rfloordiv)
_check_op(series, other, ops.rmul)
_check_op(series, other, ops.rpow, pos_only=True)
_check_op(series, other, ops.rmod)
@pytest.mark.parametrize(
"func",
[lambda x: x * 2, lambda x: x[::2], lambda x: 5],
ids=["multiply", "slice", "constant"],
)
def test_divmod(self, func):
series = tm.makeTimeSeries().rename("ts")
other = func(series)
results = divmod(series, other)
if isinstance(other, abc.Iterable) and len(series) != len(other):
# if the lengths don't match, this is the test where we use
# `tser[::2]`. Pad every other value in `other_np` with nan.
other_np = []
for n in other:
other_np.append(n)
other_np.append(np.nan)
else:
other_np = other
other_np = np.asarray(other_np)
with np.errstate(all="ignore"):
expecteds = divmod(series.values, np.asarray(other_np))

tser = tm.makeTimeSeries().rename("ts")
check(tser, tser * 2)
check(tser, tser[::2])
check(tser, 5)

def check_comparators(series, other):
_check_op(series, other, operator.gt)
_check_op(series, other, operator.ge)
_check_op(series, other, operator.eq)
_check_op(series, other, operator.lt)
_check_op(series, other, operator.le)

check_comparators(tser, 5)
check_comparators(tser, tser + 1)

# TODO: taken from tests.series.test_operators; needs cleanup
def test_divmod(self):
def check(series, other):
results = divmod(series, other)
if isinstance(other, abc.Iterable) and len(series) != len(other):
# if the lengths don't match, this is the test where we use
# `tser[::2]`. Pad every other value in `other_np` with nan.
other_np = []
for n in other:
other_np.append(n)
other_np.append(np.nan)
else:
other_np = other
other_np = np.asarray(other_np)
with np.errstate(all="ignore"):
expecteds = divmod(series.values, np.asarray(other_np))

for result, expected in zip(results, expecteds):
# check the values, name, and index separately
tm.assert_almost_equal(np.asarray(result), expected)

assert result.name == series.name
tm.assert_index_equal(result.index, series.index._with_freq(None))
for result, expected in zip(results, expecteds):
# check the values, name, and index separately
tm.assert_almost_equal(np.asarray(result), expected)

tser = tm.makeTimeSeries().rename("ts")
check(tser, tser * 2)
check(tser, tser[::2])
check(tser, 5)
assert result.name == series.name
tm.assert_index_equal(result.index, series.index._with_freq(None))

def test_series_divmod_zero(self):
# Check that divmod uses pandas convention for division by zero,
Expand Down
50 changes: 29 additions & 21 deletions pandas/tests/indexing/test_scalar.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,31 +47,39 @@ def _check(f, func, values=False):
_check(f, "at")

@pytest.mark.parametrize("kind", ["series", "frame"])
def test_at_and_iat_set(self, kind):
def _check(f, func, values=False):
@pytest.mark.parametrize("col", ["ints", "uints"])
def test_iat_set_ints(self, kind, col):
f = getattr(self, kind)[col]
if f is not None:
indices = self.generate_indices(f, True)
for i in indices:
f.iat[i] = 1
expected = self.get_value("iat", f, i, True)
tm.assert_almost_equal(expected, 1)

if f is not None:
indices = self.generate_indices(f, values)
@pytest.mark.parametrize("kind", ["series", "frame"])
@pytest.mark.parametrize("col", ["labels", "ts", "floats"])
def test_iat_set_other(self, kind, col):
f = getattr(self, kind)[col]
if f is not None:
msg = "iAt based indexing can only have integer indexers"
with pytest.raises(ValueError, match=msg):
indices = self.generate_indices(f, False)
for i in indices:
getattr(f, func)[i] = 1
expected = self.get_value(func, f, i, values)
f.iat[i] = 1
expected = self.get_value("iat", f, i, False)
tm.assert_almost_equal(expected, 1)

d = getattr(self, kind)

# iat
for f in [d["ints"], d["uints"]]:
_check(f, "iat", values=True)

for f in [d["labels"], d["ts"], d["floats"]]:
if f is not None:
msg = "iAt based indexing can only have integer indexers"
with pytest.raises(ValueError, match=msg):
_check(f, "iat")

# at
for f in [d["ints"], d["uints"], d["labels"], d["ts"], d["floats"]]:
_check(f, "at")
@pytest.mark.parametrize("kind", ["series", "frame"])
@pytest.mark.parametrize("col", ["ints", "uints", "labels", "ts", "floats"])
def test_at_set_ints_other(self, kind, col):
f = getattr(self, kind)[col]
if f is not None:
indices = self.generate_indices(f, False)
for i in indices:
f.at[i] = 1
expected = self.get_value("at", f, i, False)
tm.assert_almost_equal(expected, 1)


class TestAtAndiAT:
Expand Down
Loading