Skip to content

Commit 2c994c7

Browse files
BUG/COMPAT: fix assert_* functions for nested arrays with latest numpy (#50396)
* BUG/COMPAT: fix assert_* functions for nested arrays with latest numpy * add whatsnew * add comment + extra test
1 parent 99d367a commit 2c994c7

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

3738
.. ---------------------------------------------------------------------------

pandas/core/dtypes/missing.py

+4
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,10 @@ def _array_equivalent_object(left: np.ndarray, right: np.ndarray, strict_nan: bo
584584
if "boolean value of NA is ambiguous" in str(err):
585585
return False
586586
raise
587+
except ValueError:
588+
# numpy can raise a ValueError if left and right cannot be
589+
# compared (e.g. nested arrays)
590+
return False
587591
return True
588592

589593

pandas/tests/dtypes/test_missing.py

+109-7
Original file line numberDiff line numberDiff line change
@@ -545,18 +545,120 @@ def test_array_equivalent_str(dtype):
545545
)
546546

547547

548-
def test_array_equivalent_nested():
548+
@pytest.mark.parametrize(
549+
"strict_nan", [pytest.param(True, marks=pytest.mark.xfail), False]
550+
)
551+
def test_array_equivalent_nested(strict_nan):
549552
# reached in groupby aggregations, make sure we use np.any when checking
550553
# if the comparison is truthy
551-
left = np.array([np.array([50, 70, 90]), np.array([20, 30, 40])], dtype=object)
552-
right = np.array([np.array([50, 70, 90]), np.array([20, 30, 40])], dtype=object)
554+
left = np.array([np.array([50, 70, 90]), np.array([20, 30])], dtype=object)
555+
right = np.array([np.array([50, 70, 90]), np.array([20, 30])], dtype=object)
553556

554-
assert array_equivalent(left, right, strict_nan=True)
555-
assert not array_equivalent(left, right[::-1], strict_nan=True)
557+
assert array_equivalent(left, right, strict_nan=strict_nan)
558+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
556559

557-
left = np.array([np.array([50, 50, 50]), np.array([40, 40, 40])], dtype=object)
560+
left = np.empty(2, dtype=object)
561+
left[:] = [np.array([50, 70, 90]), np.array([20, 30, 40])]
562+
right = np.empty(2, dtype=object)
563+
right[:] = [np.array([50, 70, 90]), np.array([20, 30, 40])]
564+
assert array_equivalent(left, right, strict_nan=strict_nan)
565+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
566+
567+
left = np.array([np.array([50, 50, 50]), np.array([40, 40])], dtype=object)
558568
right = np.array([50, 40])
559-
assert not array_equivalent(left, right, strict_nan=True)
569+
assert not array_equivalent(left, right, strict_nan=strict_nan)
570+
571+
572+
@pytest.mark.parametrize(
573+
"strict_nan", [pytest.param(True, marks=pytest.mark.xfail), False]
574+
)
575+
def test_array_equivalent_nested2(strict_nan):
576+
# more than one level of nesting
577+
left = np.array(
578+
[
579+
np.array([np.array([50, 70]), np.array([90])], dtype=object),
580+
np.array([np.array([20, 30])], dtype=object),
581+
],
582+
dtype=object,
583+
)
584+
right = np.array(
585+
[
586+
np.array([np.array([50, 70]), np.array([90])], dtype=object),
587+
np.array([np.array([20, 30])], dtype=object),
588+
],
589+
dtype=object,
590+
)
591+
assert array_equivalent(left, right, strict_nan=strict_nan)
592+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
593+
594+
left = np.array([np.array([np.array([50, 50, 50])], dtype=object)], dtype=object)
595+
right = np.array([50])
596+
assert not array_equivalent(left, right, strict_nan=strict_nan)
597+
598+
599+
@pytest.mark.parametrize(
600+
"strict_nan", [pytest.param(True, marks=pytest.mark.xfail), False]
601+
)
602+
def test_array_equivalent_nested_list(strict_nan):
603+
left = np.array([[50, 70, 90], [20, 30]], dtype=object)
604+
right = np.array([[50, 70, 90], [20, 30]], dtype=object)
605+
606+
assert array_equivalent(left, right, strict_nan=strict_nan)
607+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
608+
609+
left = np.array([[50, 50, 50], [40, 40]], dtype=object)
610+
right = np.array([50, 40])
611+
assert not array_equivalent(left, right, strict_nan=strict_nan)
612+
613+
614+
@pytest.mark.xfail(reason="failing")
615+
@pytest.mark.parametrize("strict_nan", [True, False])
616+
def test_array_equivalent_nested_mixed_list(strict_nan):
617+
# mixed arrays / lists in left and right
618+
# https://github.com/pandas-dev/pandas/issues/50360
619+
left = np.array([np.array([1, 2, 3]), np.array([4, 5])], dtype=object)
620+
right = np.array([[1, 2, 3], [4, 5]], dtype=object)
621+
622+
assert array_equivalent(left, right, strict_nan=strict_nan)
623+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
624+
625+
# multiple levels of nesting
626+
left = np.array(
627+
[
628+
np.array([np.array([1, 2, 3]), np.array([4, 5])], dtype=object),
629+
np.array([np.array([6]), np.array([7, 8]), np.array([9])], dtype=object),
630+
],
631+
dtype=object,
632+
)
633+
right = np.array([[[1, 2, 3], [4, 5]], [[6], [7, 8], [9]]], dtype=object)
634+
assert array_equivalent(left, right, strict_nan=strict_nan)
635+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
636+
637+
# same-length lists
638+
subarr = np.empty(2, dtype=object)
639+
subarr[:] = [
640+
np.array([None, "b"], dtype=object),
641+
np.array(["c", "d"], dtype=object),
642+
]
643+
left = np.array([subarr, None], dtype=object)
644+
right = np.array([list([[None, "b"], ["c", "d"]]), None], dtype=object)
645+
assert array_equivalent(left, right, strict_nan=strict_nan)
646+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
647+
648+
649+
@pytest.mark.xfail(reason="failing")
650+
@pytest.mark.parametrize("strict_nan", [True, False])
651+
def test_array_equivalent_nested_dicts(strict_nan):
652+
left = np.array([{"f1": 1, "f2": np.array(["a", "b"], dtype=object)}], dtype=object)
653+
right = np.array(
654+
[{"f1": 1, "f2": np.array(["a", "b"], dtype=object)}], dtype=object
655+
)
656+
assert array_equivalent(left, right, strict_nan=strict_nan)
657+
assert not array_equivalent(left, right[::-1], strict_nan=strict_nan)
658+
659+
right2 = np.array([{"f1": 1, "f2": ["a", "b"]}], dtype=object)
660+
assert array_equivalent(left, right2, strict_nan=strict_nan)
661+
assert not array_equivalent(left, right2[::-1], strict_nan=strict_nan)
560662

561663

562664
def test_array_equivalent_index_with_tuples():

pandas/tests/util/test_assert_almost_equal.py

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

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

0 commit comments

Comments
 (0)