Search code examples
pythonarraysmatrixflatten

How can I reshape a 2D array into 1D in python?


Let me edit my question again. I know how flatten works but I am looking if it possible to remove the inside braces and just simple two outside braces just like in MATLAB and maintain the same shape of (3,4). here it is arrays inside array, and I want to have just one array so I can plot it easily also get the same results is it is in Matlab. For example I have the following matrix (which is arrays inside array):

s=np.arange(12).reshape(3,4)
print(s)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

Is it possible to reshape or flatten() it and get results like this:

[ 0  1  2  3
  4  5  6  7
  8  9 10 11]

Solution

  • First answer

    If I understood correctly your question (and 4 other answers say I didn't), your problem is not how to flatten() or reshape(-1) an array, but how to ensure that even after reshaping, it still display with 4 elements per line.

    I don't think you can, strictly speaking. Arrays are just a bunch of elements. They don't contain indication about how we want to see them. That's a printing problem, you are supposed to solve when printing. You can see [here][1] that people who want to do that... start with reshaping array in 2D.

    That being said, without creating your own printing function, you can control how numpy display arrays, using np.set_printoptions.

    Still, it is tricky so, because this function allows you only to specify how many characters, not elements, are printed per line. So you need to know how many chars each element will need, to force linebreaks.

    In your example:

    np.set_printoptions(formatter={"all":lambda x:"{:>6}".format(x)}, linewidth=7+(6+2)*4)
    

    The formatter ensure that each number use 6 chars. The linewidth, taking into account "array([" part, and the closing "])" (9 chars) plus the 2 ", " between each elements, knowing we want 4 elements, must be 9+6×4+2×3: 9 chars for "array([...])", 6×4 for each 4 numbers, 2×3 for each 3 ", " separator. Or 7+(6+2)×4.

    You can use it only for one printing

    with np.printoptions(formatter={"all":lambda x:"{:>6}".format(x)}, linewidth=7+(6+2)*4):
        print(s.reshape(-1))
    

    Edit after some times : subclass

    Another method that came to my mind, would be to subclass ndarray, to make it behave as you would want

    import numpy as np
    
    
    class MyArr(np.ndarray):
    # To create a new array, with args ls: number of element to print per line, and arr, normal array to take data from
        def __new__(cls, ls, arr):
            n=np.ndarray.__new__(MyArr, (len(arr,)))
            n.ls=ls
            n[:]=arr[:]
            return n
    
        def __init__(self, *args):
            pass
    
        # So that this .ls is viral: when ever the array is created from an operation from an array that has this .ls, the .ls is copyied in the new array
        def __array_finalize__(self, obj):
            if not hasattr(self, 'ls') and type(obj)==MyArr and hasattr(obj, 'ls'):
                self.ls=obj.ls
    
        # Function to print an array with .ls elements per line
        def __repr__(self):
            # For other than 1D array, just use standard representation
            if len(self.shape)!=1:
                return super().__repr__()
    
            mxsize=max(len(str(s)) for s in self)
            s='['
            for i in range(len(self)):
                if i%self.ls==0 and i>0:
                     s+='\n '
                s+=f'{{:{mxsize}}}'.format(self[i])
                if i+1<len(self): s+=', '
            s+=']'
            return s
    

    Now you can use this MyArr to build your own 1D array

    MyArr(4, range(12))
    

    shows

    [ 0.0,  1.0,  2.0,  3.0, 
      4.0,  5.0,  6.0,  7.0, 
      8.0,  9.0, 10.0, 11.0]
    

    And you can use it anywhere a 1d ndarray is legal. And most of the time, the .ls attribute will follows (I say "most of the time", because I cannot guarantee that some functions wont build a new ndarray, and fill them with the data from this one)

    a=MyArr(4, range(12))
    a*2
    #[ 0.0,  2.0,  4.0,  6.0, 
    #  8.0, 10.0, 12.0, 14.0, 
    # 16.0, 18.0, 20.0, 22.0]
    a*a
    #[  0.0,   1.0,   4.0,   9.0, 
    #  16.0,  25.0,  36.0,  49.0, 
    #  64.0,  81.0, 100.0, 121.0]
    a[8::-1]
    #[8.0, 7.0, 6.0, 5.0, 
    # 4.0, 3.0, 2.0, 1.0, 
    # 0.0]
    
    # It even resists reshaping
    b=a.reshape((3,4))
    b
    #MyArr([[ 0.,  1.,  2.,  3.],
    #       [ 4.,  5.,  6.,  7.],
    #       [ 8.,  9., 10., 11.]])
    b.reshape((12,))
    #[ 0.0,  1.0,  2.0,  3.0, 
    #  4.0,  5.0,  6.0,  7.0, 
    #  8.0,  9.0, 10.0, 11.0]
    
    # Or fancy indexing
    a[np.array([1,2,5,5,5])]
    #[1.0, 2.0, 5.0, 5.0,
    # 5.0]
    
    
    # Or matrix operations
    M=np.eye(12,k=1)+2*M.identity(12) # Just a matrix
    M@a
    #[ 1.0,  4.0,  7.0, 10.0, 
    # 13.0, 16.0, 19.0, 22.0, 
    # 25.0, 28.0, 31.0, 22.0]
    np.diag(M*a)
    #[ 0.0,  2.0,  4.0,  6.0, 
    #  8.0, 10.0, 12.0, 14.0, 
    # 16.0, 18.0, 20.0, 22.0]
    
    # But of course, some time you loose the MyArr class
    import pandas as pd
    pd.DataFrame(a, columns=['v']).v.values
    #array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11.])
    
    
      [1]: https://stackoverflow.com/questions/25991666/how-to-efficiently-output-n-items-per-line-from-numpy-array