Search code examples
pythonnumpyreshape

numpy reshape implementation


My main focus will on this page.

First, I don't understand the page. For the first parameter shape,

ndarray.reshape(shape, order='C')

why this method on ndarray allows the elements of the shape parameter to be passed in as separate arguments?

That is, why does this implementation allow a.reshape(10, 11)?

Second, I am wondering where the implementation on GitHub is? Searching the whole sentence "Returns an array containing the same data with a new shape" does not give me a correct link on GitHub. My attempt.

Thank you!


Solution

  • At the risk of being pedantic, here's some comparisons between using the function and the method versions. The function version takes an initial object that it interprets as a array (or makes into an array). The method already has the array object (self).

    The function has a python signature, the method is compiled from c so can take some liberties in its signature (especially since it was written years ago before py3 and the current standards).

    In [66]: x=np.arange(12)
    
    In [67]: np.reshape(x,(3,4))
    Out[67]: 
    array([[ 0,  1,  2,  3],
           [ 4,  5,  6,  7],
           [ 8,  9, 10, 11]])
    

    The method can take either a tuple or numbers:

    In [68]: x.reshape((3,4))
    Out[68]: 
    array([[ 0,  1,  2,  3],
           [ 4,  5,  6,  7],
           [ 8,  9, 10, 11]])
    
    In [69]: x.reshape(3,4)
    Out[69]: 
    array([[ 0,  1,  2,  3],
           [ 4,  5,  6,  7],
           [ 8,  9, 10, 11]])
    

    The function tries to interpret the 3rd argument as order:

    In [70]: np.reshape(x,3,4)
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    File ~\miniconda3\lib\site-packages\numpy\core\fromnumeric.py:57, in _wrapfunc(obj, method, *args, **kwds)
         56 try:
    ---> 57     return bound(*args, **kwds)
         58 except TypeError:
         59     # A TypeError occurs if the object does have such a method in its
         60     # class, but its signature is not identical to that of NumPy's. This
       (...)
         64     # Call _wrapit from within the except clause to ensure a potential
         65     # exception has a traceback chain.
    
    TypeError: order must be str, not int
    
    During handling of the above exception, another exception occurred:
    
    TypeError                                 Traceback (most recent call last)
    Cell In[70], line 1
    ----> 1 np.reshape(x,3,4)
    
    File <__array_function__ internals>:200, in reshape(*args, **kwargs)
    
    File ~\miniconda3\lib\site-packages\numpy\core\fromnumeric.py:298, in reshape(a, newshape, order)
        198 @array_function_dispatch(_reshape_dispatcher)
        199 def reshape(a, newshape, order='C'):
        200     """
        201     Gives a new shape to an array without changing its data.
        202 
       (...)
        296            [5, 6]])
        297     """
    --> 298     return _wrapfunc(a, 'reshape', newshape, order=order)
    
    File ~\miniconda3\lib\site-packages\numpy\core\fromnumeric.py:66, in _wrapfunc(obj, method, *args, **kwds)
         57     return bound(*args, **kwds)
         58 except TypeError:
         59     # A TypeError occurs if the object does have such a method in its
         60     # class, but its signature is not identical to that of NumPy's. This
       (...)
         64     # Call _wrapit from within the except clause to ensure a potential
         65     # exception has a traceback chain.
    ---> 66     return _wrapit(obj, method, *args, **kwds)
    
    File ~\miniconda3\lib\site-packages\numpy\core\fromnumeric.py:43, in _wrapit(obj, method, *args, **kwds)
         41 except AttributeError:
         42     wrap = None
    ---> 43 result = getattr(asarray(obj), method)(*args, **kwds)
         44 if wrap:
         45     if not isinstance(result, mu.ndarray):
    
    TypeError: order must be str, not int
    
    In [71]: np.reshape(x,(3,4),'F')
    Out[71]: 
    array([[ 0,  3,  6,  9],
           [ 1,  4,  7, 10],
           [ 2,  5,  8, 11]])
    

    The method requires that the order be a keyword:

    In [72]: x.reshape(3,4,'F')
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    Cell In[72], line 1
    ----> 1 x.reshape(3,4,'F')
    
    TypeError: 'str' object cannot be interpreted as an integer
    
    In [73]: x.reshape(3,4,order='F')
    Out[73]: 
    array([[ 0,  3,  6,  9],
           [ 1,  4,  7, 10],
           [ 2,  5,  8, 11]])
        
    In [75]: x.reshape((3,4),'F')
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    Cell In[75], line 1
    ----> 1 x.reshape((3,4),'F')
    
    TypeError: 'tuple' object cannot be interpreted as an integer
    
    In [76]: x.reshape((3,4),order='F')
    Out[76]: 
    array([[ 0,  3,  6,  9],
           [ 1,  4,  7, 10],
           [ 2,  5,  8, 11]])
    

    Compare these with the errors when the newshape is wrong. Same error, but the function has the additional wrapit layering.

    In [77]: x.reshape((3,5))
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    Cell In[77], line 1
    ----> 1 x.reshape((3,5))
    
    ValueError: cannot reshape array of size 12 into shape (3,5)
    
    In [78]: np.reshape(x,(3,5))
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    Cell In[78], line 1
    ----> 1 np.reshape(x,(3,5))
    
    File <__array_function__ internals>:200, in reshape(*args, **kwargs)
    
    File ~\miniconda3\lib\site-packages\numpy\core\fromnumeric.py:298, in reshape(a, newshape, order)
        198 @array_function_dispatch(_reshape_dispatcher)
        199 def reshape(a, newshape, order='C'):
        200     """
        201     Gives a new shape to an array without changing its data.
        202 
       (...)
        296            [5, 6]])
        297     """
    --> 298     return _wrapfunc(a, 'reshape', newshape, order=order)
    
    File ~\miniconda3\lib\site-packages\numpy\core\fromnumeric.py:57, in _wrapfunc(obj, method, *args, **kwds)
         54     return _wrapit(obj, method, *args, **kwds)
         56 try:
    ---> 57     return bound(*args, **kwds)
         58 except TypeError:
         59     # A TypeError occurs if the object does have such a method in its
         60     # class, but its signature is not identical to that of NumPy's. This
       (...)
         64     # Call _wrapit from within the except clause to ensure a potential
         65     # exception has a traceback chain.
         66     return _wrapit(obj, method, *args, **kwds)
    
    ValueError: cannot reshape array of size 12 into shape (3,5)
    

    (these error tracebacks are long because the ipython %xmode is set to verbose.)

    edit

    np.random.rand has this note:

    rand(d0, d1, ..., dn)
    
    Random values in a given shape.
    
    .. note::
        This is a convenience function for users porting code from Matlab,
        and wraps `random_sample`. That function takes a
        tuple to specify the size of the output, which is consistent with
        other NumPy functions like `numpy.zeros` and `numpy.ones`.
    

    While numpy consistently shows shape as tuple (even the 0d and 1d cases), there are functions/methods that accept it as individual numbers. This note suggests this was an early feature of numpy, from the days when MATLAB still cast a long shaddow.

    Of course in Python the 1,2,3 is just as much a tuple as (1,2,3). And in contexts like indexing the () are optional, e.g. x[1,2,3] is the same as x[(1,2,3)]. Both are interpreted as x.__getitem__((1,2,3)).