你可以将布尔索引与高级numpy数组索引结合使用:
array3 = array1.astype(float)
array3[array1 != 0] = array2[array1[array1 != 0]-1, 2]
结果是:
array([[ 0, 62., 62., 88.],
[ 0, 73., 64., 95.],
[ 0, 59., 67., 65.]])
解释
首先创建一个布尔数组,指示哪些位置有非零条目:
>>> non_zero_mask = array1 != 0
array([[False, True, True, True],
[False, True, True, True],
[False, True, True, True]], dtype=bool)
这将用于查找应替换的元素。
然后您需要找到这些元素的值:
>>> non_zero_values = array1[non_zero_mask]
array([7, 4, 1, 8, 5, 2, 9, 6, 3])
由于你的array2
是有序的,并且以值1开头,因此我们需要减去1才能找到适当的替换值行。如果你的array2
没有排序,你可能需要对其进行排序或在其中进行另一个索引:
>>> replacement_rows = array2[non_zero_values-1]
array([[ 7., 7., 62.],
[ 4., 4., 62.],
[ 1., 1., 88.],
[ 8., 8., 73.],
[ 5., 5., 64.],
[ 2., 2., 95.],
[ 9., 9., 59.],
[ 6., 6., 67.],
[ 3., 3., 65.]])
>>> replacement_values = array2[non_zero_values-1, 2] # third element of that row!
array([ 62., 62., 88., 73., 64., 95., 59., 67., 65.])
然后只需将这些值分配给原始或新数组:
array3[non_zero_mask] = replacement_values
这种方法依赖于
array2
的排序顺序,因此如果存在更复杂的条件,则会出现错误。但是,要解决这个问题,需要找到值和索引之间的关系,并将其插入到简单的
-1
中,或者进行另一个中间步骤的
np.where
/布尔索引。
扩展
如果您没有排序过的array2
,并且无法对其进行排序,则可以执行以下操作:
>>> array3 = array1.astype(float)
>>> array3[array1 != 0] = array2[np.where(array2[:, 0][None, :] == array1[array1 != 0][:, None])[1], 2]
>>> array3
array([[ 0., 62., 62., 88.],
[ 0., 73., 64., 95.],
[ 0., 59., 67., 65.]])
由于这个方法使用广播数组相互作用,你将创建一个大小为array1.size * array1.size
的数组。因此,这可能不是非常节省内存,但仍然完全向量化。
Numba(如果你想要速度)
numba非常适合加速那些没有本地numpy或scipy版本会很慢的事情。如果你有anaconda或conda,它已经安装了,所以这可能是一个可行的选择:
import numba as nb
import numpy as np
@nb.njit
def nb_replace_values(array, old_new_array):
res = np.zeros(array.shape, dtype=np.float64)
rows = array.shape[0]
columns = array.shape[1]
rows_replace_array = old_new_array.shape[0]
for row in range(rows):
for column in range(columns):
val = array[row, column]
if val != 0:
for ind_replace in range(rows_replace_array):
if old_new_array[ind_replace, 0] == val:
res[row, column] = old_new_array[ind_replace, 2]
break
return res
nb_replace_values(array1, array2)
array([[ 0., 62., 62., 88.],
[ 0., 73., 64., 95.],
[ 0., 59., 67., 65.]])
特别对于大数组而言,这将是最快和最节省内存的解决方案,因为不会创建任何临时数组。第一次调用将更慢,因为函数需要即时编译。
时间记录:
%timeit nb_replace_values(array1, array2)
100000次循环,3次中的最佳结果:每个循环6.23微秒。
%%timeit
array3 = array1.astype(float)
array3[array1 != 0] = array2[np.where(array2[:, 0][None, :] == array1[array1 != 0][:, None])[1], 2]
10000次循环,3次中的最佳结果:每个循环花费74.8微秒。
%%timeit
array3 = array1.astype(float)
for i in array2[:,0]:
i_arr1,j_arr1 = np.where(array1 == i)
i_arr2 = np.where(array2[:,0] == i)
array3[i_arr1,j_arr1] = array2[i_arr2,2]
1000次循环,3次中的最佳结果:每个循环689微秒。
pandas
或自定义的numba
函数来处理这些情况。 - MSeifert