如何在不改变数组a的地址的情况下,最快地将数据从数组b复制到数组a?我需要这样做是因为一个外部库(PyFFTW)正在使用指向我的数组的指针,而且这个指针不能更改。
例如:
a = numpy.empty(n, dtype=complex)
for i in xrange(a.size):
a[i] = b[i]
不使用循环是否有可能实现这个目标?
我相信
a = numpy.empty_like(b)
a[:] = b
会快速地复制这些值。如Funsi所提到的,最新版本的numpy也有copyto
函数。
NumPy 1.7版本引入了numpy.copyto
函数,可以满足您的需求:
numpy.copyto(dst, src)
将一个数组中的值复制到另一个数组中,必要时会进行广播。
参考文献: https://docs.scipy.org/doc/numpy/reference/generated/numpy.copyto.html
a = numpy.array(b)
速度比numpy v1.6及以下版本中建议的解决方案都要快,并且也会复制数组。然而,我无法将其与copyto(a,b)进行测试,因为我没有最新版本的numpy。
numpy.array(src)
或numpy.copyto(dst, src)
将数据从一个numpy数组复制到另一个。
更新 2022-05:使用numpy v1.22和CPython v3.9重新测试表明,在我的系统上,src.astype(...)
目前几乎始终最快。因此,最好运行提供的代码片段以获取特定设置的数字。
(但是,如果dst
的内存已经分配,则始终选择numpy.copyto(dst, src)
,以重用内存。请参见本文末尾的分析。)
分析设置
import timeit
import numpy as np
import pandas as pd
from IPython.display import display
def profile_this(methods, setup='', niter=10 ** 4, p_globals=None, **kwargs):
if p_globals is not None:
print('globals: {0}, tested {1:.0e} times'.format(p_globals, niter))
timings = np.array([timeit.timeit(method, setup=setup, number=niter,
globals=p_globals, **kwargs) for
method in methods])
ranking = np.argsort(timings)
timings = np.array(timings)[ranking]
methods = np.array(methods)[ranking]
speedups = np.amax(timings) / timings
# pd.set_option('html', False)
data = {'time (s)': timings,
'speedup': ['{:.2f}x'.format(s) if s != 1 else '' for s in speedups],
'methods': methods}
data_frame = pd.DataFrame(data, columns=['time (s)', 'speedup', 'methods'])
display(data_frame)
print()
代码分析
setup = '''import numpy as np; x = np.random.random(n)'''
methods = (
'''y = np.zeros(n, dtype=x.dtype); y[:] = x''',
'''y = np.zeros_like(x); y[:] = x''',
'''y = np.empty(n, dtype=x.dtype); y[:] = x''',
'''y = np.empty_like(x); y[:] = x''',
'''y = np.copy(x)''',
'''y = x.astype(x.dtype)''',
'''y = 1*x''',
'''y = np.empty_like(x); np.copyto(y, x)''',
'''y = np.empty_like(x); np.copyto(y, x, casting='no')''',
'''y = np.empty(n)\nfor i in range(x.size):\n\ty[i] = x[i]'''
)
for n, it in ((2, 6), (3, 6), (3.8, 6), (4, 6), (5, 5), (6, 4.5)):
profile_this(methods[:-1:] if n > 2 else methods, setup,
niter=int(10 ** it), p_globals={'n': int(10 ** n)})
Windows 7上Intel i7 CPU,CPython v3.5.0,numpy v1.10.1的结果。
globals: {'n': 100}, tested 1e+06 times
time (s) speedup methods
0 0.386908 33.76x y = np.array(x)
1 0.496475 26.31x y = x.astype(x.dtype)
2 0.567027 23.03x y = np.empty_like(x); np.copyto(y, x)
3 0.666129 19.61x y = np.empty_like(x); y[:] = x
4 0.967086 13.51x y = 1*x
5 1.067240 12.24x y = np.empty_like(x); np.copyto(y, x, casting=...
6 1.235198 10.57x y = np.copy(x)
7 1.624535 8.04x y = np.zeros(n, dtype=x.dtype); y[:] = x
8 1.626120 8.03x y = np.empty(n, dtype=x.dtype); y[:] = x
9 3.569372 3.66x y = np.zeros_like(x); y[:] = x
10 13.061154 y = np.empty(n)\nfor i in range(x.size):\n\ty[...
globals: {'n': 1000}, tested 1e+06 times
time (s) speedup methods
0 0.666237 6.10x y = x.astype(x.dtype)
1 0.740594 5.49x y = np.empty_like(x); np.copyto(y, x)
2 0.755246 5.39x y = np.array(x)
3 1.043631 3.90x y = np.empty_like(x); y[:] = x
4 1.398793 2.91x y = 1*x
5 1.434299 2.84x y = np.empty_like(x); np.copyto(y, x, casting=...
6 1.544769 2.63x y = np.copy(x)
7 1.873119 2.17x y = np.empty(n, dtype=x.dtype); y[:] = x
8 2.355593 1.73x y = np.zeros(n, dtype=x.dtype); y[:] = x
9 4.067133 y = np.zeros_like(x); y[:] = x
globals: {'n': 6309}, tested 1e+06 times
time (s) speedup methods
0 2.338428 3.05x y = np.array(x)
1 2.466636 2.89x y = x.astype(x.dtype)
2 2.561535 2.78x y = np.empty_like(x); np.copyto(y, x)
3 2.603601 2.74x y = np.empty_like(x); y[:] = x
4 3.005610 2.37x y = np.empty_like(x); np.copyto(y, x, casting=...
5 3.215863 2.22x y = np.copy(x)
6 3.249763 2.19x y = 1*x
7 3.661599 1.95x y = np.empty(n, dtype=x.dtype); y[:] = x
8 6.344077 1.12x y = np.zeros(n, dtype=x.dtype); y[:] = x
9 7.133050 y = np.zeros_like(x); y[:] = x
globals: {'n': 10000}, tested 1e+06 times
time (s) speedup methods
0 3.421806 2.82x y = np.array(x)
1 3.569501 2.71x y = x.astype(x.dtype)
2 3.618747 2.67x y = np.empty_like(x); np.copyto(y, x)
3 3.708604 2.61x y = np.empty_like(x); y[:] = x
4 4.150505 2.33x y = np.empty_like(x); np.copyto(y, x, casting=...
5 4.402126 2.19x y = np.copy(x)
6 4.917966 1.96x y = np.empty(n, dtype=x.dtype); y[:] = x
7 4.941269 1.96x y = 1*x
8 8.925884 1.08x y = np.zeros(n, dtype=x.dtype); y[:] = x
9 9.661437 y = np.zeros_like(x); y[:] = x
globals: {'n': 100000}, tested 1e+05 times
time (s) speedup methods
0 3.858588 2.63x y = x.astype(x.dtype)
1 3.873989 2.62x y = np.array(x)
2 3.896584 2.60x y = np.empty_like(x); np.copyto(y, x)
3 3.919729 2.58x y = np.empty_like(x); np.copyto(y, x, casting=...
4 3.948563 2.57x y = np.empty_like(x); y[:] = x
5 4.000521 2.53x y = np.copy(x)
6 4.087255 2.48x y = np.empty(n, dtype=x.dtype); y[:] = x
7 4.803606 2.11x y = 1*x
8 6.723291 1.51x y = np.zeros_like(x); y[:] = x
9 10.131983 y = np.zeros(n, dtype=x.dtype); y[:] = x
globals: {'n': 1000000}, tested 3e+04 times
time (s) speedup methods
0 85.625484 1.24x y = np.empty_like(x); y[:] = x
1 85.693316 1.24x y = np.empty_like(x); np.copyto(y, x)
2 85.790064 1.24x y = np.empty_like(x); np.copyto(y, x, casting=...
3 86.342230 1.23x y = np.empty(n, dtype=x.dtype); y[:] = x
4 86.954862 1.22x y = np.zeros(n, dtype=x.dtype); y[:] = x
5 89.503368 1.18x y = np.array(x)
6 91.986177 1.15x y = 1*x
7 95.216021 1.11x y = np.copy(x)
8 100.524358 1.05x y = x.astype(x.dtype)
9 106.045746 y = np.zeros_like(x); y[:] = x
y = np.empty_like(x)
是设置的一部分。
globals: {'n': 100}, tested 1e+06 times
time (s) speedup methods
0 0.328492 2.33x np.copyto(y, x)
1 0.384043 1.99x y = np.array(x)
2 0.405529 1.89x y[:] = x
3 0.764625 np.copyto(y, x, casting='no')
globals: {'n': 1000}, tested 1e+06 times
time (s) speedup methods
0 0.453094 1.95x np.copyto(y, x)
1 0.537594 1.64x y[:] = x
2 0.770695 1.15x y = np.array(x)
3 0.884261 np.copyto(y, x, casting='no')
globals: {'n': 6309}, tested 1e+06 times
time (s) speedup methods
0 2.125426 1.20x np.copyto(y, x)
1 2.182111 1.17x y[:] = x
2 2.364018 1.08x y = np.array(x)
3 2.553323 np.copyto(y, x, casting='no')
globals: {'n': 10000}, tested 1e+06 times
time (s) speedup methods
0 3.196402 1.13x np.copyto(y, x)
1 3.523396 1.02x y[:] = x
2 3.531007 1.02x y = np.array(x)
3 3.597598 np.copyto(y, x, casting='no')
globals: {'n': 100000}, tested 1e+05 times
time (s) speedup methods
0 3.862123 1.01x np.copyto(y, x)
1 3.863693 1.01x y = np.array(x)
2 3.873194 1.01x y[:] = x
3 3.909018 np.copyto(y, x, casting='no')
x.copy()
和 np.array(x)
一样快,但我更喜欢前者的语法:$ python3 -m timeit -s "import numpy as np; x = np.random.random((100, 100))" "x.copy()"
- 100000 loops, best of 3: 4.7 usec per loop
。对于 np.array(x)
,我的测试结果也类似。在 Linux 系统下,使用 i5-4210U 处理器和 numpy 1.10.4 版本进行测试。 - Marco Sullanp.copy
更加宽容:np.copy(False)
,np.copy(None)
仍然有效,而a = None; a.copy()
会抛出AttributeError: 'NoneType' object has no attribute 'copy'
。此外,我们使用函数语法而不是方法语法更加精确地声明我们想要在这行代码中发生什么。 - mabnp.copy(None)
不会抛出错误,这真的很不符合 Python 的风格。更多使用 a.copy()
的理由 :) - Marco Sullay[:] = x
现在比copyto(y, x)
稍微快一点。代码和输出请参见https://gist.github.com/bhawkins/7cdbd5b9372cb798e34e21f92279d2dc。 - Brian Hawkins您可以轻松使用:
b = 1*a
这是最快的方法,但也存在一些问题。如果您未直接定义a
的dtype
,并且没有检查b
的dtype
,则可能会遇到麻烦。例如:
a = np.arange(10) # dtype = int64
b = 1*a # dtype = int64
a = np.arange(10.) # dtype = float64
b = 1*a # dtype = float64
a = np.arange(10) # dtype = int64
b = 1. * a # dtype = float64
我希望我能把问题解释清楚。有时候,只需要一个小操作就可以改变数据类型。
a = numpy.zeros(len(b))
或者a = numpy.empty(n,dtype=complex)
也会创建一个新数组。 - ahelm有很多不同的事情可以做:
a=np.copy(b)
a=np.array(b) # Does exactly the same as np.copy
a[:]=b # a needs to be preallocated
a=b[np.arange(b.shape[0])]
a=copy.deepcopy(b)
无法正常工作的事情
a=b
a=b[:] # This have given my code bugs
为什么不使用
a = 0 + b
我认为这很类似于先前的乘法,但可能更简单。
假设目标数组 a
已经存在,我可以想到三个选项(其中两个已经在其他答案中提到):
a[...] = b
a[:] = b
np.copyto(a, b)
我已经在连续数组的情况下对它们进行了测试。对于大型数组,它们都几乎同样快(因为时间由实际复制时间主导,在所有三个函数中都能高效完成)。对于小型数组,第一个函数似乎比第二个函数稍微快一些,而第二个函数又比第三个函数稍微快一些。就可读性而言,前两个函数大致相当(略微偏向第一个函数,因为它不会暗示目标矩阵的第一维度上有一个稍微令人困惑的迭代)。我发现最后一个函数不太易读,因为我往往会忘记它是 Intel (mov dst, src
) 还是 AT&T (mov src, dst
) 语法,除非使用命名参数 (np.copyto(dst=a, src=b)
),但这可能会有点啰嗦。并且 copyto
的目标参数在前,而 ufunc 的目标参数在后(例如,np.sin(b, a)
等价于 a[...] = np.sin(b)
,除了避免创建临时数组)。
a = b
只是创建了一个指向b
的新引用。a[:] = b
意味着“将a
中的所有元素设置为与b
相同”。这个区别很重要,因为 numpy 数组是可变类型。 - Brian Hawkinsempty()
比zeros()
快约10%。令人惊讶的是,empty_like()
甚至更快。copyto(a,b)
比数组语法a[:] = b
更快。请参见https://gist.github.com/bhawkins/5095558。 - Brian Hawkinsnp.copyto(a, b)
和何时使用a = b.astype(b.dtype)
以提高速度,请参见下面的答案:https://dev59.com/t2w15IYBdhLWcg3wu-I9#33672015 - mabb=a.copy()
会创建一个新的数组。我们需要修改一个已存在的对象,而不是创建一个新的对象。(原帖要求使用恒定内存地址。) - Brian Hawkins