"Индикаторная" функция для элементов массива

Я сразу с примера того, что мне нужно.

Можно ли без циклов и map сделать из массива a = [3, 1, 3] два таких массива [[3, 0, 3], [0, 1, 0]]? Т. е. новый массив имеет размер (n, len(a)), где n - это число различных элементов в a. При этом строчки нового массива образуются так: одинаковые элементы остаются на своих местах, а на остальных местах взамен старых ставятся нули. Надеюсь понятно объяснил.

Например:
на входе массив: a = [3, 1, 2, 1, 3] ожидаемый результат: [[3, 0, 0, 0, 3], [0, 1, 0, 1, 0], [0, 0, 2, 0, 0]].

Можно ли методами векторизации решить эту задачу?

Порядок получившихся строк не имеет значения. У меня практически нет идей. Допустим, я создал n нужных пустых массивов (допустим они состоят из нулей). А как дальше? Подскажите, пожалуйста.


Ответы (2 шт):

Автор решения: MaxU

Полувекторизированное решение - цикл пробегает по уникальным значениям:

import numpy as np
import pandas as pd

a = np.array([3, 1, 2, 1, 3])
unq = pd.unique(a)
res = np.zeros((len(unq), len(a)), dtype="int32")

for i,n in enumerate(unq):
    res[i][a == n] = n

результат:

In [265]: res
Out[265]:
array([[3, 0, 0, 0, 3],
       [0, 1, 0, 1, 0],
       [0, 0, 2, 0, 0]], dtype=int32)
→ Ссылка
Автор решения: Alex Alex

Без циклов for:

import numpy as np
a = np.array([3, 1, 2, 1, 3])
n_a=a.shape[0] # число элементов в исходном массиве 
unq=np.unique(a) 
n_unq=unq.shape[0] # число уникальных элементов
b= np.tile(a, (n_unq, 1))
c=np.tile(unq, (n_a, 1)).T
print(c*(b==c))

или еще вариант:

import numpy as np
a = np.array([3, 1, 2, 1, 3])
unq=np.unique(a) 
M=np.meshgrid(a, unq)
print(np.where(M[0]==M[1], M[0], 0))

Если массив большой, то, возможно, стоит использовать разреженные матрицы.

→ Ссылка