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 级别的迭代。
快速“向量化”意味着在编译后的代码中执行计算。大多数是围绕多维数组构建的,例如二维的。如果您的 split 生成了相同大小的数组,您可以将 np.sum 与适当的轴参数一起使用。
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 数组的数学 (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 sum 比 np.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 的 wiz-bang fillna, Numpy: 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 列表方法通常更快。构造一个数组(或 Dataframe)所花费的时间从来都不是微不足道的。