Matlab конвертирует вектор в двоичную матрицу

У меня есть вектор v размера (m,1), элементы которого являются целыми числами, выбранными из 1:n. Я хочу создать матрицу M размера (m,n), элементы которой M(i,j) равны 1, если v(i) = j, и равны 0 в противном случае. Я не хочу использовать циклы и хотел бы реализовать это только как простую манипуляцию с векторной матрицей.

Вот я и подумал сначала создать матрицу с повторяющимися элементами

 M = v * ones(1,n) % this is a (m,n) matrix of repeated v

Например, v=[1,1,3,2]' m = 4 и n = 3

M =
     1     1     1
     1     1     1
     3     3     3
     2     2     2

затем мне нужно создать вектор сравнения c размера (1,n)

c = 1:n
1 2 3

Затем мне нужно выполнить серию логических сравнений

M(1,:)==c % this results in [1,0,0]
.
M(4,:)==c % this results in [0,1,0]

Тем не менее, я думал, что должно быть возможно выполнить последние шаги по каждой отдельной строке в компактной матричной нотации, но я в тупике и недостаточно осведомлен об индексации. Конечный результат должен быть

M =
     1     0     0
     1     0     0
     0     0     1
     0     1     0

person gciriani    schedule 09.09.2015    source источник


Ответы (3)


Очень простой вызов bsxfun сделает свое дело:

>> n = 3;
>> v = [1,1,3,2].';
>> M = bsxfun(@eq, v, 1:n)

M =

     1     0     0
     1     0     0
     0     0     1
     0     1     0

Принцип работы кода на самом деле довольно прост. bsxfun – это так называемая функция расширения Binary Singleton EX. Это означает, что вы предоставляете два массива/матрицы любого размера, если они широковещательные. Это означает, что они должны иметь возможность расширяться в размерах, чтобы они оба были равны по размеру. В этом случае v представляет собой интересующий вас вектор и является первым параметром — обратите внимание, что он транспонирован. Второй параметр — это вектор от 1 до n. Что произойдет сейчас, так это то, что вектор-столбец v будет реплицирован / расширен для стольких значений, сколько имеется n, а второй вектор будет реплицирован для стольких строк, сколько есть в v. . Затем мы делаем оператор eq/равно между этими двумя массивами. Фактически эта расширенная матрица имеет все единицы в первом столбце, все двойки во втором столбце, вплоть до n. Выполняя eq между этими двумя матрицами, вы фактически определяете, какие значения в v равны соответствующему индексу столбца.


Вот подробный тест времени и разбивка каждой функции. Я поместил каждую реализацию в отдельную функцию, а также разрешил n=max(v), чтобы работал первый код Луиса. Я использовал timeit для определения времени каждой функции:

function timing_binary

n = 10000;
v = randi(1000,n,1);
m = numel(v);

    function luis_func()
    M1 = full(sparse(1:m,v,1));       
    end

    function luis_func2()
    %m = numel(v);
    %n = 3; %// or compute n automatically as n = max(v);
    M2 = zeros(m, n);
    M2((1:m).' + (v-1)*m) = 1;      
    end

    function ray_func()
    M3 = bsxfun(@eq, v, 1:n);
    end

    function op_func()
    M4= ones(1,m)'*[1:n] == v * ones(1,n);
    end

t1 = timeit(@luis_func);
t2 = timeit(@luis_func2);
t3 = timeit(@ray_func);
t4 = timeit(@op_func);

fprintf('Luis Mendo - Sparse: %f\n', t1);
fprintf('Luis Mendo - Indexing: %f\n', t2);
fprintf('rayryeng - bsxfun: %f\n', t3);
fprintf('OP: %f\n', t4);


end

Этот тест предполагает n = 10000, а вектор v представляет собой вектор 10000 x 1 случайно распределенных целых чисел от 1 до 1000. Кстати, мне пришлось изменить вторую функцию Луиса, чтобы индексирование работало, поскольку для сложения требуются векторы совместимых размеров.

Запустив этот код, мы получим:

>> timing_binary
Luis Mendo - Sparse: 0.015086
Luis Mendo - Indexing: 0.327993
rayryeng - bsxfun: 0.040672
OP: 0.841827

Выигрывает код Луиса Мендо sparse (как я и ожидал), за ним следует bsxfun, за которым следует индексирование, а затем предлагаемый вами подход с использованием матричных операций. Тайминги указаны в секундах.

person rayryeng    schedule 09.09.2015
comment
о боже, я писал точно такой же ответ, ха-ха, я определенно печатаю слишком медленно! - person Benoit_11; 09.09.2015
comment
@ Benoit_11 - лол, извини :) - person rayryeng; 09.09.2015
comment
@LuisMendo - Спасибо! :D Мне пришлось немного изменить его, потому что я слишком быстро прочитал вопрос. - person rayryeng; 09.09.2015
comment
Я не думаю, что вам нужен оператор точки перед знаком транспонирования. - person gciriani; 09.09.2015
comment
@LuisMendo не согласится с вами. .' должен выполнять транспонирование независимо от того, является ли массив реальным или сложным. Выполнение ' само по себе приведет к выполнению сложного транспонирования. Я использую точку, потому что я явно хочу прояснить это. Хотя это скорее стилистический выбор. В вашем случае у вас нет воображаемых компонентов, поэтому вы можете использовать или или, но я привык использовать .'. Неиспользование его несколько раз укусило меня в @$$... и поэтому я просто привык его использовать. - person rayryeng; 09.09.2015
comment
Я проведу несколько временных тестов и посмотрю, что быстрее. - person rayryeng; 09.09.2015
comment
@gciriani - Ваш метод самый медленный. Я обновлю свой пост. - person rayryeng; 09.09.2015
comment
@gciriani Я всегда советую: используйте .', когда вы просто хотите транспонировать! Вот два примера ошибок, вызванных использованием ', когда вы имеете в виду .': временной домен"> stackoverflow.com/questions/22258444/ stackoverflow.com/questions/23509241/ - person Luis Mendo; 09.09.2015
comment
Прохладный! Спасибо. Пока вы писали это, я попытался с помощью секундомера запустить вектор 1o^6 против значений 10^4, и мой компьютер завис: ни ctrl-C, ни что-либо еще не могли его остановить. Пришлось физически выдернуть вилку :-D - person gciriani; 10.09.2015

Предполагая, что n равно max(v), вы можете использовать sparse:

v = [1,1,3,2];
M = full(sparse(1:numel(v),v,1));

Что делает sparse, так это строит разреженную матрицу, используя первый аргумент в качестве индексов строк. , второй как индексы столбцов, а третий как значения матрицы. Затем он преобразуется в полную матрицу с full.


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

v = [1,1,3,2];
m = numel(v);
n = 3; %// or compute n automatically as n = max(v);
M = zeros(m, n);
M((1:m) + (v-1)*m) = 1;
person Luis Mendo    schedule 09.09.2015

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

M= ones(1,m)'*[1:n] == v * ones(1,n)
person gciriani    schedule 09.09.2015