Чередование массивов NumPy с несовпадающими формами - PullRequest
5 голосов
/ 05 июня 2019

Я бы хотел чередовать несколько массивов с разными размерами вдоль определенной оси.В частности, у меня есть список массивов формы (_, *dims), изменяющихся вдоль первой оси, которые я хотел бы чередовать, чтобы получить другой массив формы (_, *dims).Например, при вводе

a1 = np.array([[11,12], [41,42]])
a2 = np.array([[21,22], [51,52], [71,72], [91,92], [101,102]])
a3 = np.array([[31,32], [61,62], [81,82]])

interweave(a1,a2,a3)

желаемый результат будет

np.array([[11,12], [21,22], [31,32], [41,42], [51,52], [61,62], [71,72], [81,82], [91,92], [101,102]]

С помощью предыдущих сообщений (таких как Numpy concatenate массивов с чередованием )Я получил эту работу, когда массивы совпадают по первому измерению:

import numpy as np

def interweave(*arrays, stack_axis=0, weave_axis=1):
    final_shape = list(arrays[0].shape)
    final_shape[stack_axis] = -1

    # stack up arrays along the "weave axis", then reshape back to desired shape
    return np.concatenate(arrays, axis=weave_axis).reshape(final_shape)

К сожалению, если входные фигуры не совпадают по первому измерению, вышеприведенное выдает исключение, так как мы должны объединить по другой осичем несоответствующий.В самом деле, я не вижу никакого способа эффективно использовать конкатенацию здесь, поскольку конкатенация вдоль несоответствующей оси уничтожит информацию, необходимую для получения желаемого результата.

Еще одна идея, которая у меня была, заключалась в том, чтобы заполнить входные массивыпустые записи, пока их формы не совпадут по первому измерению, а затем удалите пустые записи в конце дня.Хотя это сработало бы, я не уверен, как лучше всего это реализовать, и, похоже, в первую очередь это не нужно.

Ответы [ 3 ]

3 голосов
/ 05 июня 2019

Вот подход, основанный главным образом на NumPy, использующий также zip_longest для перемежения массивов со значением заполнения:

def interleave(*a):
    # zip_longest filling values with as many NaNs as
    # values in second axis
    l = *zip_longest(*a, fillvalue=[np.nan]*a[0].shape[1]),
    # build a 2d array from the list
    out = np.concatenate(l)
    # return non-NaN values
    return out[~np.isnan(out[:,0])]

a1 = np.array([[11,12], [41,42]])
a2 = np.array([[21,22], [51,52], [71,72], [91,92], [101,102]])
a3 = np.array([[31,32], [61,62], [81,82]])

interleave(a1,a2,a3)

array([[ 11.,  12.],
       [ 21.,  22.],
       [ 31.,  32.],
       [ 41.,  42.],
       [ 51.,  52.],
       [ 61.,  62.],
       [ 71.,  72.],
       [ 81.,  82.],
       [ 91.,  92.],
       [101., 102.]])
2 голосов
/ 05 июня 2019

Вы, вероятно, ищете np.choose. С правильно построенным индексом вы можете сделать результат за один вызов:

def interweave(*arrays, axis=0):
    arrays = [np.moveaxis(a, axis, 0) for a in arrays]
    m = len(arrays)
    n = max(map(len, arrays))
    index = [k for i, k in (divmod(x, m) for x in range(m * n)) if i < len(arrays[k])]
    return np.moveaxis(np.choose(index, arrays), 0, axis)

range(m * n) - размер выходного пространства, если все массивы были одинакового размера. divmod вычисляет элемент чередования и массив, из которого он выбирается. Элементы, которые отсутствуют из-за слишком короткого массива, пропускаются, поэтому в результате выбираются только допустимые элементы из массивов.

Возможно, есть лучшие способы создания индекса, но это работает в качестве примера. Вы должны переместить ось стека в первую позицию, поскольку choose занимает первую ось.

0 голосов
/ 05 июня 2019

Я продолжил и обобщил ответ Яту на ситуацию, с которой я сталкиваюсь на практике, когда число измерений произвольно.Вот что у меня есть:

import numpy as np
from itertools import zip_longest

def interleave(*a):
    #creating padding array of NaNs
    fill_shape = a[0].shape[1:]
    fill_array = np.full(fill_shape,np.nan)

    l = *zip_longest(*a, fillvalue=fill_array),
    # build a 2d array from the list
    out = np.concatenate(l)
    # return non-NaN values
    tup = (0,)*(len(out.shape)-1)
    return out[~np.isnan(out[(...,)+tup])]

Тестирование:

b1 = np.array(
        [
                [[111,112,113],[121,122,123]],
                [[411,412,413],[421,422,423]]
        ])
b2=np.array(
        [
                [[211,212,213],[221,222,223]],
                [[511,512,513],[521,522,523]],
                [[711,712,713],[721,722,712]],
                [[911,912,913],[921,922,923]],
                [[1011,1012,1013],[1021,1022,1023]]
        ])
b3=np.array(
        [
                [[311,312,313],[321,322,323]],
                [[611,612,613],[621,622,623]],
                [[811,812,813],[821,822,823]]
        ])

In [1]: interleave(b1,b2,b3)
Out [1]: [[[ 111.  112.  113.]
  [ 121.  122.  123.]]

 [[ 211.  212.  213.]
  [ 221.  222.  223.]]

 [[ 311.  312.  313.]
  [ 321.  322.  323.]]

 [[ 411.  412.  413.]
  [ 421.  422.  423.]]

 [[ 511.  512.  513.]
  [ 521.  522.  523.]]

 [[ 611.  612.  613.]
  [ 621.  622.  623.]]

 [[ 711.  712.  713.]
  [ 721.  722.  712.]]

 [[ 811.  812.  813.]
  [ 821.  822.  823.]]

 [[ 911.  912.  913.]
  [ 921.  922.  923.]]

 [[1011. 1012. 1013.]
  [1021. 1022. 1023.]]]

Любые предложения приветствуются!В частности, в моем приложении ограничивающим фактором является пространство, а не время, поэтому мне интересно, есть ли способ сделать это, используя значительно меньше памяти (наборы данных велики вдоль оси слияния).

...