Python 使用numpy repeat的两个阵列的元素方向编织

Python 使用numpy repeat的两个阵列的元素方向编织,python,arrays,performance,numpy,scientific-computing,Python,Arrays,Performance,Numpy,Scientific Computing,我有两个长度不等的数组val1和val2,我试图以一种特定的方式将它们编织在一起,这是由等长数组mult1和mult2定义的。一般来说,我的数组非常长(~1e6个元素),这是我计算中的一个性能关键瓶颈,因此我无法承担python for循环的费用,因此我尝试利用Numpy中的矢量化函数。为了明确起见: mult1 = np.array([0, 1, 2, 1, 0]) mult2 = np.array([1, 0, 1, 1, 0]) val1 = np.array([1, 2, 3, 4])

我有两个长度不等的数组val1和val2,我试图以一种特定的方式将它们编织在一起,这是由等长数组mult1和mult2定义的。一般来说,我的数组非常长(~1e6个元素),这是我计算中的一个性能关键瓶颈,因此我无法承担python for循环的费用,因此我尝试利用Numpy中的矢量化函数。为了明确起见:

mult1 = np.array([0, 1, 2, 1, 0])
mult2 = np.array([1, 0, 1, 1, 0])

val1 = np.array([1, 2, 3, 4])
val2 = np.array([-1, -2, -3])

desired_final_result = np.array([-1, 1, 2, 3, -2, 4, -3])
val1和val2的交织由以下通过mult1和mult2索引的元素级处理定义。两个mult数组的每个条目定义从相应的val数组中选择多少个元素。我们通过mult数组进行元素分析;mult1[i]的值决定我们从val1中选择多少个条目,然后我们继续使用mult2[i]的值来选择适当数量的val2条目,始终选择val1条目作为每个索引i的第一个条目

请注意,len(val1)=mult1.sum()len(val2)=mult2.sum(),因此我们总是以len(期望的最终结果)=len(val1)+len(val2)作为最终数组

最小示例的显式解释
  • 由于mult1的条目i=0为0,因此我们从val1中选择0条目,然后转到mult2的条目i=0,即1,因此我们从val2中选择1条目。这解释了为什么期望的_final_结果的第一个条目是-1

  • 由于mult1的条目i=1是1,因此我们从val1中选择1条目,然后转到mult2的条目i=1,即0,因此从val2中选择0条目。这解释了为什么期望的_final_结果的第二个条目是1

  • 由于mult1的条目i=2是2,因此我们从val1中选择下一个2条目,然后转到mult2的条目i=2,即1,因此我们从val2中选择下一个1条目。这就解释了为什么期望的最终结果的条目2-4是2,3,-2

  • 由于mult1的条目i=3是1,因此我们从val1中选择下一个1条目,然后转到mult2的条目i=3,该条目也是1,因此我们从val2中选择下一个1条目。这解释了为什么期望的最终结果的条目5-6是4,-3

  • 最后,由于mult1和mult2的i=4都是0,因此我们无事可做,数组被填充

问题:
有没有办法使用矢量化函数(如np.repeat和/或np.choose)来解决我的问题?或者我需要用C编写这个计算并将其包装到python中吗?

这可以用NumPy例程来完成,但我想到的最好的方法是非常笨拙的:

reps = numpy.empty([len(mult1)*2], dtype=int)
reps[::2] = mult1
reps[1::2] = mult2

to_repeat = numpy.empty_like(reps)
to_repeat[::2] = -1   # Avoid using 0 and 1 in case either of val1 or val2 is empty
to_repeat[1::2] = -2

indices = numpy.repeat(to_repeat, reps)
indices[indices==-1] = numpy.arange(len(val1))
indices[indices==-2] = numpy.arange(len(val1), len(val1) + len(val2))

final_result = numpy.concatenate([val1, val2])[indices]

在结果数组中创建布尔索引:

mult = np.array([mult1, mult2]).ravel('F')
tftf = np.tile([True, False], len(mult1))
mask = np.repeat(tftf, mult)

result = np.empty(len(val1) + len(val2), int)
result[ mask] = val1
result[~mask] = val2
编辑-我相信这也行得通:

idx = np.repeat(mult1.cumsum(), mult2)
result = np.insert(val1, idx, val2)

它很短,但可能不会更快。

Cython或Numba会让你为此编写一个简单的循环,并且速度很快。由于你的切片和输入长度不同,我也发现它不可能被合理地矢量化。也许我不清楚两个mult数组的长度是否总是相等。好的,但是
val1
val2
不是,而且您想要一起裁剪的子阵列尤其不是。我不是说这一定是不可能的,但如果是的话,我也不会感到惊讶。啊,对。好吧,如果不可能的话,那就不可能了。正如我所说,我总是可以用cython“手动”编写代码,但我发现,当存在一个自然的解决方案时,我永远无法打败Numpy,这就是我为什么要问的原因。不管怎样,谢谢你考虑一下,@AndrasDeak,在走这条路之前,我会再等一段时间,看看有没有人有想法。哦,嗯。您只需使用
repeat
调用构建一个掩码数组,并使用它直接放置元素。这比我做的简单多了。哇,这真是太美了@morningsun。你之前已经给我演示过这个np.tile技巧,但直到现在我才意识到它有多有用。明亮的我还应该补充一点,我用cython编写了“暴力”方法。即使在彻底清理了cython的任何python层之后,我发现对于长度为~1e6的数组,您的解决方案的速度大约是我的2.5倍。太好了。@user2357112-我想如果一个人想推广到2个以上的数组,你会得到一个标签数组,与你的答案类似。@morningsun-第二个解决方案是1。更圆滑,2。不那么直观(无论如何对我来说),3。对于具有阶单位重数的大型阵列,速度大约慢30%。所以,numpy巫术的加分,但我认为你的第一个解决方案更可取。