Индекс Matlab для логического индексирования

Я дал список индексов, например,i = [3 5] и вектора v = 1:6. Мне нужна функция f возвращает логическую карту для вектора v учитывая показатели i, например:

f(i, length(v)) = [0 0 1 0 1 0]

так я буду называть эту функцию несколько миллионов раз, я хотел бы сделать это как можно быстрее. Есть ли builtin функция, которая выполняет эту задачу?

7 ответов


я знаю, что я поздно в игре, но я действительно хотел найти более быстрое решение, которое так же элегантно, как ismember. И действительно, есть один, который использует недокументированных ismembc функция:

ismembc(v, i)

Benchmark

N = 7;
i = [3 5];

%// slayton's solution
tic
for ii = 1:1e5
    clear idx;
    idx(N) = false;
    idx(i) = true;
end
toc

%// H.Muster's solution
tic
for ii = 1:1e5
    v = 1:N;
    idx = ismember(v, i);
end
toc

%// Jonas' solution
tic
for ii = 1:1e5
    idx = sparse(i, 1, true, N, 1);
end
toc

%// ismembc solution
tic
for ii = 1:1e5
    v = 1:N;
    idx = ismembc(v, i);
end
toc

вот что я получил:

Elapsed time is 1.482971 seconds.
Elapsed time is 6.369626 seconds.
Elapsed time is 2.039481 seconds.
Elapsed time is 0.776234 seconds.

поразительно, ismembc - это действительно самый быстрый!

Edit:
Для очень больших значений N (то есть, когда v является большим массивом), более быстрое решение на самом деле является slayton (и HebeleHododo, если на то пошло). У вас есть довольно разнообразные стратегии на выбор, выбирайте тщательно:)

Edit by H. Muster:
Вот результаты тестов, включая _ismemberoneoutput:

Slayton's solution:
   Elapsed time is 1.075650 seconds.
ismember:
   Elapsed time is 3.163412 seconds.
ismembc:
   Elapsed time is 0.390953 seconds.
_ismemberoneoutput:
   Elapsed time is 0.477098 seconds.

интересно, что решение Джонаса не работает для меня, так как я получаю Index exceeds matrix dimensions. ошибка...

редактировать hoogamaphone:
Стоит отметить, что ismembc требует, чтобы оба входа были числовыми, отсортированными, не разреженными, не-NaN значениями, что является деталью, которую можно легко пропустить в источник документации.


можно использовать ismember

 i = [3 5];
 v = 1:6;

 ismember(v,i)

вернутся

ans =

     0     0     1     0     1     0

для, вероятно, более быстрой версии, вы можете попробовать

builtin('_ismemberoneoutput', v, i)

обратите внимание, что я проверил это только для векторов строк, как указано вами.


просто создайте вектор логических индексов и установите нужные местоположения в true/false

idx = false( size( v) );
idx( i ) = true;

Это можно обернуть в функцию так:

function idx = getLogicalIdx(size, i)
  idx = false(size);
  idx(i) = true;
end

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

idx = false(size(v)); % allocate the vector
while( keepGoing)

  idx(i) = true; % set the desired values to true for this iteration

  doSomethingWithIndecies(idx);

  idx(i) = false; % set indices back to false for next iteration

end

Если вам действительно нужна производительность, чем вы можете написать функцию mex, чтобы сделать это за вас. Вот очень основная, непроверенная функция, которая Я написал, что это примерно на 2x быстрее, чем другие методы:

#include <math.h>
#include <matrix.h>
#include <mex.h>

void mexFunction(int nlhs, mxArray *plhs[],
                 int nrhs, const mxArray *prhs[])
{
    double M;
    double *in;

    M = mxGetScalar(prhs[0]);
    in = mxGetPr(prhs[1]);
    size_t N = mxGetNumberOfElements(prhs[1]);



    plhs[0] = mxCreateLogicalMatrix( M,1 );
    mxLogical *out= mxGetLogicals( plhs[0] );


    int i, ind;
    for (i=0; i<N; i++){
        out[ (int)in[i] ] = 1;
    }

}

существует несколько различных способов выделения вектора в matlab. Некоторые из них быстрее, чем другие, см. Этот недокументированный пост Matlab для хорошего резюме:

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

N = 1000;
ITER = 1e5;

i = randi(5000,100,1);
sz = [N, 1];

fprintf('Create using false()\n');
tic;
for j = 1:ITER
    clear idx;
    idx = false( N, 1 );
    idx(i) = true;
end
toc;

fprintf('Create using indexing\n');
tic;
for j = 1:ITER
    clear idx;
    idx(N) = false;
    idx(i) = true;
end
toc;

fprintf('Create once, update as needed\n');
tic;
idx = false(N,1);
for j = 1:ITER
    idx(i) = true;
    idx(i) = false;
end
toc;

fprintf('Create using ismembc\n');
a = ones(N,1);
tic;
for j = 1:ITER

    idx = ismembc(1:N, i);
end
toc;

просто адресуйте новую переменную с матрицей idx, она заполнит для вас нули:

idx = [3 5];
a(idx) = true

нет необходимости в функции, ни для передачи длины, если вы не хотите, чтобы конечные нули тоже.


Я ожидаю, что решение @slayton будет самым быстрым. Однако вот один вариант, который может, по крайней мере, сэкономить вам память, если векторы большие.

vecLen = 6;
logicalIdx = sparse(idx,1,true,vecLen,1);

вы можете написать такую функцию:

function logicalIdx = getLogicalIdx(idx, v)
    logicalIdx = zeros(1,size(v,2));
    logicalIdx(idx) = 1;
end

при вызове функции:

v = 1:6;
idx = [3 5];
getLogicalIdx(idx,v)

выход будет:

ans =

     0     0     1     0     1     0

можете ли вы просто сделать v (i) =1 ?

например, если вы говорите x = нули(1,10); а = [1 3 4];

x (a) = 1 вернет 1 Ноль Один Один Ноль Ноль Ноль Ноль Ноль 0