Search code examples
pythonpython-2.7numpyvectorizationscientific-computing

Specifying dtype=object for numpy.gradient


Is there a way to specify the dtype for numpy.gradient?

I'm using an array of subarrays and it's throwing the following error:

ValueError: setting an array element with a sequence.

Here is an example:

import numpy as np
a = np.empty([3, 3], dtype=object)
it = np.nditer(a, flags=['multi_index', 'refs_ok'])
while not it.finished:
    i = it.multi_index[0]
    j = it.multi_index[1]
    a[it.multi_index] = np.array([i, j])
    it.iternext()
print(a)

which outputs

[[array([0, 0]) array([0, 1]) array([0, 2])]
 [array([1, 0]) array([1, 1]) array([1, 2])]
 [array([2, 0]) array([2, 1]) array([2, 2])]]

I would like print(np.gradient(a)) to return

array(
    [[array([[1, 0],[0, 1]]), array([[1, 0], [0, 1]]), array([[1, 0], [0, 1]])],
     [array([[1, 0], [0, 1]]), array([[1, 0], [0, 1]]), array([[1, 0],[0, 1]])],
     [array([[1, 0], [0, 1]]), array([[1, 0], [0, 1]]), array([[1, 0],[0, 1]])]],
    dtype=object)

Notice that, in this case, the gradient of the vector field is an identity tensor field.


Solution

  • why are you working an array of dtype object? That's more work than using a 2d array.

    e.g.

    In [53]: a1=np.array([[1,2],[3,4],[5,6]])
    
    In [54]: a1
    Out[54]: 
    array([[1, 2],
           [3, 4],
           [5, 6]])
    
    In [55]: np.gradient(a1)
    Out[55]: 
    [array([[ 2.,  2.],
           [ 2.,  2.],
           [ 2.,  2.]]),
     array([[ 1.,  1.],
           [ 1.,  1.],
           [ 1.,  1.]])]
    

    or working column by column, or row by row

    In [61]: [np.gradient(i) for i in a1.T]
    Out[61]: [array([ 2.,  2.,  2.]), array([ 2.,  2.,  2.])]
    
    In [62]: [np.gradient(i) for i in a1]
    Out[62]: [array([ 1.,  1.]), array([ 1.,  1.]), array([ 1.,  1.])]
    

    dtype=object only make sense if the subarrays/lists differ in type and/or shape. And even then it doesn't add much to a regular Python list.

    ==============================

    I can take your 2d a, and make a 3d array with:

    In [126]: a1=np.zeros((3,3,2),int)
    
    In [127]: a1.flat[:]=[i for  i in a.flatten()]
    
    In [128]: a1
    Out[128]: 
    array([[[0, 0],
            [0, 1],
            [0, 2]],
    
           [[1, 0],
            [1, 1],
            [1, 2]],
    
           [[2, 0],
            [2, 1],
            [2, 2]]])
    

    Or I could produce the same thing with meshgrid:

    In [129]: X,Y=np.meshgrid(np.arange(3),np.arange(3),indexing='ij')
    In [130]: a2=np.array([Y,X]).T
    

    When I apply np.gradient to that I get 3 arrays, each (3,3,2) in shape.

    In [136]: ga1=np.gradient(a1)
    
    In [137]: len(ga1)
    Out[137]: 3
    
    In [138]: ga1[0].shape
    Out[138]: (3, 3, 2)
    

    It looks like the 1st 2 arrays have the values you want, so it's just a matter of rearranging them.

    In [141]: np.array(ga1[:2]).shape
    Out[141]: (2, 3, 3, 2)
    
    In [143]: gga1=np.array(ga1[:2]).transpose([1,2,0,3])
    
    In [144]: gga1.shape
    Out[144]: (3, 3, 2, 2)
    
    In [145]: gga1[0,0]
    Out[145]: 
    array([[ 1., -0.],
           [-0.,  1.]])
    

    If they must go back into a (3,3) object array, I could do:

    In [146]: goa1=np.empty([3,3],dtype=object)
    
    In [147]: for i in range(3):
        for j in range(3):
            goa1[i,j]=gga1[i,j]
       .....:         
    
    In [148]: goa1
    Out[148]: 
    array([[array([[ 1., -0.],
           [-0.,  1.]]),
            array([[ 1., -0.],
           [ 0.,  1.]]),
            array([[ 1., -0.],
           ...
           [ 0.,  1.]]),
            array([[ 1.,  0.],
           [ 0.,  1.]])]], dtype=object)
    

    I still wonder what's the point to working with a object array.