从给定的numpy数组创建块对角numpy数组

时间:2015-11-03 20:20:58

标签: python arrays numpy

我有一个二维numpy数组,列数和行数相同。我想把它们安排成一个更大的阵列,在对角线上有较小的阵列。应该可以指定起始矩阵在对角线上的频率。例如:

a = numpy.array([[5, 7], 
                 [6, 3]])

因此,如果我想在对角线上使用此数组2次,则所需的输出将为:

array([[5, 7, 0, 0], 
       [6, 3, 0, 0], 
       [0, 0, 5, 7], 
       [0, 0, 6, 3]])

3次:

array([[5, 7, 0, 0, 0, 0], 
       [6, 3, 0, 0, 0, 0], 
       [0, 0, 5, 7, 0, 0], 
       [0, 0, 6, 3, 0, 0],
       [0, 0, 0, 0, 5, 7],
       [0, 0, 0, 0, 6, 3]])

使用numpy方法和起始数组的任意大小(仍然认为起始数组具有相同数量的行和列)是否有一种快速实现方法?

3 个答案:

答案 0 :(得分:13)

numpy.kron的经典案例 -

np.kron(np.eye(n), a)

示例运行 -

In [57]: n = 2

In [58]: np.kron(np.eye(n), a)
Out[58]: 
array([[ 5.,  7.,  0.,  0.],
       [ 6.,  3.,  0.,  0.],
       [ 0.,  0.,  5.,  7.],
       [ 0.,  0.,  6.,  3.]])

In [59]: n = 3

In [60]: np.kron(np.eye(n), a)
Out[60]: 
array([[ 5.,  7.,  0.,  0.,  0.,  0.],
       [ 6.,  3.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  5.,  7.,  0.,  0.],
       [ 0.,  0.,  6.,  3.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  5.,  7.],
       [ 0.,  0.,  0.,  0.,  6.,  3.]])

答案 1 :(得分:3)

private void addNewMarker(POI placeInfo){
        mMap.setInfoWindowAdapter(new CustomInfoWindow(MapsActivity.this,placeInfo.getPhotos()));

        try {
            String snippet = "ID: " + placeInfo.getID() + "\nCategory: "+ placeInfo.getCategory();
            mMarker = mMap.addMarker(new MarkerOptions()
                    .title(placeInfo.getPOI_name())
                    .position(new LatLng(placeInfo.getLatitude(),placeInfo.getLongitude()))
                    .snippet(snippet));
        }catch (NullPointerException e){
            Log.e(TAG, "moveCamera: NullPointerException thrown: " + e.getMessage() );
        }

    }

但看起来np.kron解决方案对于小n来说要快一点。

import numpy as np
from scipy.linalg import block_diag

a = np.array([[5, 7], 
              [6, 3]])

n = 3

d = block_diag(*([a] * n))

d

array([[5, 7, 0, 0, 0, 0],
       [6, 3, 0, 0, 0, 0],
       [0, 0, 5, 7, 0, 0],
       [0, 0, 6, 3, 0, 0],
       [0, 0, 0, 0, 5, 7],
       [0, 0, 0, 0, 6, 3]], dtype=int32)

然而,对于n = 300,例如,block_diag要快得多:

%timeit np.kron(np.eye(n), a)
12.4 µs ± 95.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit block_diag(*([a] * n))
19.2 µs ± 34.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

答案 2 :(得分:3)

对于矩阵的特殊情况,简单的切片比DataGrid(最慢的)要快得多,并且大多与基于StructureID Target Val Measured Val 'A' 2 1.8 'A' 2 1.5 'B' 4 4.6 'C' 1 2.5 'C' 1 2.3 'C' 1 2.2 . . . . . . 的方法(来自@Divakar答案)相当。 与 StructureID Correct Target Val 'A' 1.5 'B' 3.0 'C' 2.2 'D' 1.0 . . . . 相比,它在较小的numpy.kron()上表现更好,在某种程度上与块重复次数无关。

请注意,使用Numba可以轻松改善numpy.einsum()在较小输入上的性能,但是对于较大输入,则性能会变差。

使用Numba,如果重复次数很少,则完全显式循环和并行化(scipy.linalg.block_diag())会再次获得与arr类似的结果。

总体而言,性能最高的解决方案是block_diag_view()block_diag_loop_jit()

block_diag_einsum()

block_diag_einsum()的基准:

bm_4

block_diag_view()的基准:

bm_400


使用以下附加代码从this template生成了图:

import numpy as np
import scipy as sp
import numba as nb

import scipy.linalg


NUM = 4
M = 9


def block_diag_kron(arr, num=NUM):
    return np.kron(np.eye(num), arr)


def block_diag_einsum(arr, num=NUM):
    rows, cols = arr.shape
    result = np.zeros((num, rows, num, cols), dtype=arr.dtype)
    diag = np.einsum('ijik->ijk', result)
    diag[:] = arr
    return result.reshape(rows * num, cols * num)


def block_diag_scipy(arr, num=NUM):
    return sp.linalg.block_diag(*([arr] * num))


def block_diag_view(arr, num=NUM):
    rows, cols = arr.shape
    result = np.zeros((num * rows, num * cols), dtype=arr.dtype)
    for k in range(num):
        result[k * rows:(k + 1) * rows, k * cols:(k + 1) * cols] = arr
    return result


@nb.jit
def block_diag_view_jit(arr, num=NUM):
    rows, cols = arr.shape
    result = np.zeros((num * rows, num * cols), dtype=arr.dtype)
    for k in range(num):
        result[k * rows:(k + 1) * rows, k * cols:(k + 1) * cols] = arr
    return result


@nb.jit(parallel=True)
def block_diag_loop_jit(arr, num=NUM):
    rows, cols = arr.shape
    result = np.zeros((num * rows, num * cols), dtype=arr.dtype)
    for k in nb.prange(num):
        for i in nb.prange(rows):
            for j in nb.prange(cols):
                result[i + (rows * k), j + (cols * k)] = arr[i, j]
    return result

(已编辑,包括基于NUM = 4的方法和具有显式循环的另一个Numba版本。)