Search code examples
pythonarraysinputmergeargs

Merge arrays without summing their overlapping cells on Python


I have a variable amount of arrays with the same size and same dtype. Their cells (called pixels in my code) either have a float number or a NaN. I would like to merge these arrays but with 3 specific criterias. If for one pixel (called overlapping pixel):

  • At least 2 arrays have a value (non-NaN): attribute to the overlapping pixel in the merged array the value of 1 of the input arrays only.
  • Only 1 input array has a value, attribute to the overlapping pixel in the merged array the value of this input array's pixel.
  • If none of the input arrays have a value for a specific pixel, we write a np.nan in the overlapping pixel.

In order to do that, I have a loop going through each pixel, and evaluating how many input arrays have a value. To satisfy the 1st criteria, I wrote a set of if/elif/else conditions. To satisfy the 2nd criteria, the else part of my conditions is simply a np.nansum (because all but 1 array have NaNs at this specific pixel).

I wrote a function that is not efficient at all, and quite limiting. How could I improve my code in order to treat a variable amount of arrays to merge ? (more than 3 arrays).

My code:

import numpy as np

def merger(*args):

    # This function evaluates pixel per pixel the values of 2 to 3 arrays the same size. 
    # Each pixel either has a value or a NaN. We want to merge the arrays without summing their values at overlapping pixels.
    # If at least two arrays have a value for an intersecting pixel, we pick one of the array's value to attribute to the merging pixel in the new array.


    # If we have 2 arrays to merge
    if len(args) == 2:
      
      C = np.empty([args[0].shape[0], args[0].shape[1], args[0].shape[2]],dtype=float)
      
      for b in range(args[0].shape[0]):
        for i in range(args[0].shape[1]):
          for j in range(args[0].shape[2]):

            # If the two similar pixels have a value, take the value of the first array
            if np.isnan(args[0][b,i,j]) == False and np.isnan(args[1][b,i,j]) == False:

              C[b,i,j] = args[0][b,i,j]

            # If all of the pixels are NaN, we input a NaN
            elif np.isnan(args[0][b,i,j]) == True and np.isnan(args[1][b,i,j]) == True:

              C[b,i,j] = np.nan
            
            # Else, take the nansum of the two pixels (because one is a NaN, the other will be the real value)
            else:
              C[b,i,j] = np.nansum([args[0][b,i,j],args[1][b,i,j]])

      
    # If we have 3 arrays to merge (A1, A2 and A3) 
    if len(args) == 3:
      
      C = np.empty([args[0].shape[0], args[0].shape[1], args[0].shape[2]],dtype=float)
      
      for b in range(args[0].shape[0]):
        for i in range(args[0].shape[1]):
          for j in range(args[0].shape[2]):

            # If A1 and A2 have a value but not A3, pick the value of A1. If A1 and A3 have a value but not A2, pick the value of A1
            if np.isnan(args[0][b,i,j]) == False and np.isnan(args[1][b,i,j]) == False and np.isnan(args[2][b,i,j]) == True or np.isnan(args[0][b,i,j]) == False and np.isnan(args[2][b,i,j]) == False and np.isnan(args[1][b,i,j]) == True:

              C[b,i,j] = args[0][b,i,j]

            # If A2 and A3 have a value but not A1, pick the value of A2
            elif np.isnan(args[1][b,i,j]) == False and np.isnan(args[2][b,i,j]) == False and np.isnan(args[0][b,i,j]) == True:

              C[b,i,j] = args[1][b,i,j]

            # If all the arrays have a value, pick the value of A3
            elif np.isnan(args[1][b,i,j]) == False and np.isnan(args[2][b,i,j]) == False and np.isnan(args[2][b,i,j]) == False:

              C[b,i,j] = args[2][b,i,j]

            # If all of the pixels are NaN, we input a NaN
            elif np.isnan(args[1][b,i,j]) == True and np.isnan(args[2][b,i,j]) == True and np.isnan(args[2][b,i,j]) == True:

              C[b,i,j] = np.nan

            # If only one array has a value, nansum will attribute this value to the pixel
            else:
              C[b,i,j] = np.nansum([args[0][b,i,j],args[1][b,i,j], args[2][b,i,j]])




      return C



# Example
A1 = np.array([[[1, 1, 1],[np.nan, np.nan, np.nan], [1, np.nan, 1]],[[1, 1, 1],[np.nan, np.nan, np.nan], [1, np.nan, 1]]])
A2 = np.array([[[1, np.nan, 1],[1, np.nan, np.nan], [1, np.nan, 1]],[[1, 1, 1],[np.nan, np.nan, 1], [np.nan, 1, 1]]])
A3 = np.array([[[1, 1, 1],[np.nan, np.nan, 1], [np.nan, 1, 1]],[[1, np.nan, 1],[1, np.nan, np.nan], [1, np.nan, 1]]])


merger(A1, A2, A3)

array([[[ 1.,  1.,  1.],
        [ 1., nan,  1.],
        [ 1.,  1.,  1.]],

       [[ 1.,  1.,  1.],
        [ 1., nan,  1.],
        [ 1.,  1.,  1.]]])

Solution

  • Unless I'm missing something, how is this any different than iteratively replacing A1 nan values with A2 then A3? Since you aren't summing anything and you are arbitrarily picking one non-null value from the other arrays.

    A1 = np.array([[[1, 1, 1],[np.nan, np.nan, np.nan], [1, np.nan, 1]],[[1, 1, 1],[np.nan, np.nan, np.nan], [1, np.nan, 1]]])
    A2 = np.array([[[1, np.nan, 1],[1, np.nan, np.nan], [1, np.nan, 1]],[[1, 1, 1],[np.nan, np.nan, 1], [np.nan, 1, 1]]])
    A3 = np.array([[[1, 1, 1],[np.nan, np.nan, 1], [np.nan, 1, 1]],[[1, np.nan, 1],[1, np.nan, np.nan], [1, np.nan, 1]]])
    
    
    A1[np.isnan(A1)] = A2[np.isnan(A1)]
    A1[np.isnan(A1)] = A3[np.isnan(A1)]
    print(A1)
    

    Output

    [[[ 1.  1.  1.]
      [ 1. nan  1.]
      [ 1.  1.  1.]]
    
     [[ 1.  1.  1.]
      [ 1. nan  1.]
      [ 1.  1.  1.]]]