Numpy在转置矩阵时如何移动数据?

时间:2019-01-04 12:43:41

标签: python numpy

似乎numpy.transpose仅保存步幅,并且实际上确实根据this进行了变位

那么,什么时候实际发生数据移动以及如何移动?使用许多memcpy吗?或其他技巧?

我遵循的路径: array_reshapePyArray_NewshapePyArray_NewCopyPyArray_NewLikeArrayPyArray_NewFromDescrPyArray_NewFromDescrAndBasePyArray_NewFromDescr_int 但对轴置换一无所知。确实何时发生?

2 个答案:

答案 0 :(得分:0)

您的问题的答案是:Numpy不会移动数据。

您在上述链接的第688行上看到PyArray_Transpose吗?此功能有一个替代品,

    n = permute->len;
    axes = permute->ptr;
    ...
    for (i = 0; i < n; i++) {
        int axis = axes[i];
        ...
        permutation[i] = axis;
}

任何数组形状都是纯粹的元数据,Numpy使用它来理解如何处理数据,因为内存始终是线性且连续存储的。因此,没有理由从文档here

移动或重新排序任何数据
  

其他操作(例如转置)不会移动数据元素   在数组中四处移动,而是更改有关形状和步幅的信息,以使数组的索引发生变化,但是中的数据不会移动。   通常,这些新版本的数组元数据和相同的数据缓冲区是   新的“视图”进入数据缓冲区。有一个不同的ndarray对象,但是它   使用相同的数据缓冲区。这就是为什么有必要强制复印通过   如果真的想制作一个新的独立文件,请使用.copy()方法   数据缓冲区的副本。

复制的唯一原因可能是最大化cache efficiency,尽管Numpy已经considers

  

事实证明,numpy在处理ufunc时足够聪明,可以确定哪个索引是内存中变化最快的索引并将其用于最内层循环。

答案 1 :(得分:0)

通过numpy C代码进行跟踪是一个缓慢而乏味的过程。我更喜欢根据时间来推断行为模式。

制作示例数组及其转置:

In [168]: A = np.random.rand(1000,1000)
In [169]: At = A.T

首先快速浏览-无需处理数据缓冲区:

In [171]: timeit B = A.ravel()
262 ns ± 4.39 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

快速复制(大概使用了一些快速的块内存应对方法):

In [172]: timeit B = A.copy()
2.2 ms ± 26.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

慢速复制(可能需要按照跨步顺序遍历源,并按照自己的顺序遍历目标):

In [173]: timeit B = A.copy(order='F')
6.29 ms ± 2.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

无需更改顺序即可复制At-快速:

In [174]: timeit B = At.copy(order='F')
2.23 ms ± 51.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

类似于[173],但从“ F”变为“ C”:

In [175]: timeit B = At.copy(order='C')
6.29 ms ± 4.16 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [176]: timeit B = At.ravel()
6.54 ms ± 214 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

具有更简单的跨步重新排序的副本介于两者之间:

In [177]: timeit B = A[::-1,::-1].copy()
3.75 ms ± 4.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [178]: timeit B = A[::-1].copy()
3.73 ms ± 6.48 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [179]: timeit B = At[::-1].copy(order='K')
3.98 ms ± 212 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

astype还需要较慢的副本:

In [182]: timeit B = A.astype('float128')
6.7 ms ± 8.12 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

PyArray_NewFromDescr_int被描述为Generic new array creation routine.虽然我不知道它将数据从源复制到目标的位置,但显然是在检查orderstrides以及dtype。大概可以处理需要通用副本的所有情况。轴排列不是特殊情况。