Skip to content

Commit 71db310

Browse files
Backport PR #50396 on branch 1.5.x (BUG/COMPAT: fix assert_* functions for nested arrays with latest numpy) (#50739)
Backport PR #50396: BUG/COMPAT: fix assert_* functions for nested arrays with latest numpy Co-authored-by: Joris Van den Bossche <[email protected]>
1 parent 060345a commit 71db310

File tree

4 files changed

+198
-7
lines changed

4 files changed

+198
-7
lines changed

doc/source/whatsnew/v1.5.3.rst

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Bug fixes
3131
- Bug in :meth:`Series.quantile` emitting warning from NumPy when :class:`Series` has only ``NA`` values (:issue:`50681`)
3232
- Bug when chaining several :meth:`.Styler.concat` calls, only the last styler was concatenated (:issue:`49207`)
3333
- Fixed bug when instantiating a :class:`DataFrame` subclass inheriting from ``typing.Generic`` that triggered a ``UserWarning`` on python 3.11 (:issue:`49649`)
34+
- Bug in :func:`pandas.testing.assert_series_equal` (and equivalent ``assert_`` functions) when having nested data and using numpy >= 1.25 (:issue:`50360`)
3435
-
3536

3637
.. ---------------------------------------------------------------------------

pandas/core/dtypes/missing.py

+4
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,10 @@ def _array_equivalent_object(left: np.ndarray, right: np.ndarray, strict_nan: bo
594594
if "boolean value of NA is ambiguous" in str(err):
595595
return False
596596
raise
597+
except ValueError:
598+
# numpy can raise a ValueError if left and right cannot be
599+
# compared (e.g. nested arrays)
600+
return False
597601
return True
598602

599603

pandas/tests/dtypes/test_missing.py

+109-7
Original file line numberDiff line numberDiff line change
@@ -525,18 +525,120 @@ def test_array_equivalent_str(dtype):
525525
)
526526

527527

528-
def test_array_equivalent_nested():
528+
@pytest.mark.parametrize(
529+
"strict_nan", [pytest.param(True, marks=pytest.mark.xfail), False]
530+
)
531+
def test_array_equivalent_nested(strict_nan):
529532
# reached in groupby aggregations, make sure we use np.any when checking
530533
# if the comparison is truthy
531-
left = np.array([np.array([50, 70, 90]), np.array([20, 30, 40])], dtype=object)
532-
right = np.array([np.array([50, 70, 90]), np.array([20, 30, 40])], dtype=object)
534+
left = np.array([np.array([50, 70, 90]), np.array([20, 30])], dtype=object)
535+
right = np.array([np.array([50, 70, 90]), np.array([20, 30])], dtype=object)
533536

534-
assert array_equivalent(left, right, strict_nan=True)
535-
assert not array_equivalent(left, right[::-1], strict_nan=True)
537+
assert array_equivalent(left, right, strict_nan=strict_nan)
538+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
536539

537-
left = np.array([np.array([50, 50, 50]), np.array([40, 40, 40])], dtype=object)
540+
left = np.empty(2, dtype=object)
541+
left[:] = [np.array([50, 70, 90]), np.array([20, 30, 40])]
542+
right = np.empty(2, dtype=object)
543+
right[:] = [np.array([50, 70, 90]), np.array([20, 30, 40])]
544+
assert array_equivalent(left, right, strict_nan=strict_nan)
545+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
546+
547+
left = np.array([np.array([50, 50, 50]), np.array([40, 40])], dtype=object)
538548
right = np.array([50, 40])
539-
assert not array_equivalent(left, right, strict_nan=True)
549+
assert not array_equivalent(left, right, strict_nan=strict_nan)
550+
551+
552+
@pytest.mark.parametrize(
553+
"strict_nan", [pytest.param(True, marks=pytest.mark.xfail), False]
554+
)
555+
def test_array_equivalent_nested2(strict_nan):
556+
# more than one level of nesting
557+
left = np.array(
558+
[
559+
np.array([np.array([50, 70]), np.array([90])], dtype=object),
560+
np.array([np.array([20, 30])], dtype=object),
561+
],
562+
dtype=object,
563+
)
564+
right = np.array(
565+
[
566+
np.array([np.array([50, 70]), np.array([90])], dtype=object),
567+
np.array([np.array([20, 30])], dtype=object),
568+
],
569+
dtype=object,
570+
)
571+
assert array_equivalent(left, right, strict_nan=strict_nan)
572+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
573+
574+
left = np.array([np.array([np.array([50, 50, 50])], dtype=object)], dtype=object)
575+
right = np.array([50])
576+
assert not array_equivalent(left, right, strict_nan=strict_nan)
577+
578+
579+
@pytest.mark.parametrize(
580+
"strict_nan", [pytest.param(True, marks=pytest.mark.xfail), False]
581+
)
582+
def test_array_equivalent_nested_list(strict_nan):
583+
left = np.array([[50, 70, 90], [20, 30]], dtype=object)
584+
right = np.array([[50, 70, 90], [20, 30]], dtype=object)
585+
586+
assert array_equivalent(left, right, strict_nan=strict_nan)
587+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
588+
589+
left = np.array([[50, 50, 50], [40, 40]], dtype=object)
590+
right = np.array([50, 40])
591+
assert not array_equivalent(left, right, strict_nan=strict_nan)
592+
593+
594+
@pytest.mark.xfail(reason="failing")
595+
@pytest.mark.parametrize("strict_nan", [True, False])
596+
def test_array_equivalent_nested_mixed_list(strict_nan):
597+
# mixed arrays / lists in left and right
598+
# https://github.com/pandas-dev/pandas/issues/50360
599+
left = np.array([np.array([1, 2, 3]), np.array([4, 5])], dtype=object)
600+
right = np.array([[1, 2, 3], [4, 5]], dtype=object)
601+
602+
assert array_equivalent(left, right, strict_nan=strict_nan)
603+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
604+
605+
# multiple levels of nesting
606+
left = np.array(
607+
[
608+
np.array([np.array([1, 2, 3]), np.array([4, 5])], dtype=object),
609+
np.array([np.array([6]), np.array([7, 8]), np.array([9])], dtype=object),
610+
],
611+
dtype=object,
612+
)
613+
right = np.array([[[1, 2, 3], [4, 5]], [[6], [7, 8], [9]]], dtype=object)
614+
assert array_equivalent(left, right, strict_nan=strict_nan)
615+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
616+
617+
# same-length lists
618+
subarr = np.empty(2, dtype=object)
619+
subarr[:] = [
620+
np.array([None, "b"], dtype=object),
621+
np.array(["c", "d"], dtype=object),
622+
]
623+
left = np.array([subarr, None], dtype=object)
624+
right = np.array([list([[None, "b"], ["c", "d"]]), None], dtype=object)
625+
assert array_equivalent(left, right, strict_nan=strict_nan)
626+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
627+
628+
629+
@pytest.mark.xfail(reason="failing")
630+
@pytest.mark.parametrize("strict_nan", [True, False])
631+
def test_array_equivalent_nested_dicts(strict_nan):
632+
left = np.array([{"f1": 1, "f2": np.array(["a", "b"], dtype=object)}], dtype=object)
633+
right = np.array(
634+
[{"f1": 1, "f2": np.array(["a", "b"], dtype=object)}], dtype=object
635+
)
636+
assert array_equivalent(left, right, strict_nan=strict_nan)
637+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
638+
639+
right2 = np.array([{"f1": 1, "f2": ["a", "b"]}], dtype=object)
640+
assert array_equivalent(left, right2, strict_nan=strict_nan)
641+
assert not array_equivalent(left, right2[::-1], strict_nan=strict_nan)
540642

541643

542644
@pytest.mark.parametrize(

pandas/tests/util/test_assert_almost_equal.py

+84
Original file line numberDiff line numberDiff line change
@@ -458,3 +458,87 @@ def test_assert_almost_equal_iterable_values_mismatch():
458458

459459
with pytest.raises(AssertionError, match=msg):
460460
tm.assert_almost_equal([1, 2], [1, 3])
461+
462+
463+
subarr = np.empty(2, dtype=object)
464+
subarr[:] = [np.array([None, "b"], dtype=object), np.array(["c", "d"], dtype=object)]
465+
466+
NESTED_CASES = [
467+
# nested array
468+
(
469+
np.array([np.array([50, 70, 90]), np.array([20, 30])], dtype=object),
470+
np.array([np.array([50, 70, 90]), np.array([20, 30])], dtype=object),
471+
),
472+
# >1 level of nesting
473+
(
474+
np.array(
475+
[
476+
np.array([np.array([50, 70]), np.array([90])], dtype=object),
477+
np.array([np.array([20, 30])], dtype=object),
478+
],
479+
dtype=object,
480+
),
481+
np.array(
482+
[
483+
np.array([np.array([50, 70]), np.array([90])], dtype=object),
484+
np.array([np.array([20, 30])], dtype=object),
485+
],
486+
dtype=object,
487+
),
488+
),
489+
# lists
490+
(
491+
np.array([[50, 70, 90], [20, 30]], dtype=object),
492+
np.array([[50, 70, 90], [20, 30]], dtype=object),
493+
),
494+
# mixed array/list
495+
(
496+
np.array([np.array([1, 2, 3]), np.array([4, 5])], dtype=object),
497+
np.array([[1, 2, 3], [4, 5]], dtype=object),
498+
),
499+
(
500+
np.array(
501+
[
502+
np.array([np.array([1, 2, 3]), np.array([4, 5])], dtype=object),
503+
np.array(
504+
[np.array([6]), np.array([7, 8]), np.array([9])], dtype=object
505+
),
506+
],
507+
dtype=object,
508+
),
509+
np.array([[[1, 2, 3], [4, 5]], [[6], [7, 8], [9]]], dtype=object),
510+
),
511+
# same-length lists
512+
(
513+
np.array([subarr, None], dtype=object),
514+
np.array([list([[None, "b"], ["c", "d"]]), None], dtype=object),
515+
),
516+
# dicts
517+
(
518+
np.array([{"f1": 1, "f2": np.array(["a", "b"], dtype=object)}], dtype=object),
519+
np.array([{"f1": 1, "f2": np.array(["a", "b"], dtype=object)}], dtype=object),
520+
),
521+
(
522+
np.array([{"f1": 1, "f2": np.array(["a", "b"], dtype=object)}], dtype=object),
523+
np.array([{"f1": 1, "f2": ["a", "b"]}], dtype=object),
524+
),
525+
# array/list of dicts
526+
(
527+
np.array(
528+
[
529+
np.array(
530+
[{"f1": 1, "f2": np.array(["a", "b"], dtype=object)}], dtype=object
531+
),
532+
np.array([], dtype=object),
533+
],
534+
dtype=object,
535+
),
536+
np.array([[{"f1": 1, "f2": ["a", "b"]}], []], dtype=object),
537+
),
538+
]
539+
540+
541+
@pytest.mark.filterwarnings("ignore:elementwise comparison failed:DeprecationWarning")
542+
@pytest.mark.parametrize("a,b", NESTED_CASES)
543+
def test_assert_almost_equal_array_nested(a, b):
544+
_assert_almost_equal_both(a, b)

0 commit comments

Comments
 (0)