如何高效地将一维数组拉伸到任意大小,而不进行插值?

huangapple go评论65阅读模式
英文:

How can I efficiently stretch a one-dimensional array to an arbitrary size without interpolating?

问题

我正在尝试将一维数组拉伸到任意大小而不进行插值。

例如:

>>> my_array
*[0,1,8,4]*
>>> stretch(my_array, 7)
*[0,0,1,1,8,8,4]*

或者

>>> my_array
*[6,3,7,1]*
>>> stretch(my_array, 10)
*[6,6,3,3,3,7,7,7,1,1]*

等等。

我的天真方法确切地实现了我想要的功能。

def interp(list, length):
   out = np.zeros(length, dtype=np.uint)
   for x in range(length):
        out[x] = list[int(x * (len(list)/length))]
   return out

然而,这被证明是非常慢的;我尝试在每帧中执行几十次/几百次这样的操作。

numpy.interp 方法对于连续函数很好用,但我也想处理非连续的数据(就像示例中那样)。

numpy.repeat 接近,但只能将数组拉伸到某个整数倍数。

谢谢阅读!

编辑:为了澄清,我想做的是一维最近邻插值。

例如:

[ 5| 5| 7| 7| 7| 9| 9]
[  5   |   7  |   9  ]

我尝试从底部数组到顶部,对于任意大小。

目标数组的每个单元格映射到输入数组中的最近邻居。

英文:

I'm trying to stretch a one-dimensional array to an arbitrary size without interpolating.

Ex:

>>> my_array
*[0,1,8,4]*
>>> stretch(my_array, 7)
*[0,0,1,1,8,8,4]*

or

>>> my_array
*[6,3,7,1]*
>>> stretch(my_array, 10)
*[6,6,3,3,3,7,7,7,1,1]*

etc.

My naive approach does exactly what I want.

def interp(list, length):
   out = np.zeros(length, dtype=np.uint)
   for x in range(length):
        out[x] = list[int(x * (len(list)/length))]
   return out

However, this has proven to be extremely slow; I'm trying to do this dozens/hundreds of times per frame.

numpy.interp method works fine for a continuous function, but I'm trying to manipulate non-continuous data as well. (as in the examples)

numpy.repeat is close, but can only stretch an array by some whole number multiple.

Thanks for reading!

EDIT: To clarify, I guess I'm trying to do nearest-neighbor interpolation in one dimension.

Ex:

[ 5| 5| 7| 7| 7| 9| 9]
[  5   |   7  |   9  ]

I'm trying to get from the bottom array to the top, for any arbitrary size.

Each cell of the target array maps to its nearest neighbor in the input array.

答案1

得分: 1

精确的逻辑不清楚,但可能的矢量解决方案是使用numpy.repeat和切片:

my_array = np.array([0,1,8,4])

def stretch(a, n):
    return np.repeat(a, np.ceil(n/len(a)))[:n]

stretch(my_array, 7)
# array([0, 0, 1, 1, 8, 8, 4])

stretch(my_array, 10)
# array([0, 0, 0, 1, 1, 1, 8, 8, 8, 4])

您可以根据您的期望规则调整切片部分。

英文:

The exact logic is unclear, but a vectorial solution might be to use numpy.repeat and slicing:

my_array = np.array([0,1,8,4])

def stretch(a, n):
    return np.repeat(a, np.ceil(n/len(a)))[:n]

stretch(my_array, 7)
# array([0, 0, 1, 1, 8, 8, 4])

stretch(my_array, 10)
# array([0, 0, 0, 1, 1, 1, 8, 8, 8, 4])

You can adapt the slicing part with your desired rule

答案2

得分: 1

Create a list of m indexes running from 0 to n-1, by the formula k * n // m, and generate a new list by dereferencing the initial list, using an implicit loop.

E.g.

[6, 3, 7, 1] -> [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3] -> [6, 6, 6, 3, 3, 3, 7, 7, 7, 1, 1]

One-liner:

print([a[k * n // m] for k in range(m)])

Two-liner:

i= [k * n // m for k in range(m)]
print([a[i[k]] for k in range(m)])

If n and m are constant, you compute the indexes once for all. I doubt one can do much better.

OpenCV's resize does just that in the INTER_NEAREST mode. If done on a single image row, the overhead will probably be unbearable. If done on multiple rows, that might be faster.

英文:

Create a list of m indexes running from 0 to n-1, by the formula k * n // m, and generate a new list by dereferencing the initial list, using an implicit loop.

E.g.

[6, 3, 7, 1] -> [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3] -> [6, 6, 6, 3, 3, 3, 7, 7, 7, 1, 1]

One-liner:

print([a[k * n // m] for k in range(m)])

Two-liner:

i= [k * n // m for k in range(m)]
print([a[i[k]] for k in range(m)])

If n and m are constant, you compute the indexes once for all. I doubt one can do much better.


OpenCV's resize does just that in the INTER_NEAREST mode. If done on a single image row, the overhead will probably be unbearable. If done on multiple rows, that might be faster.

答案3

得分: 0

使用np.linspace生成索引的方法与Yves方法类似。

import numpy as np

def stretch(arr, length):
    idx = np.linspace(0, len(arr), length, endpoint=False).astype(np.int64)
    return arr[idx]

np.random.seed(1234)
arr = np.random.randint(0, 10, 10)

stretch(arr, 15)

时间性能比较:

%timeit [arr[k * len(arr) // 1000000] for k in range(1000000)]

%timeit stretch(arr, 1000000)

将10个元素拉伸到1,000,000个元素时,使用stretch方法比普通方法快40倍。

英文:

Similar to Yves method but using np.linspace to generate the indices.

import numpy as np

def stretch( arr, length ):
    idx = np.linspace( 0, len(arr), length, endpoint = False).astype( np.int64 )
    # endpoint = False to get the final index as len(arr) - 1
    # print( idx )
    # print( arr[idx] )
    return arr[idx]

np.random.seed( 1234 ) 
arr = np.random.randint( 0, 10, 10 )
arr
# array([3, 6, 5, 4, 8, 9, 1, 7, 9, 6])

stretch( arr, 15 )
# array([3, 3, 6, 5, 5, 4, 8, 8, 9, 1, 1, 7, 9, 9, 6])

Timings

%timeit [arr[k * len(arr) // 1000000] for k in range(1000000)]
284 ms ± 3.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit stretch( arr, 1000000 )
6.37 ms ± 18.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Stretching 10 elements to a 1,000,000 is 40 times as fast with stretch.

huangapple
  • 本文由 发表于 2023年5月30日 00:37:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/76358982.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定