pythonnumpymatlabscipyassertion

`numpy.testing.assert_array_equal` fails when comparing structured numpy arrays with array fields


I was comparing some data using numpy.testing.assert_array_equal. The data was read from a MAT-file using scipy.io.loadmat. The MAT-file was generated as follows:

a = [1, 2; 3, 4];
b = struct('MyField', 10); 
c = struct('MyField', [1, 2; 3, 4]);
save('example.mat', 'a', 'b', 'c');

For testing, I manually generated the expected NumPy array to match how scipy.io.loadmat outputs them:

import numpy as np
from numpy.testing import assert_array_equal
from scipy.io import loadmat

a = np.array([[1., 2.], [3., 4.]])
b = np.array([[(np.array(10.0),)]], dtype=[("MyField", "O")])
c = np.array(
    [[
        (np.array([[1., 2.], [3., 4.]]),)
    ]], dtype=[("MyField", "O")])

matdict = loadmat("example.mat", mat_dtype=True)

assert_array_equal(matdict["a"], a) # Passes
assert_array_equal(matdict["b"], b) # Passes
assert_array_equal(matdict["c"], c) # Fails

This comparison fails only for variable c, throwing the following error:

Traceback (most recent call last):
  File ".../python3.13/site-packages/numpy/testing/_private/utils.py", line 851, in assert_array_compare
    val = comparison(x, y)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  ...
  File ".../python3.13/site-packages/numpy/testing/_private/utils.py", line 1057, in assert_array_equal
    assert_array_compare(operator.__eq__, actual, desired, err_msg=err_msg,
    ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                         verbose=verbose, header='Arrays are not equal',
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                         strict=strict)
                         ^^^^^^^^^^^^^^
  File ".../python3.13/site-packages/numpy/testing/_private/utils.py", line 929, in assert_array_compare
    raise ValueError(msg)
ValueError: 
error during assertion:

Traceback (most recent call last):
  File ".../python3.13/site-packages/numpy/testing/_private/utils.py", line 851, in assert_array_compare
    val = comparison(x, y)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()


Arrays are not equal
 ACTUAL: array([[(array([[1., 2.],
               [3., 4.]]),)]], dtype=[('MyField', 'O')])
 DESIRED: array([[(array([[1., 2.],
               [3., 4.]]),)]], dtype=[('MyField', 'O')])

I initially suspected that the issue may be related to the usage of structured numpy array or maybe the object dtype. However I'm not too sure about this since the test passed for variable b.

I don't know why it fails for this particular case only, since the stdout looks visually identical. I would really appreciate some help here on understanding the underlying issue here, and also explaining the right way to handle such comparisons.

Thanks!


Solution

  • This happens because the field in c contains a Numpy array, and assert_array_equal tries to compare structured arrays using ==, which fails when it encounters arrays inside object fields.

    In b, the field is a scalar (10.0), so it works fine, but in c, MyField holds an array, and comparing two arrays with == returns an array of booleans which causes the truth value error.

    To fix it, compare the inner arrays manually using assert_array_equal:

    from numpy.testing import assert_array_equal
    
    # Extract and compare the nested arrays directly
    actual = matdict["c"]
    expected = np.array([[(np.array([[1., 2.], [3., 4.]]),)]], dtype=[("MyField", "O")])
    
    assert actual.dtype == expected.dtype
    assert actual.shape == expected.shape
    
    for a, e in zip(actual.flat, expected.flat):
        assert_array_equal(a[0], e[0])  # Compare inner arrays
    

    If you have to do this often, I suggest you to write an helper :)