从最小值开始,标注间的Numpy偏移值

db2dz4w8  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(72)

我有一个3D数组,维数为(2,4,500)。看一下[...,0],子数组看起来如下:

array([[ 700,    0, 2000,    0],
       [-400,    0,    0, -200]])

我可以保证,对于这个子数组的给定行,所有值都将是非负数或非正数(即,我们不会在同一行中有正值和负值)。
我试图实现的是将两行相互偏移,而不管位置如何,这样最小的(绝对值)条目将首先减少。对于上面的例子,所需的输出数组是:

array([[ 100,    0, 2000,    0],
       [   0,    0,    0,    0]])

也就是说,我可以先用-200抵消700,然后用-400抵消剩下的500。
在另一示例中:

array([[ 100,    0, 2000,    0],
       [   0,    0,    0, -200]])

我希望结果是:

array([[   0,    0, 1900,    0],
       [   0,    0,    0,    0]])

如果两行的条目具有相同的符号(例如,在上面的例子中将-200换成200),那么阵列应该保持原样。理想情况下,这可以直接应用于3D数组,但如果需要的话,我也很乐意循环遍历维度为(2,4)的2D子数组。
我的感觉是,一个整洁的解决方案必须存在,可能基于cumsum和argsort,但我一直无法使它工作。
编辑:每个子数组的终止条件是立即(如果两行的和具有相同的符号或为0),或者一旦其中一行变为零。
下面的代码示例应该能够突出我试图实现的目标:

import numpy as np

arr = np.array([[-700, 0, -2000, 0], [400, 0, 0, 200]])

out_arr = np.zeros_like(arr)
row0sum = np.nansum(arr[0])
row1sum = np.nansum(arr[1])
if np.sign(row0sum) * np.sign(row1sum) == -1:
    if abs(row0sum) > abs(row1sum):
        non_zeroing_index = 0
        zeroing_index = 1
    else:
        non_zeroing_index = 1
        zeroing_index = 0
    size_order = np.argsort(abs(arr[non_zeroing_index]))
    total_reducing_value = np.nansum(arr[zeroing_index])
    for i, val in enumerate(arr[non_zeroing_index][size_order]):
        if total_reducing_value == 0:
            out_arr[non_zeroing_index, size_order[i]] = val
        if val == 0:
            continue
        elif val > 0:
            resulting_value = np.clip(val + total_reducing_value, 0, val)
            reduction = resulting_value - val
            out_arr[non_zeroing_index, size_order[i]] = resulting_value
            total_reducing_value -= reduction
        else:
            resulting_value = np.clip(val + total_reducing_value, val, 0)
            reduction = resulting_value - val
            out_arr[non_zeroing_index, size_order[i]] = resulting_value
            total_reducing_value -= reduction
else:
    out_arr = arr
# move on to the next sub array

然而,在每个子数组上进行迭代必然会很慢,特别是如果必须进行数百次

aij0ehis

aij0ehis1#

你可以写一个offset函数来执行所有矩阵的任务:

import numpy as np

def offset(x):
  z = x.copy()
  sm = -z[:,1].sum(1)
  cmsm = z[:,0].cumsum(1)
  bool_idx = cmsm >= sm[:,None]
  
  s = np.where(bool_idx)
  mat, ind_index = np.unique(s[0], return_counts = True)
  idx = np.minimum.reduceat(s[1], np.r_[0,ind_index.cumsum()[:-1]])
  z[:,0][~bool_idx] = 0
  z[mat, 0, idx] = cmsm[mat, idx] - sm
  z[:,1] = 0
  return z
a = np.array([[ 700,    0, 2000,    0], [-400,    0,    0, -200]])
b = np.array([[ 100,    0, 2000,    0], [   0,    0,    0, -200]])

full_array = np.array([a, b]) # You need a 3 dimensional array to pass to the function

offset(full_array)
array([[[ 100,    0, 2000,    0],
        [   0,    0,    0,    0]],

       [[   0,    0, 1900,    0],
        [   0,    0,    0,    0]]])

编辑

如果您有较大的底片,请使用下面的函数:

def multi_offset(x):
  z = x.copy()
  index = z.sum((1,2)) >= 0
  z[index] = offset(z[index])
  z[~index] = -offset(-z[~index][:,::-1,:])[:,::-1,:]
  return z

c = np.array([[400, 0, 0, 200], [-700, 0, -2000, 0]])
multi_offset(np.array([a, b, c]))
array([[[  100,     0,  2000,     0],
        [    0,     0,     0,     0]],

       [[    0,     0,  1900,     0],
        [    0,     0,     0,     0]],

       [[    0,     0,     0,     0],
        [ -100,     0, -2000,     0]]])

相关问题