Search code examples
pythonarraysnumpyzip

Zip arrays in Python


I have one 2D array and one 1D array. I would like to zip them together.

import numpy as np

arr2D = [[5.88964708e-02, -2.38142395e-01, -4.95821417e-01, -7.07269274e-01],
         [0.53363666,  0.1654723 , -0.16439857, -0.44880487]]
arr2D = np.asarray(arr2D)

arr1D = np.arange(7, 8.5+0.5, 0.5)
arr1D = np.asarray(arr1D)

res = np.array(list(zip(arr1D, arr2D)))

print(res)

which results in:

[[7.0 array([ 0.05889647, -0.2381424 , -0.49582142, -0.70726927])]
 [7.5 array([ 0.53363666,  0.1654723 , -0.16439857, -0.44880487])]]

But I am trying to get:

[[(7.0, 0.05889647), (7.5, -0.2381424), (8.0, -0.49582142), (8.5, -0.70726927)]]
[[(7.0, 0.53363666), (7.5, 0.1654723),(8.0, -0.16439857), (8.5, -0.44880487)]]

How can I do this?


Solution

  • In [382]: arr2D = [[5.88964708e-02, -2.38142395e-01, -4.95821417e-01, -7.07269274e-01], 
         ...:          [0.53363666,  0.1654723 , -0.16439857, -0.44880487]] 
         ...: arr2D = np.asarray(arr2D) 
         ...: arr1D = np.arange(7, 8.5+0.5, 0.5)   # already an array                                      
    
    
    In [384]: arr2D.shape                                                                                  
    Out[384]: (2, 4)
    In [385]: arr1D.shape                                                                                  
    Out[385]: (4,)
    

    zip iterates on the first dimension of the arguments, and stops with the shortest:

    In [387]: [[i,j[0:2]] for i,j in zip(arr1D, arr2D)]                                                    
    Out[387]: 
    [[7.0, array([ 0.05889647, -0.2381424 ])],
     [7.5, array([0.53363666, 0.1654723 ])]]
    

    If we transpose the 2d, so it is now (4,2), we get a four element list:

    In [389]: [[i,j] for i,j in zip(arr1D, arr2D.T)]                                                       
    Out[389]: 
    [[7.0, array([0.05889647, 0.53363666])],
     [7.5, array([-0.2381424,  0.1654723])],
     [8.0, array([-0.49582142, -0.16439857])],
     [8.5, array([-0.70726927, -0.44880487])]]
    

    We could add another level of iteration to get the desired pairs:

    In [390]: [[(i,k) for k in j] for i,j in zip(arr1D, arr2D.T)]                                          
    Out[390]: 
    [[(7.0, 0.0588964708), (7.0, 0.53363666)],
     [(7.5, -0.238142395), (7.5, 0.1654723)],
     [(8.0, -0.495821417), (8.0, -0.16439857)],
     [(8.5, -0.707269274), (8.5, -0.44880487)]]
    

    and with list transpose idiom:

    In [391]: list(zip(*_))                                                                                
    Out[391]: 
    [((7.0, 0.0588964708), (7.5, -0.238142395), (8.0, -0.495821417), (8.5, -0.707269274)),
     ((7.0, 0.53363666), (7.5, 0.1654723), (8.0, -0.16439857), (8.5, -0.44880487))]
    

    Or we can get that result directly by moving the zip into an inner loop:

    [[(i,k) for i,k in  zip(arr1D, row)] for row in arr2D] 
    

    In other words, you are pairing the elements of arr1D with the elements of each row of 2D, rather than with the whole row.

    Since you already have arrays, one of the array solutions might be better, but I'm trying to clarify what is happening with zip.

    numpy

    There are various ways of building a numpy array from these arrays. Since you want to repeat the arr1D values:

    This repeat makes a (4,2) array that matchs arr2D (tile also works):

    In [400]: arr1D[None,:].repeat(2,0)                                                                    
    Out[400]: 
    array([[7. , 7.5, 8. , 8.5],
           [7. , 7.5, 8. , 8.5]])
    In [401]: arr2D                                                                                        
    Out[401]: 
    array([[ 0.05889647, -0.2381424 , -0.49582142, -0.70726927],
           [ 0.53363666,  0.1654723 , -0.16439857, -0.44880487]])
    

    which can then be joined on a new trailing axis:

    In [402]: np.stack((_400, arr2D), axis=2)                                                              
    Out[402]: 
    array([[[ 7.        ,  0.05889647],
            [ 7.5       , -0.2381424 ],
            [ 8.        , -0.49582142],
            [ 8.5       , -0.70726927]],
    
           [[ 7.        ,  0.53363666],
            [ 7.5       ,  0.1654723 ],
            [ 8.        , -0.16439857],
            [ 8.5       , -0.44880487]]])
    

    Or a structured array with tuple-like display:

    In [406]: arr = np.zeros((2,4), dtype='f,f')                                                           
    In [407]: arr                                                                                          
    Out[407]: 
    array([[(0., 0.), (0., 0.), (0., 0.), (0., 0.)],
           [(0., 0.), (0., 0.), (0., 0.), (0., 0.)]],
          dtype=[('f0', '<f4'), ('f1', '<f4')])
    In [408]: arr['f1'] = arr2D                                                                            
    In [409]: arr['f0'] = _400                                                                             
    In [410]: arr                                                                                          
    Out[410]: 
    array([[(7. ,  0.05889647), (7.5, -0.2381424 ), (8. , -0.49582142),
            (8.5, -0.70726925)],
           [(7. ,  0.5336367 ), (7.5,  0.1654723 ), (8. , -0.16439857),
            (8.5, -0.44880486)]], dtype=[('f0', '<f4'), ('f1', '<f4')])