Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matlab index to logic indexing

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?

like image 672
blubb Avatar asked Jan 30 '13 14:01

blubb


People also ask

How do you do logical indexing in MATLAB?

In logical indexing, you use a single, logical array for the matrix subscript. MATLAB extracts the matrix elements corresponding to the nonzero values of the logical array. The output is always in the form of a column vector. For example, A(A > 12) extracts all the elements of A that are greater than 12.

Does MATLAB use 0 or 1 indexing?

In most programming languages, the first element of an array is element 0. In MATLAB, indexes start at 1.

What kind of indexing is used in MATLAB?

In MATLAB®, there are three primary approaches to accessing array elements based on their location (index) in the array. These approaches are indexing by position, linear indexing, and logical indexing.

Why do we use * in MATLAB?

MATLAB matches all characters in the name exactly except for the wildcard character * , which can match any one or more characters.


4 Answers

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.

like image 195
Eitan T Avatar answered Oct 21 '22 12:10

Eitan T


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.

like image 30
H.Muster Avatar answered Oct 21 '22 11:10

H.Muster


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;
like image 5
slayton Avatar answered Oct 21 '22 11:10

slayton


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);
like image 2
Jonas Avatar answered Oct 21 '22 13:10

Jonas