Flip column values in NumPy matrix while preserving NaN values and respecting duplicate values

Question:

I want to flip the values in each column of a NumPy matrix. The function should leave NaN values as they are in the output matrix and duplicate values should be replaced by their flipped counterpart. Here’s an example of an input and output matrix:

A = np.array([
    [1.0,2.0,3.0],
    [np.nan,2.0,np.nan],
    [2.0,1.0,np.nan],
    [3.0,np.nan,1.0]
    ])

A_flipped = np.array([
    [3.0,1.0,1.0],
    [np.nan,1.0,np.nan],
    [2.0,2.0,np.nan],
    [1.0,np.nan,3.0]
    ])
Asked By: Johannes Wiesner

||

Answers:

def flip_column_values(A):
    
    A_copy = np.copy(A)
    
    for col in A_copy.T:
        values = col[~np.isnan(col)] # get all non-missing values in column
        values_sorted = np.sort(values) # sort them ascending
  
        # create new array with flipped values
        values_new = np.empty_like(values) # get
        for i in range(len(values_sorted)):
            flipped = values_sorted[len(values_sorted)-i-1]
            values_new[values==values_sorted[i]] = flipped
        
        col[~np.isnan(col)] = values_new
        
    return A_copy

# test if function delivers right output
output = flip_column_values(A)
np.testing.assert_equal(output,A_flipped)
Answered By: arrmansa

I the meantime I also came up with my own two-function solution that uses dictionary mapping with np.vectorize. It also uses np.apply_along_axis instead of a for-loop. Both np.vectorize and np.apply_along_axis might be faster for large arrays. Also note, that with dictionary mapping there’s no need to impute np.nan values, so we also save some lines of code:

def _reverse_array_values(a):
    unique_vals = np.unique(a[~np.isnan(a)])  # Get unique non-nan values
    flip_dict = dict(zip(unique_vals,np.flip(unique_vals)))  # Create flip dictionary
    return np.vectorize(flip_dict.get)(a)  # map old values onto new values

def flip_column_values(A):
    A_ = np.apply_along_axis(_reverse_array_values,0,A)
    return A_

output = flip_column_values(A)
np.testing.assert_equal(output,A_flipped)
Answered By: Johannes Wiesner
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.