diff --git a/python/cudf/cudf/core/frame.py b/python/cudf/cudf/core/frame.py index de39e0f0631..67650a09f3b 100644 --- a/python/cudf/cudf/core/frame.py +++ b/python/cudf/cudf/core/frame.py @@ -3401,6 +3401,30 @@ def _colwise_binop( col, (left_column, right_column, reflect, fill_value), ) in operands.items(): + + # Handle object columns that are empty or + # all nulls when performing binary operations + if ( + left_column.dtype == "object" + and left_column.null_count == len(left_column) + and fill_value is None + ): + if fn in ( + "add", + "sub", + "mul", + "mod", + "pow", + "truediv", + "floordiv", + ): + output[col] = left_column + elif fn in ("eq", "lt", "le", "gt", "ge"): + output[col] = left_column.notnull() + elif fn == "ne": + output[col] = left_column.isnull() + continue + if right_column is cudf.NA: right_column = cudf.Scalar( right_column, dtype=left_column.dtype diff --git a/python/cudf/cudf/tests/test_binops.py b/python/cudf/cudf/tests/test_binops.py index 13019ca11f9..8277b8e7b32 100644 --- a/python/cudf/cudf/tests/test_binops.py +++ b/python/cudf/cudf/tests/test_binops.py @@ -37,6 +37,15 @@ operator.pow, ] +_binops_compare = [ + operator.eq, + operator.ne, + operator.lt, + operator.le, + operator.gt, + operator.ge, +] + @pytest.mark.parametrize("obj_class", ["Series", "Index"]) @pytest.mark.parametrize("binop", _binops) @@ -2888,3 +2897,19 @@ def test_binops_non_cudf_types(obj_class, binop, other_type): lhs = obj_class(data) rhs = other_type(data) assert cp.all((binop(lhs, rhs) == binop(lhs, lhs)).values) + + +@pytest.mark.parametrize("binop", _binops + _binops_compare) +@pytest.mark.parametrize("data", [None, [-9, 7], [5, -2], [12, 18]]) +@pytest.mark.parametrize("scalar", [1, 3, 12, np.nan]) +def test_empty_column(binop, data, scalar): + gdf = cudf.DataFrame(columns=["a", "b"]) + if data is not None: + gdf["a"] = data + + pdf = gdf.to_pandas() + + got = binop(gdf, scalar) + expected = binop(pdf, scalar) + + utils.assert_eq(expected, got)