Matlab index to logic indexing

删除回忆录丶 提交于 2019-11-27 06:01:01

问题


I have given a list of indices, e.g. i = [3 5] and a vector v = 1:6. I need a function f which returns the logical map for the vector v given the indices i, e.g.:

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

Since I will call this function several million times, I would like to make it as fast as possible. Is there a builtin function which performs this task?


回答1:


I know I'm late in the game, but I really wanted to find a faster solution which is just as elegant as ismember. And indeed there is one, that employs the undocumented ismembc function:

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

Here's what I got:

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

Amazingly, ismembc is indeed the fastest!

Edit:
For very large values of N (i.e. when v is a large array), the faster solution is actually slayton's (and HebeleHododo's, for that matter). You have quite a variety of strategies to choose from, pick carefully :)

Edit by H.Muster:
Here's are benchmark results including _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.

Interestingly, Jonas' solution does not run for me, as I get an Index exceeds matrix dimensions. error...

Edit by hoogamaphone:
It's worth noting that ismembc requires both inputs to be numerical, sorted, non-sparse, non-NaN values, which is a detail that could be easily missed in the source documentation.




回答2:


You can use ismember

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

 ismember(v,i)

will return

ans =

     0     0     1     0     1     0

For a probably faster version, you can try

builtin('_ismemberoneoutput', v, i)

Note that I tested this only for row vectors like specified by you.




回答3:


Simply create a vector of logical indices and set the desired locations to true/false

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

This can be wrapped in a function like so:

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

If you need a indexing vector of the same size for each of your million operations allocated the vector once and then operate on it each iteration:

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

If you really need performance than you can write a mex function to do this for you. Here is a very basic, untested function that I wrote that is about 2x faster than the other methods:

#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;
    }

}

There are several different ways to allocate a vector in matlab. Some are faster than others, see this Undocumented Matlab post for a good summary:

Here are some quick benchmarks comparing the different methods. The last method is by far the fastest but it requires you to use the same size logical indexing vector for each operation.

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;



回答4:


Just address a new variable with the idx matrix, it wil fill in the zeros for you:

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

No need for a function, nor for passing the length in unless you want trailing zeros too.




回答5:


I expect that @slayton's solution is fastest. However, here's a one-liner alternative, that may at least save you some memory if the vectors are large.

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



回答6:


You can write a function like this:

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

When you call the function:

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

The output will be:

ans =

     0     0     1     0     1     0



回答7:


Can you simply do v(i) =1 ?

for example if you say x = zeros(1,10); and a = [1 3 4];

x(a) = 1 will return 1 0 1 1 0 0 0 0 0 0



来源:https://stackoverflow.com/questions/14606863/matlab-index-to-logic-indexing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!