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?
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.
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.
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;
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.
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);
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
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