Вот в чем проблема:
Для массива a numpy, содержащего n элементов, обозначим через b набор его уникальных значений в порядке возрастания, обозначим через m размер массива b. Вам нужно создать массив numpy с размерами n × m, в каждой строке которого должно быть значение 1 в случае, если оно равно значению данного индекса массива b, в других местах оно должно быть 0.
import numpy as np
def convert(a):
b = np.unique(sorted(a))
result = []
for i in a:
result.append((b == i) * 1)
return np.array(result)
a = np.array([1, 1, 2, 3, 2, 4, 5, 2, 3, 4, 5, 1, 1])
b = np.unique(sorted(a))
print(convert(a))
Это мое решение. есть ли какие-то улучшения, которые я могу сделать? Я не уверен в том, что объявить обычный список для результата, а затем преобразовать его в np.array.
2 ответа
Удалять sorted
Из документов: numpy.unique возвращает отсортированные уникальные элементы массива.
Вы можете просто удалить вызов sorted:
b = np.unique(sorted(a))
# produces the same result as
b = np.unique(a)
Понимание списка
В большинстве случаев вы можете и должны избегать такой схемы создания списков:
result = []
for i in a:
result.append((b == i) * 1)
Его можно заменить кратким пониманием списка и напрямую передать в np.array:
result = np.array([(b == i) * 1 for i in a])
# or directly return it (if applicable)
return np.array([(b == i) * 1 for i in a])
Понимание списков более питонично и часто быстрее. Как правило, не изменяя list объект также менее подвержен ошибкам.
Может быть лучший способ нанести на карту lambda x: (uniques == x) * 1 над входным массивом a. Вот обсуждение темы на StackOverflow: Самый эффективный способ сопоставить функцию с массивом numpy. Похоже на использование np.vectorize следует избегать по соображениям производительности.
С использованием map может быть похоже на понимание списка с точки зрения производительности (я сделал нет правильно протестируйте производительность здесь):
def convert_listcomp(a):
uniques = np.unique(a)
return np.array([(b == i) * 1 for i in a])
def convert_map(a):
uniques = np.unique(a)
return np.array(list(map(lambda x: (uniques == x) * 1, a)))
Всегда есть однострочный вариант, предполагающий a это вектор-строка (1d массив):
(a.reshape((-1, 1)) == np.unique(a)).astype(int)
Это работает путем трансляции == операция. Посмотрим, как это сделать.
Когда вы спрашиваете numpy чтобы применить операцию, сначала он проверяет, совместимы ли размеры. В противном случае возникает исключение. Например, попробуйте ввести np.ones(3) - np.ones(4) в переводчике. После нажатия Enter вы должны увидеть сообщение
ValueError: operands could not be broadcast together with shapes (3,) (4,).
Это происходит потому, что 3 != 4. Ага. Но это еще не все.
Пытаться a.reshape((-1, 1)) == np.unique(a). Несмотря на n!=m обычно держится, numpy с радостью вычисляет матрицу формы (n, m). Почему?
Это магия вещание:
При работе с двумя массивами NumPy сравнивает их формы поэлементно. Он начинается с конечных (то есть крайних правых) размеров и продолжается влево. Два измерения совместимы, когда
они равны, или
один из них 1
Если эти условия не выполняются, генерируется исключение ValueError: операнды не могут быть переданы вместе, указывая, что массивы имеют несовместимые формы. Размер результирующего массива — это размер, который не равен 1 по каждой оси входных данных.
Как здесь применяется это правило? Ну форма x = a.reshape((-1, 1)) является (n, 1), форма y = np.unique(a) является (1, m), поэтому вторая точка сверху остается в силе. Поэтому numpy расширяется x от формы (n, 1) к xx формы (n, m) путем «копирования» (насколько мне известно, копирования не происходит) его значения по второй оси, т.е. соблюдая правило
xx[j, k] = x[j] for all j=1..n, k=1..m.
По аналогии, y расширяется от формы (1, m) к yy формы (n, m) уважая
yy[j, k] = y[k] for all j=1..n, k=1..m
и операция применяется к xx а также yy как обычно, т.е.
x == y ~> xx == yy ~> :)
