沿numpy数组中的范围应用函数

时间:2019-02-05 18:15:57

标签: python numpy

说我有以下numpy数组:

a = np.arange(20)

还有一个包含索引的数组,如下所示:

ix = np.array([4,10,15])

我一直在尝试针对以下问题提出矢量化解决方案:如何使用a中的索引对ix进行分解的函数?

所以说我在哪里将anp.split分开(我仅在这里使用np.split来说明我想在此处应用功能的组):

np.split(a,ix)

[array([0, 1, 2, 3]),
 array([4, 5, 6, 7, 8, 9]),
 array([10, 11, 12, 13, 14]),
 array([15, 16, 17, 18, 19])]

比如说我想对每个块取总和,所以给出:

[6, 39, 60, 85]

如何使用numpy将其向量化?

3 个答案:

答案 0 :(得分:1)

我不知道这是否是最好的解决方案,但是您可以通过添加零将具有不同大小的数组列表转换为固定大小的数组列表。然后实现诸如sum之类的不受零影响的函数。

请参见下面的示例。

a = np.arange(20)
ix = np.array([4,10,15])
b = np.split(a,ix)
print(b)

结果

[array([0, 1, 2, 3]),
 array([4, 5, 6, 7, 8, 9]),
 array([10, 11, 12, 13, 14]),
 array([15, 16, 17, 18, 19])]

然后使用itertools将列表转换为数组from here

import itertools
c = np.array(list(itertools.zip_longest(*b, fillvalue=0))).T
print(c)

这将导致

[[ 0  1  2  3  0  0]
 [ 4  5  6  7  8  9]
 [10 11 12 13 14  0]
 [15 16 17 18 19  0]]

然后使用

求和
np.sum(c, axis = 1)

结果

array([ 6, 39, 60, 85])

答案 1 :(得分:1)

split生成数组列表,其长度可能不同。它实际上是反复进行的

In [12]: alist = []
In [13]: alist.append(a[0:idx[0]])
In [14]: alist.append(a[idx[0]:idx[1]])
In [15]: alist.append(a[idx[1]:idx[2]])
....

分别将sum应用于列表的每个元素是有道理的:

In [11]: [np.sum(row) for row in alist]
Out[11]: [6, 39, 60, 85]

当您拥有形状不同的数组的列表时,可以肯定的是,您将必须对其进行Python级迭代。

快速的“向量化”意味着以编译的代码执行计算。大多数是围绕多维数组构建的,例如2d个。如果您的split产生了相等大小的数组,则可以将np.sum与相应的axis参数一起使用。

In [23]: a1 = a.reshape(4,5)
In [24]: a1
Out[24]: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])
In [25]: np.sum(a1, axis=1)
Out[25]: array([10, 35, 60, 85])

有时候,我们可以玩弄技巧,将问题转换为n-d维,例如,如果拆分的第一个数组用0填充。但是转换本身可能需要迭代。

如此处(及其链接)所述Origin of AttributeError: object has no attribute 'cos' 应用于对象dtype数组的math(ufunc)函数最终将操作委派给对象的相应方法。但这仍然涉及对象的(近)Python级迭代。


一些时间:

In [57]: timeit [np.sum(row) for row in alist]
31.7 µs ± 1.21 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [58]: timeit np.sum(list(itertools.zip_longest(*alist, fillvalue=0)),axis=0)
25.2 µs ± 82 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [59]: timeit np.nansum(pd.DataFrame(alist), axis=1)
908 µs ± 28.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [61]: timeit np.frompyfunc(sum,1,1)(alist)
12.9 µs ± 21.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

在后一种情况下,Python sumnp.sum快。但是列表理解也是这样:

In [63]: timeit [sum(row) for row in alist]
6.86 µs ± 13.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

以及Divakar的fillnaNumpy: Fix array with rows of different lengths by filling the empty elements with zeros

In [70]: timeit numpy_fillna(np.array(alist)).sum(axis=1)
44.2 µs ± 208 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

一旦有了多维数组,numpy代码就会很快。但是,如果从列表开始,甚至从数组列表开始,Python列表方法通常会更快。构造数组(或数据框)所花费的时间从来都不短。

答案 2 :(得分:1)

熊猫解决方案将是:

import numpy as np
import pandas as pd

a = np.arange(20)

ix = np.array([4, 10, 15])

data = pd.DataFrame(np.split(a, ix))

print(np.nansum(data, axis=1))

输出

[ 6. 39. 60. 85.]