用不同数组的项替换数组的项

2024-10-02 14:24:08 发布

您现在位置:Python中文网/ 问答频道 /正文

考虑一个numpy2D整数数组,其中一些条目是0(array1)。考虑一个不同的二维数组(array2),其中第一列具有与array1相同的非零值,而另一列(比如索引2)具有不同的数值(float)。你知道吗

如何通过将array1中的每个非零条目替换为array2的第2列的相应值来创建新的array3?你怎么做到超级干净?你知道吗

示例:

>>> array1
array([[0, 27, 43, 10],
       [0, 80, 15,  2],
       [0,  3,  6,  9]])

>>> array2
array([[ 10.,  4., 88.],
       [  2.,  2., 95.],
       [  9.,  2., 65.],
       [ 43.,  1., 62.],
       [ 15.,  5., 64.],
       [  6.,  6., 67.],
       [ 27.,  5., 62.],
       [ 80.,  8., 73.],
       [  3.,  9., 59.]])

>>> array3
array([[0., 62., 62., 88.],
       [0., 73., 64., 95.],
       [0., 59., 67., 65.]])

Tags: 示例条目整数数组floatarray数值array1
2条回答

可以将布尔索引与高级numpy数组索引一起使用:

array3 = array1.astype(float) # this copies the array by default.
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的数组。所以这可能不是很有效,但仍然完全矢量化。你知道吗

麻木(如果你想要速度)

是伟大的,如果你想加快事情会很慢,因为没有原生的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]
            # only replace values that are not zero
            if val != 0:
                # Find the value to replace the element with
                for ind_replace in range(rows_replace_array):
                    if old_new_array[ind_replace, 0] == val:
                        # Match found. Replace and break the innermost loop
                        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 loops, best of 3: 6.23 µs per loop

%%timeit
array3 = array1.astype(float)
array3[array1 != 0] = array2[np.where(array2[:, 0][None, :] == array1[array1 != 0][:, None])[1], 2]

10000 loops, best of 3: 74.8 µs per loop

# Solution provided by @PDRX
%%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 loops, best of 3: 689 µs per loop

你知道吗 我不确定我是否理解您的要求,但让我们试试list comprehensions

array3 = [[array2[subitem1 - 1][2] if subitem1 != 0 else 0 for subitem1 in subarray1] for subarray1 in array1]

但很难阅读,我更喜欢列表:

array3 = [
    [
        array2[subitem1 - 1][2] if subitem1 != 0 else 0
        for subitem1 in subarray1
    ]
    for subarray1 in array1
]

相关问题 更多 >