#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:
Попробуйте работать с маскированными массивами. Более подробная информация в документации —
- Сначала создайте маску с
np.ones_like
и вычитанием транслируемогоrelCols
, добавив к нему ось. - Затем создайте замаскированный массив из
dataArr
- Используйте
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. Спасибо вам обоим. Я добавил, как эффективно работает мой исходный код, чтобы обеспечить некоторый сравнительный анализ