如何从一个Numpy数组复制数据到另一个数组?

99

如何在不改变数组a的地址的情况下,最快地将数据从数组b复制到数组a?我需要这样做是因为一个外部库(PyFFTW)正在使用指向我的数组的指针,而且这个指针不能更改。

例如:

a = numpy.empty(n, dtype=complex)
for i in xrange(a.size):
  a[i] = b[i]

不使用循环是否有可能实现这个目标?

8个回答

94

我相信

a = numpy.empty_like(b)
a[:] = b

会快速地复制这些值。如Funsi所提到的,最新版本的numpy也有copyto函数。


4
但是,相较于 numpy.zerosnumpy.empty 会更快吗? - mg007
11
a = b 只是创建了一个指向 b 的新引用。a[:] = b 意味着“将 a 中的所有元素设置为与 b 相同”。这个区别很重要,因为 numpy 数组是可变类型。 - Brian Hawkins
17
@mg007 我进行了一些测试,结果显示empty()zeros()快约10%。令人惊讶的是,empty_like()甚至更快。copyto(a,b)比数组语法a[:] = b更快。请参见https://gist.github.com/bhawkins/5095558。 - Brian Hawkins
2
@Brian Hawkins是正确的。关于何时使用np.copyto(a, b)和何时使用a = b.astype(b.dtype)以提高速度,请参见下面的答案:https://dev59.com/t2w15IYBdhLWcg3wu-I9#33672015 - mab
3
@MarcoSulla 的代码 b=a.copy() 会创建一个新的数组。我们需要修改一个已存在的对象,而不是创建一个新的对象。(原帖要求使用恒定内存地址。) - Brian Hawkins
显示剩余10条评论

31

这对我不起作用。我得到了“AttributeError:'module'对象没有属性'copyto'” - kalu

23
a = numpy.array(b)

速度比numpy v1.6及以下版本中建议的解决方案都要快,并且也会复制数组。然而,我无法将其与copyto(a,b)进行测试,因为我没有最新版本的numpy。


1
这是一个复制数组的好方法,但它会创建一个新对象。OP需要知道如何快速为已经创建的数组分配值。 - Brian Hawkins

18
为了回答您的问题,我尝试了一些变体并对它们进行了分析。
结论:在可能的情况下,请使用内置的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 Sulla
是的Marco,这更多是个人口味问题。但请注意,np.copy更加宽容:np.copy(False)np.copy(None)仍然有效,而a = None; a.copy()会抛出AttributeError: 'NoneType' object has no attribute 'copy'。此外,我们使用函数语法而不是方法语法更加精确地声明我们想要在这行代码中发生什么。 - mab
1
事实上,np.copy(None) 不会抛出错误,这真的很不符合 Python 的风格。更多使用 a.copy() 的理由 :) - Marco Sulla
1
我刚刚使用Python 2.7.12和NumPy 1.11.2运行了这些基准测试,并发现y[:] = x现在比copyto(y, x)稍微快一点。代码和输出请参见https://gist.github.com/bhawkins/7cdbd5b9372cb798e34e21f92279d2dc。 - Brian Hawkins

10

您可以轻松使用:

b = 1*a

这是最快的方法,但也存在一些问题。如果您未直接定义adtype ,并且没有检查bdtype,则可能会遇到麻烦。例如:

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

我希望我能把问题解释清楚。有时候,只需要一个小操作就可以改变数据类型。


1
不会改变原数组。这相当于 b = a.copy()。 - Charles Brunet
抱歉,我不理解你的意思。您说的“创建一个新数组”是什么意思?这里介绍的所有其他方法都有相同的功能。a = numpy.zeros(len(b))或者a = numpy.empty(n,dtype=complex)也会创建一个新数组。 - ahelm
2
假设你有 a = numpy.empty(1000)。现在,你需要填充 a 的数据,而不改变其在内存中的地址。如果你执行 a[0] = 1,你不会重新创建一个数组,你只是改变了数组的内容。 - Charles Brunet
1
@CharlesBrunet 数组必须在某个时刻被创建。这个巧妙的一行代码可以在一个操作中完成所有工作。 - heltonbiker

8

有很多不同的事情可以做:

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 

3

为什么不使用

a = 0 + b

我认为这很类似于先前的乘法,但可能更简单。


1

假设目标数组 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),除了避免创建临时数组)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接