From 53e0d05453dbe7fb2ea0d284f58ac0abb6d99b18 Mon Sep 17 00:00:00 2001 From: galipremsagar Date: Tue, 26 Dec 2023 22:29:46 +0000 Subject: [PATCH] Deprecate ignoring empty objects in concat --- python/cudf/cudf/core/dataframe.py | 36 ++-- python/cudf/cudf/core/groupby/groupby.py | 12 +- python/cudf/cudf/core/index.py | 14 +- python/cudf/cudf/core/join/_join_helpers.py | 9 +- python/cudf/cudf/core/multiindex.py | 21 ++- python/cudf/cudf/core/reshape.py | 14 +- python/cudf/cudf/core/series.py | 4 +- python/cudf/cudf/io/parquet.py | 16 +- python/cudf/cudf/tests/test_concat.py | 187 ++++++++++++-------- python/cudf/cudf/tests/test_dataframe.py | 72 +++++--- python/cudf/cudf/tests/test_index.py | 22 ++- 11 files changed, 263 insertions(+), 144 deletions(-) diff --git a/python/cudf/cudf/core/dataframe.py b/python/cudf/cudf/core/dataframe.py index 50fe5adebf8..bfb5fbe4d48 100644 --- a/python/cudf/cudf/core/dataframe.py +++ b/python/cudf/cudf/core/dataframe.py @@ -910,7 +910,9 @@ def _init_from_series_list(self, data, columns, index): transpose = self.T else: - concat_df = cudf.concat(data, axis=1) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + concat_df = cudf.concat(data, axis=1) cols = concat_df._data.to_pandas_index() if cols.dtype == "object": @@ -1920,9 +1922,11 @@ def _get_renderable_dataframe(self): lower_left = self.tail(lower_rows).iloc[:, :left_cols] lower_right = self.tail(lower_rows).iloc[:, right_cols:] - upper = cudf.concat([upper_left, upper_right], axis=1) - lower = cudf.concat([lower_left, lower_right], axis=1) - output = cudf.concat([upper, lower]) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + upper = cudf.concat([upper_left, upper_right], axis=1) + lower = cudf.concat([lower_left, lower_right], axis=1) + output = cudf.concat([upper, lower]) output = self._clean_nulls_from_dataframe(output) output._index = output._index._clean_nulls_from_index() @@ -5154,14 +5158,17 @@ def describe( None, ) - return cudf.concat( - [ - series.reindex(names, copy=False) - for series in describe_series_list - ], - axis=1, - sort=False, - ) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + res = cudf.concat( + [ + series.reindex(names, copy=False) + for series in describe_series_list + ], + axis=1, + sort=False, + ) + return res @_cudf_nvtx_annotate def to_pandas(self, *, nullable: bool = False) -> pd.DataFrame: @@ -6258,7 +6265,10 @@ def mode(self, axis=0, numeric_only=False, dropna=True): if len(mode_results) == 0: return DataFrame() - df = cudf.concat(mode_results, axis=1) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + df = cudf.concat(mode_results, axis=1) + if isinstance(df, Series): df = df.to_frame() diff --git a/python/cudf/cudf/core/groupby/groupby.py b/python/cudf/cudf/core/groupby/groupby.py index 0262e586807..849ec46f74d 100644 --- a/python/cudf/cudf/core/groupby/groupby.py +++ b/python/cudf/cudf/core/groupby/groupby.py @@ -1319,13 +1319,17 @@ def _post_process_chunk_results( # group is a row-like "Series" where the index labels # are the same as the original calling DataFrame if _is_row_of(chunk_results[0], self.obj): - result = cudf.concat(chunk_results, axis=1).T + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + result = cudf.concat(chunk_results, axis=1).T result.index = group_names result.index.names = self.grouping.names # When the UDF is like df.x + df.y, the result for each # group is the same length as the original group elif len(self.obj) == sum(len(chk) for chk in chunk_results): - result = cudf.concat(chunk_results) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + result = cudf.concat(chunk_results) index_data = group_keys._data.copy(deep=True) index_data[None] = grouped_values.index._column result.index = cudf.MultiIndex._from_data(index_data) @@ -1336,7 +1340,9 @@ def _post_process_chunk_results( f"type {type(chunk_results[0])}" ) else: - result = cudf.concat(chunk_results) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + result = cudf.concat(chunk_results) if self._group_keys: index_data = group_keys._data.copy(deep=True) index_data[None] = grouped_values.index._column diff --git a/python/cudf/cudf/core/index.py b/python/cudf/cudf/core/index.py index 3cce1ab515e..25a58d77830 100644 --- a/python/cudf/cudf/core/index.py +++ b/python/cudf/cudf/core/index.py @@ -1103,6 +1103,16 @@ def _values(self): @_cudf_nvtx_annotate def _concat(cls, objs): non_empties = [index for index in objs if len(index)] + if len(objs) != len(non_empties): + # Do not remove until pandas-3.0 support is added. + warnings.warn( + "The behavior of array concatenation with empty entries is " + "deprecated. In a future version, this will no longer exclude " + "empty items when determining the result dtype. " + "To retain the old behavior, exclude the empty entries before " + "the concat operation.", + FutureWarning, + ) if all(isinstance(obj, RangeIndex) for obj in non_empties): result = _concat_range_index(non_empties) else: @@ -1300,7 +1310,9 @@ def __repr__(self): top = self[0:mr] bottom = self[-1 * mr :] - preprocess = cudf.concat([top, bottom]) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + preprocess = cudf.concat([top, bottom]) else: preprocess = self diff --git a/python/cudf/cudf/core/join/_join_helpers.py b/python/cudf/cudf/core/join/_join_helpers.py index 1071261044f..822c1848d58 100644 --- a/python/cudf/cudf/core/join/_join_helpers.py +++ b/python/cudf/cudf/core/join/_join_helpers.py @@ -2,6 +2,7 @@ from __future__ import annotations +import warnings from collections import abc from typing import TYPE_CHECKING, Any, Tuple, cast @@ -170,9 +171,11 @@ def _match_categorical_dtypes_both( return lcol, rcol.astype(ltype) else: # merge categories - merged_categories = cudf.concat( - [ltype.categories, rtype.categories] - ).unique() + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + merged_categories = cudf.concat( + [ltype.categories, rtype.categories] + ).unique() common_type = cudf.CategoricalDtype( categories=merged_categories, ordered=False ) diff --git a/python/cudf/cudf/core/multiindex.py b/python/cudf/cudf/core/multiindex.py index 5c2b4e6c7b0..dcef65f0046 100644 --- a/python/cudf/cudf/core/multiindex.py +++ b/python/cudf/cudf/core/multiindex.py @@ -6,6 +6,7 @@ import numbers import operator import pickle +import warnings from collections import abc from functools import cached_property from numbers import Integral @@ -717,15 +718,17 @@ def _compute_validity_mask(self, index, row_tuple, max_length): continue lookup[i] = cudf.Series(row) frame = cudf.DataFrame(dict(enumerate(index._data.columns))) - data_table = cudf.concat( - [ - frame, - cudf.DataFrame( - {"idx": cudf.Series(column.arange(len(frame)))} - ), - ], - axis=1, - ) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + data_table = cudf.concat( + [ + frame, + cudf.DataFrame( + {"idx": cudf.Series(column.arange(len(frame)))} + ), + ], + axis=1, + ) # Sort indices in pandas compatible mode # because we want the indices to be fetched # in a deterministic order. diff --git a/python/cudf/cudf/core/reshape.py b/python/cudf/cudf/core/reshape.py index 7a80d70acb3..465186d81d2 100644 --- a/python/cudf/cudf/core/reshape.py +++ b/python/cudf/cudf/core/reshape.py @@ -5,6 +5,7 @@ from typing import Dict, Optional import cupy +import warnings import numpy as np import pandas as pd @@ -320,9 +321,20 @@ def concat(objs, axis=0, join="outer", ignore_index=False, sort=None): df = cudf.DataFrame() _normalize_series_and_dataframe(objs, axis=axis) + any_empty = any(obj.empty for obj in objs) + if any_empty: + # Do not remove until pandas-3.0 support is added. + warnings.warn( + "The behavior of array concatenation with empty entries is " + "deprecated. In a future version, this will no longer exclude " + "empty items when determining the result dtype. " + "To retain the old behavior, exclude the empty entries before " + "the concat operation.", + FutureWarning, + ) # Inner joins involving empty data frames always return empty dfs, but # We must delay returning until we have set the column names. - empty_inner = any(obj.empty for obj in objs) and join == "inner" + empty_inner = any_empty and join == "inner" objs = [obj for obj in objs if obj.shape != (0, 0)] diff --git a/python/cudf/cudf/core/series.py b/python/cudf/cudf/core/series.py index 5876a577b87..959b91afd32 100644 --- a/python/cudf/cudf/core/series.py +++ b/python/cudf/cudf/core/series.py @@ -1429,7 +1429,9 @@ def __repr__(self): if max_rows not in (0, None) and len(self) > max_rows: top = self.head(int(max_rows / 2 + 1)) bottom = self.tail(int(max_rows / 2 + 1)) - preprocess = cudf.concat([top, bottom]) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + preprocess = cudf.concat([top, bottom]) else: preprocess = self.copy() preprocess.index = preprocess.index._clean_nulls_from_index() diff --git a/python/cudf/cudf/io/parquet.py b/python/cudf/cudf/io/parquet.py index bcc24a85cf9..a6da55c1a7f 100644 --- a/python/cudf/cudf/io/parquet.py +++ b/python/cudf/cudf/io/parquet.py @@ -794,13 +794,15 @@ def _parquet_to_frame( dtype=_dtype, ) - # Concatenate dfs and return. - # Assume we can ignore the index if it has no name. - return ( - cudf.concat(dfs, ignore_index=dfs[-1].index.name is None) - if len(dfs) > 1 - else dfs[0] - ) + if len(dfs) > 1: + # Concatenate dfs and return. + # Assume we can ignore the index if it has no name. + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + res = cudf.concat(dfs, ignore_index=dfs[-1].index.name is None) + return res + else: + return dfs[0] @_cudf_nvtx_annotate diff --git a/python/cudf/cudf/tests/test_concat.py b/python/cudf/cudf/tests/test_concat.py index a265618e4ba..7fa1b634185 100644 --- a/python/cudf/cudf/tests/test_concat.py +++ b/python/cudf/cudf/tests/test_concat.py @@ -2,10 +2,13 @@ from decimal import Decimal +import warnings import numpy as np import pandas as pd import pytest +from contextlib import contextmanager + import cudf as gd from cudf.api.types import _is_categorical_dtype from cudf.core._compat import PANDAS_GE_150, PANDAS_LT_140, PANDAS_GE_200 @@ -17,6 +20,20 @@ ) +@contextmanager +def _hide_concat_empty_dtype_warning(): + with warnings.catch_warnings(): + # Ignoring warnings in this test as warnings are + # being caught and validated in other tests. + warnings.filterwarnings( + "ignore", + "The behavior of array concatenation with empty entries " + "is deprecated.", + category=FutureWarning, + ) + yield + + def make_frames(index=None, nulls="none"): df = pd.DataFrame( { @@ -66,8 +83,9 @@ def test_concat_dataframe(index, nulls, axis): df_empty1 = gdf_empty1.to_pandas() # DataFrame - res = gd.concat([gdf, gdf2, gdf, gdf_empty1], axis=axis).to_pandas() - sol = pd.concat([df, df2, df, df_empty1], axis=axis) + with _hide_concat_empty_dtype_warning(): + res = gd.concat([gdf, gdf2, gdf, gdf_empty1], axis=axis).to_pandas() + sol = pd.concat([df, df2, df, df_empty1], axis=axis) assert_eq( res, sol, @@ -476,8 +494,9 @@ def test_concat_series_dataframe_input(objs): pd_objs = objs gd_objs = [gd.from_pandas(obj) for obj in objs] - expected = pd.concat(pd_objs) - actual = gd.concat(gd_objs) + with _hide_concat_empty_dtype_warning(): + expected = pd.concat(pd_objs) + actual = gd.concat(gd_objs) assert_eq( expected.fillna(-1), @@ -843,23 +862,24 @@ def test_concat_join_many_df_and_empty_df(ignore_index, sort, join, axis): gdf3 = gd.from_pandas(pdf3) gdf_empty1 = gd.from_pandas(pdf_empty1) - assert_eq( - pd.concat( - [pdf1, pdf2, pdf3, pdf_empty1], - sort=sort, - join=join, - ignore_index=ignore_index, - axis=axis, - ), - gd.concat( - [gdf1, gdf2, gdf3, gdf_empty1], - sort=sort, - join=join, - ignore_index=ignore_index, - axis=axis, - ), - check_index_type=False, - ) + with _hide_concat_empty_dtype_warning(): + assert_eq( + pd.concat( + [pdf1, pdf2, pdf3, pdf_empty1], + sort=sort, + join=join, + ignore_index=ignore_index, + axis=axis, + ), + gd.concat( + [gdf1, gdf2, gdf3, gdf_empty1], + sort=sort, + join=join, + ignore_index=ignore_index, + axis=axis, + ), + check_index_type=False, + ) @pytest.mark.parametrize("ignore_index", [True, False]) @@ -970,20 +990,21 @@ def test_concat_join_no_overlapping_columns_many_and_empty( gdf6 = gd.from_pandas(pdf6) gdf_empty = gd.from_pandas(pdf_empty) - expected = pd.concat( - [pdf4, pdf5, pdf6, pdf_empty], - sort=sort, - join=join, - ignore_index=ignore_index, - axis=axis, - ) - actual = gd.concat( - [gdf4, gdf5, gdf6, gdf_empty], - sort=sort, - join=join, - ignore_index=ignore_index, - axis=axis, - ) + with _hide_concat_empty_dtype_warning(): + expected = pd.concat( + [pdf4, pdf5, pdf6, pdf_empty], + sort=sort, + join=join, + ignore_index=ignore_index, + axis=axis, + ) + actual = gd.concat( + [gdf4, gdf5, gdf6, gdf_empty], + sort=sort, + join=join, + ignore_index=ignore_index, + axis=axis, + ) assert_eq( expected, actual, @@ -1042,20 +1063,21 @@ def test_concat_join_no_overlapping_columns_many_and_empty2( ): objs_gd = [gd.from_pandas(o) if o is not None else o for o in objs] - expected = pd.concat( - objs, - sort=sort, - join=join, - ignore_index=ignore_index, - axis=axis, - ) - actual = gd.concat( - objs_gd, - sort=sort, - join=join, - ignore_index=ignore_index, - axis=axis, - ) + with _hide_concat_empty_dtype_warning(): + expected = pd.concat( + objs, + sort=sort, + join=join, + ignore_index=ignore_index, + axis=axis, + ) + actual = gd.concat( + objs_gd, + sort=sort, + join=join, + ignore_index=ignore_index, + axis=axis, + ) assert_eq(expected, actual, check_index_type=False) @@ -1079,20 +1101,21 @@ def test_concat_join_no_overlapping_columns_empty_df_basic( gdf6 = gd.from_pandas(pdf6) gdf_empty = gd.from_pandas(pdf_empty) - expected = pd.concat( - [pdf6, pdf_empty], - sort=sort, - join=join, - ignore_index=ignore_index, - axis=axis, - ) - actual = gd.concat( - [gdf6, gdf_empty], - sort=sort, - join=join, - ignore_index=ignore_index, - axis=axis, - ) + with _hide_concat_empty_dtype_warning(): + expected = pd.concat( + [pdf6, pdf_empty], + sort=sort, + join=join, + ignore_index=ignore_index, + axis=axis, + ) + actual = gd.concat( + [gdf6, gdf_empty], + sort=sort, + join=join, + ignore_index=ignore_index, + axis=axis, + ) assert_eq( expected, actual, @@ -1109,7 +1132,7 @@ def test_concat_join_series(ignore_index, sort, join, axis): s1 = gd.Series(["a", "b", "c"]) s2 = gd.Series(["a", "b"]) s3 = gd.Series(["a", "b", "c", "d"]) - s4 = gd.Series() + s4 = gd.Series(dtype="str") ps1 = s1.to_pandas() ps2 = s2.to_pandas() @@ -1123,13 +1146,14 @@ def test_concat_join_series(ignore_index, sort, join, axis): ignore_index=ignore_index, axis=axis, ) - actual = gd.concat( - [s1, s2, s3, s4], - sort=sort, - join=join, - ignore_index=ignore_index, - axis=axis, - ) + with expect_warning_if(axis == 1): + actual = gd.concat( + [s1, s2, s3, s4], + sort=sort, + join=join, + ignore_index=ignore_index, + axis=axis, + ) if PANDAS_GE_150: assert_eq( @@ -1327,12 +1351,21 @@ def test_concat_join_empty_dataframes_axis_1( gdf = gd.from_pandas(df) other_gd = [gdf] + [gd.from_pandas(o) for o in other] - expected = pd.concat( - other_pd, ignore_index=ignore_index, axis=axis, join=join, sort=sort - ) - actual = gd.concat( - other_gd, ignore_index=ignore_index, axis=axis, join=join, sort=sort - ) + with _hide_concat_empty_dtype_warning(): + expected = pd.concat( + other_pd, + ignore_index=ignore_index, + axis=axis, + join=join, + sort=sort, + ) + actual = gd.concat( + other_gd, + ignore_index=ignore_index, + axis=axis, + join=join, + sort=sort, + ) if expected.shape != df.shape: if axis == 0: for key, col in actual[actual.columns].items(): diff --git a/python/cudf/cudf/tests/test_dataframe.py b/python/cudf/cudf/tests/test_dataframe.py index 6e9b9a37ac0..94aff555c7f 100644 --- a/python/cudf/cudf/tests/test_dataframe.py +++ b/python/cudf/cudf/tests/test_dataframe.py @@ -93,6 +93,20 @@ def _hide_ufunc_warnings(eval_str): yield +@contextmanager +def _hide_concat_empty_dtype_warning(): + with warnings.catch_warnings(): + # Ignoring warnings in this test as warnings are + # being caught and validated in other tests. + warnings.filterwarnings( + "ignore", + "The behavior of array concatenation with empty " + "entries is deprecated.", + category=FutureWarning, + ) + yield + + def test_init_via_list_of_tuples(): data = [ (5, "cats", "jump", np.nan), @@ -1601,8 +1615,9 @@ def test_dataframe_concat_different_column_types(): "df_2", [cudf.DataFrame({"a": [], "b": []}), cudf.DataFrame({})] ) def test_concat_empty_dataframe(df_1, df_2): - got = cudf.concat([df_1, df_2]) - expect = pd.concat([df_1.to_pandas(), df_2.to_pandas()], sort=False) + with _hide_concat_empty_dtype_warning(): + got = cudf.concat([df_1, df_2]) + expect = pd.concat([df_1.to_pandas(), df_2.to_pandas()], sort=False) # ignoring dtypes as pandas upcasts int to float # on concatenation with empty dataframes @@ -1628,10 +1643,15 @@ def test_concat_empty_dataframe(df_1, df_2): ], ) def test_concat_different_column_dataframe(df1_d, df2_d): - got = cudf.concat( - [cudf.DataFrame(df1_d), cudf.DataFrame(df2_d), cudf.DataFrame(df1_d)], - sort=False, - ) + with _hide_concat_empty_dtype_warning(): + got = cudf.concat( + [ + cudf.DataFrame(df1_d), + cudf.DataFrame(df2_d), + cudf.DataFrame(df1_d), + ], + sort=False, + ) pdf1 = pd.DataFrame(df1_d) pdf2 = pd.DataFrame(df2_d) @@ -1670,8 +1690,9 @@ def is_invalid_concat(left, right): ) @pytest.mark.parametrize("ser_2", [pd.Series([], dtype="float64")]) def test_concat_empty_series(ser_1, ser_2): - got = cudf.concat([cudf.Series(ser_1), cudf.Series(ser_2)]) - expect = pd.concat([ser_1, ser_2]) + with _hide_concat_empty_dtype_warning(): + got = cudf.concat([cudf.Series(ser_1), cudf.Series(ser_2)]) + expect = pd.concat([ser_1, ser_2]) assert_eq(got, expect, check_index_type=True) @@ -7500,8 +7521,13 @@ def test_dataframe_concat_dataframe(df, other, sort, ignore_index): gdf = cudf.from_pandas(df) other_gd = cudf.from_pandas(other) - expected = pd.concat([pdf, other_pd], sort=sort, ignore_index=ignore_index) - actual = cudf.concat([gdf, other_gd], sort=sort, ignore_index=ignore_index) + with _hide_concat_empty_dtype_warning(): + expected = pd.concat( + [pdf, other_pd], sort=sort, ignore_index=ignore_index + ) + actual = cudf.concat( + [gdf, other_gd], sort=sort, ignore_index=ignore_index + ) # In empty dataframe cases, Pandas & cudf differ in columns # creation, pandas creates RangeIndex(0, 0) @@ -7739,12 +7765,13 @@ def test_dataframe_concat_dataframe_lists(df, other, sort, ignore_index): gdf = cudf.from_pandas(df) other_gd = [cudf.from_pandas(o) for o in other] - expected = pd.concat( - [pdf] + other_pd, sort=sort, ignore_index=ignore_index - ) - actual = cudf.concat( - [gdf] + other_gd, sort=sort, ignore_index=ignore_index - ) + with _hide_concat_empty_dtype_warning(): + expected = pd.concat( + [pdf] + other_pd, sort=sort, ignore_index=ignore_index + ) + actual = cudf.concat( + [gdf] + other_gd, sort=sort, ignore_index=ignore_index + ) # In some cases, Pandas creates an empty Index([], dtype="object") for # columns whereas cudf creates a RangeIndex(0, 0). @@ -7854,12 +7881,13 @@ def test_dataframe_concat_lists(df, other, sort, ignore_index): gdf = cudf.from_pandas(df) other_gd = [cudf.from_pandas(o) for o in other_pd] - expected = pd.concat( - [pdf] + other_pd, sort=sort, ignore_index=ignore_index - ) - actual = cudf.concat( - [gdf] + other_gd, sort=sort, ignore_index=ignore_index - ) + with _hide_concat_empty_dtype_warning(): + expected = pd.concat( + [pdf] + other_pd, sort=sort, ignore_index=ignore_index + ) + actual = cudf.concat( + [gdf] + other_gd, sort=sort, ignore_index=ignore_index + ) if expected.shape != df.shape: assert_eq( diff --git a/python/cudf/cudf/tests/test_index.py b/python/cudf/cudf/tests/test_index.py index 445fc84981b..d06041301b9 100644 --- a/python/cudf/cudf/tests/test_index.py +++ b/python/cudf/cudf/tests/test_index.py @@ -1034,16 +1034,19 @@ def test_index_append(data, other): pd_data = pd.Index(data) pd_other = pd.Index(other) - gd_data = cudf.core.index.as_index(data) - gd_other = cudf.core.index.as_index(other) + gd_data = cudf.Index(data) + gd_other = cudf.Index(other) if cudf.utils.dtypes.is_mixed_with_object_dtype(gd_data, gd_other): gd_data = gd_data.astype("str") gd_other = gd_other.astype("str") - expected = pd_data.append(pd_other) - - actual = gd_data.append(gd_other) + with expect_warning_if( + (len(data) == 0 or len(other) == 0) and pd_data.dtype != pd_other.dtype + ): + expected = pd_data.append(pd_other) + with expect_warning_if(len(data) == 0 or len(other) == 0): + actual = gd_data.append(gd_other) if len(data) == 0 and len(other) == 0: # Pandas default dtype to "object" for empty list # cudf default dtype to "float" for empty list @@ -1233,8 +1236,13 @@ def test_index_append_list(data, other): gd_data = cudf.from_pandas(data) gd_other = [cudf.from_pandas(i) for i in other] - expected = pd_data.append(pd_other) - actual = gd_data.append(gd_other) + with expect_warning_if( + (len(data) == 0 or any(len(d) == 0 for d in other)) + and (any(d.dtype != data.dtype for d in other)) + ): + expected = pd_data.append(pd_other) + with expect_warning_if(len(data) == 0 or any(len(d) == 0 for d in other)): + actual = gd_data.append(gd_other) assert_eq(expected, actual)