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]
])
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)
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)
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]
])
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)
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)