2014-12-01 2 views
2

Я работаю с 4-мерными матрицами с использованием numpy 1.9 и python 2.7.5.Вставка невыровненных элементов в 4-мерных матрицах в numpy

Я работаю с 3 NumPy массива A, B, C (это являются те, массив semplify):

>>> A = np.ones((2,2,4,3)); 
>>> B = np.ones((2,2,4,3))*-1; B[0,0,1,:] = [10,11,12]; B[0,1,1,:] = [13,14,15]; B[1,0,1,:] = [16,17,18]; B[1,1,1,:] = [19,20,21]; 
>>> C = [[1,1],[1,1]] 

Я хотел бы вставки в A все элементы из B, согласно C ,
Пример:

  • c[0,0] = [1] => After A[0, 0, 1, :] has to be inserted B[0, 0, 1, :]
  • c[0,1] = [1] => After A[0, 1, 1, :] has to be inserted B[0, 1, 1, :]
  • c[1,0] = [1] => After A[1, 0, 1, :] has to be inserted B[1, 0, 1, :]
  • c[1,1] = [1] => After A[1, 1, 1, :] has to be inserted B[1, 1, 1, :]

я задал тот же самый вопрос here для 3D-массив, но я не могу получить те же результаты для 4D массива (I 'm совершенно новый с numpy).

Любая идея?

Спасибо!

+0

Что не так с вашим решением для петли в 3D-корпусе? Конечно, вы можете это расширить. – hpaulj

+0

Несомненно, это сработает, но цикл for намного медленнее, чем чистое решение numpy. Мне нужно решение как можно быстрее. – Rowandish

+0

Вы сделали тайминги с 3D-вставкой? С этими небольшими массивами образцов он медленнее. С какими размерами вы работаете. Самое главное, сколько терминов в 'C'? – hpaulj

ответ

1

Вы можете изменить форму ваших массивов в 3D, и использовать решение (ы) из предыдущего вопроса:

def fn_4d(A,B,C, fn): 
    shape = list(A.shape) 
    A = A.reshape(-1,A.shape[-2], A.shape[-1]) 
    B = B.reshape(-1,B.shape[-2], B.shape[-1]) 
    C = np.array(C).reshape(-1) 
    A2 = fn(A,B,C) 
    shape[-2] += 1 
    A2 = A2.reshape(shape) 
    return A2 

Если итерационное решение

def iter_3d(A,B,C): 
    A_2 = np.zeros((A.shape[0], A.shape[1] + 1, A.shape[2])) 
    for j in xrange(np.size(C, 0)): 
     i = C[j] 
     A_2[j, :, :] = np.concatenate((A[j, 0:i + 1, :], [B[j, i, :]], A[j, i + 1:, :])) 
    return A_2 

fn_4d(A, B, C, iter_3d) 

Эта перестройка довольно общая, работает на все, начиная с первоначального 3D-размера o n до тех пор, пока вставка находится в размере -2.

Альтернативой ваш concatenate будет:

A_2[j,:,:] = np.insert(A[j,...], i+1, B[j,i,:], axis=0) 

но копаться в insert, показывает, что она по сути дела:

A_2[j,:i+1,:] = A[j,:i+1,:] 
    A_2[j,i+1,:] = B[j,i,:] 
    A_2[j,i+2:,:] = A[j,i+1:,:] 

Задержки примерно одинаковы для этих альтернатив.

3-я решение, которое вставляет несколько строк в то время, https://stackoverflow.com/a/26252542/901925, использует, в сущности, булева маска индексация:

def insert_3d2(A,B,C): 
    mi = np.ravel_multi_index([np.arange(A.shape[0]), C], A.shape[:2]) 
    bvals = np.take(B.reshape(-1, B.shape[-1]), mi, axis=0) 
    # result = np.insert(A.reshape(-1, A.shape[2]), mi + 1, bvals, axis=0) 
    # which does: 

    mi += np.arange(len(mi)) 
    A1 = A.reshape(-1, A.shape[2]) 
    shape = list(A1.shape) 
    shape[0] += len(mi) 
    A2 = np.empty(shape,dtype=A1.dtype) 
    mask = np.ones(shape[0], dtype=bool) 
    mask[mi] = False 
    A2[mask,:] = A1 
    A2[mi,:] = bvals 

    A2 = A2.reshape(A.shape[0], -1, A.shape[2]) 
    return A2 

То есть, он создает булеву маску, чтобы определить, где поместить строки из A в новом пустом массиве. По-видимому, есть небольшое преимущество в скорости для индексирования по прерывистым блокам.

Вот пересмотр подхода маски, без каких-либо уплощений. Он последовательно быстрее, чем любая другая версия.Я сохранил итерацию более C, потому что это самый ясный способ взглянуть на этот параметр. Замена цикла enumerate (C) векторными операциями, аналогичными тем, которые используются для mi и bvals, дает небольшую экономию времени.

def insert_3d3(A,B,C): 
    # without flattening 
    shape = list(A.shape) 
    shape[1] += 1 
    A2 = np.empty(shape, dtype=A.dtype) 
    mask = np.ones(shape, dtype=bool) 
    for j,i in enumerate(C): 
     mask[j,i+1,:] = False 
     A2[j,i+1,:] = B[j,i,:] 
    A2[mask] = A.ravel() 
    return A2 
0

Fancy indexing на помощь:

>>> C = np.asarray(C) 
>>> D = np.ones((2,2,5,3)) 
>>> idx = np.arange(len(C)) 
>>> D[idx, C[:, 0], C[:, 1]+1] = B[idx, C[:, 0], C[:, 1]] 
>>> D 
array([[[[ 1., 1., 1.], 
     [ 1., 1., 1.], 
     [ 1., 1., 1.], 
     [ 1., 1., 1.], 
     [ 1., 1., 1.]], 

     [[ 1., 1., 1.], 
     [ 1., 1., 1.], 
     [ 1., 1., 1.], 
     [ 102., 103., 104.], 
     [ 1., 1., 1.]]], 


     [[[ 1., 1., 1.], 
     [ 1., 1., 1.], 
     [ 96., 97., 98.], 
     [ 1., 1., 1.], 
     [ 1., 1., 1.]], 

     [[ 1., 1., 1.], 
     [ 1., 1., 1.], 
     [ 1., 1., 1.], 
     [ 1., 1., 1.], 
     [ 1., 1., 1.]]]]) 
+0

Я думаю, что вы неправильно поняли, матрицы, о которых я говорю, являются примерами (теперь я отредактировал свой вопрос, чтобы уточнить его), я хотел бы вставить A всех элементов из B , согласно C, но A является общей матрицей, а не одной матрицей. – Rowandish

+0

Хорошо, тогда я боюсь, что ваша проблема серьезно заболела ... Чтобы иметь возможность вставлять эти записи, вам нужно расширить форму 'A' на' 1' в третьем измерении. В 2D-срезах 'A [0, 1]' и 'A [1, 0]' это нормально, потому что нам есть что-то вставить. Но что следует вставлять в срезы 'A [0, 0]' и 'A [1, 1]'? – Jaime

+0

Извините, вы абсолютно правы. Теперь я исправляю свой вопрос – Rowandish