Вы можете изменить форму ваших массивов в 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
Что не так с вашим решением для петли в 3D-корпусе? Конечно, вы можете это расширить. – hpaulj
Несомненно, это сработает, но цикл for намного медленнее, чем чистое решение numpy. Мне нужно решение как можно быстрее. – Rowandish
Вы сделали тайминги с 3D-вставкой? С этими небольшими массивами образцов он медленнее. С какими размерами вы работаете. Самое главное, сколько терминов в 'C'? – hpaulj