Python/Numpy: Выполните np.все эквивалентно разным столбцам для каждой строки

#python #numpy #multidimensional-array #boolean

Вопрос:

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

Массив для выполнения операции dataArr представляет собой трехмерный логический массив numpy формы (5, 3, 5).

 dataArr = np.array([  
                    [[1, 1, 1, 0, 1], [0, 0, 0, 0, 0], [1, 1, 1, 1, 0]],
                    [[0, 0, 0, 0, 0], [1, 0, 1, 1, 1], [1, 0, 1, 1, 1]],
                    [[1, 0, 1, 1, 1], [1, 1, 1, 0, 1], [0, 0, 0, 0, 0]],
                    [[1, 1, 1, 0, 1], [0, 0, 0, 0, 0], [1, 0, 1, 1, 1]], 
                    [[0, 0, 0, 0, 0], [1, 0, 1, 0, 1], [0, 1, 1, 1, 0]]
])
 

Массив , содержащий данные, идентифицирующие столбцы для включения в np.all операцию (или эквивалент) каждой строки, dataArr называется relCols 2-d логическим массивом numpy формы (5, 3) с тем же количеством строк и столбцов, dataArr что и . Значения в каждой строке указывают, к каким столбцам в соответствующей строке dataArr следует применить np.all .

 relCols = np.array([
                    [1, 0, 1],      #apply equivalent of np.all to dataArr[0,(0,2)]
                    [0, 1, 1],      #apply equivalent of np.all to dataArr[1,(1,2)]
                    [1, 1, 0],      #apply equivalent of np.all to dataArr[2,(0,1)]
                    [1, 0, 1],      #apply equivalent of np.all to dataArr[3,(0,2)]
                    [0, 1, 1]       #apply equivalent of np.all to dataArr[4,(1,2)]
]) 
 

Это упрощенная версия моего приложения, где dataArr НАМНОГО больше, поэтому я ищу полностью векторизованный подход, если это возможно.

В настоящее время я использую np.ones для нерелевантных столбцов в dataArr и использую np.all(dataArr, axis=1) . Хотя это работает, это добавляет значительные ненужные затраты памяти, которые я пытаюсь уменьшить.

Желаемый результат:

 result = np.array([  
                    [1, 1, 1, 0, 0],
                    [1, 0, 1, 1, 1],
                    [1, 0, 1, 0, 1],
                    [1, 0, 1, 0, 1],
                    [0, 0, 1, 0, 0]
])
 

Мы будем очень признательны за любую помощь.

Вот несколько примеров кода, демонстрирующих мой оригинальный подход. Размер dataArr был изменен, чтобы имитировать то, с чем я могу столкнуться. np.ones явно не используется ниже, но не относящиеся к делу столбцы, содержащие нули, были заменены единицами.

 def original():

    dataArr = np.array([  
                        [[1, 1, 0, 1, 1, 1, 0, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 1, 1, 1, 0, 0, 1, 0, 1, 1]],
                        [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 1, 0, 1, 1, 1, 0, 1, 1], [1, 0, 1, 0, 1, 1, 1, 0, 1, 1]],
                        [[1, 0, 1, 1, 1, 1, 0, 1, 1, 0], [1, 1, 1, 0, 1, 1, 0, 0, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]],
                        [[1, 1, 1, 0, 1, 0, 1, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 0, 1, 0, 1, 0, 1, 1, 1]], 
                        [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 1, 0, 1, 0, 1, 1, 0, 1], [0, 1, 1, 0, 1, 0, 1, 0, 1, 1]]
    ], dtype=bool)

    dataArr = np.repeat(dataArr,200000, axis=0); 
    dataArr = np.repeat(dataArr,100, axis=2);   #resulting shape = (1000000, 3, 1000)

    dataReduced = np.all(dataArr, axis=1)
 

Комментарии:

1. Я изо всех сил пытаюсь понять ваши выводы. Как вы получаете (5, 5) массив? Можете ли вы объяснить, как вы оказались [1, 1, 1, 0, 0] в первом ряду? Я читаю dataArr[0, (0, 2)] как первый вложенный массив dataArr , с np.all() колонками 0 и 2. Но это просто дало бы два значения, оба равны нулю, если оценивать по оси 1, но только одно значение, если оценивать нормально.

2. Если вы замените все столбцы, содержащие [0, 0, 0, 0, 0] [1, 1, 1, 1, 1] и выполненные np.all(dataArr, axis=1) , вы получите (5, 5) массив, показанный в моем результате. Я пытаюсь воспроизвести это, не устанавливая не относящиеся к делу столбцы на единицы. [1, 1, 1, 0, 0] является результатом оценки того, все ли элементы вдоль оси 0 в столбцах 0 и 2 являются истинными. Итак, это где [1, 1, 1, 0, 1] (строка 0, строка 0) и [1, 1, 1, 1, 0] (строка 0, строка 2) оба истинны.

Ответ №1:

Попробуйте работать с маскированными массивами. Более подробная информация в документации

  1. Сначала создайте маску с np.ones_like и вычитанием транслируемого relCols , добавив к нему ось.
  2. Затем создайте замаскированный массив из dataArr
  3. Используйте np.all over axis=1, который вычисляет операцию с немаскированными значениями и игнорирует замаскированные значения.
 mask = np.ones_like(dataArr) - relCols[...,None]
maskedArr = np.ma.MaskedArray(dataArr, mask)
np.all(maskedArr, axis=1).astype(int).data
 
 array([[1, 1, 1, 0, 0],
       [1, 0, 1, 1, 1],
       [1, 0, 1, 0, 1],
       [1, 0, 1, 0, 1],
       [0, 0, 1, 0, 0]])
 

Комментарии:

1. Спасибо, я проверю это и посмотрю, не требует ли это меньше памяти, чем мой первоначальный подход к использованию np.ones для несущественных столбцов

2. было бы здорово, если бы вы могли добавить свой первоначальный подход с полным кодом, чтобы я также мог провести сравнительный анализ. Спасибо!

3. @CraigNathan, по крайней мере, используйте "uint8" или "int8" для вашего массива!

4. Это отличный момент @ddejohn, особенно если основной проблемой здесь является память.

5. Спасибо вам обоим. Я добавил, как эффективно работает мой исходный код, чтобы обеспечить некоторый сравнительный анализ