How to write a function in Matlab that takes a matrix with a single 1 value in each column and returns the index of this 1.
Ex:
if the input is x=[0 0 1;1 0 0;0 1 0] it will return indices=[2 3 1]
find is indeed the way to go
[indices,~] = find(x);
If you want to do it more cryptically, or hate find for some reason, you could also use cumsum:
indices = 4 - sum(cumsum(x,1),1);
If you're looking for the row index of the ones, this should do the trick:
[indices,~] = ind2sub(size(x),find(x))
You could also use the second output of max:
[~, result] = max(x==1, [], 1);
A slightly more esoretic approach:
result = nonzeros(bsxfun(#times, x==1, (1:size(x,1)).'));
Related
I just search the following related discussions:
How do I find a specific cell within a cell array?.
https://www.mathworks.com/matlabcentral/answers/84242-find-in-a-cell-array
However, they are not what I want exactly.
v = [1 0]; u = [0 1];
C = {v, u; u, u+u}
I create a cell C above with each element a row vector.
If I do
C{2,2}
it shows
ans =
0 2
Inversely, if I know [0 2], I want to find where it is, i.e., I want to get {2,2}, how could I do?
For the scalar case, the answer is shown in the second link; however, I cannot find the answer for the vector case so far.
Thanks!
Following this answer you linked to you can do :
found = cellfun(#(c) isequal(c,[0 2]),c)
which outputs
found =
2×2 logical array
0 0
0 1
lastly to get the coordinates you would use find :
[row,col] = find(found==1)
The output will be
row = 2
col = 2
I have a function that uses strfind in a cellfun call to find which string items in a cell array match a specified string. For example:
cellfun( #(x) strfind( x , 'openmask'), fileNames, 'uniformoutput', false)
The original cell matrix is like this:
fileNames = {'sub11att-openmask.txt', 'sub13det-masking', ...};
The result for this looks like this:
[10] [] [10] [] [9] []
I am trying to find a function that will convert this to:
10 0 10 0 9 0
Using cell2mat I get:
10 10 9
So I have to use this currently:
x(cellfun('isempty', x))={0};
cell2mat(x);
Is there a function that is cleaner than this (i.e. a one-liner solution)?
Thanks.
This works even if there are several occurrences of the sought string. It finds the first such occurrence if there's any, or gives 0 otherwise:
result = cellfun(#(x) sum(min(strfind(x, 'openmask'))), fileNames);
The code uses min to keep the first occurrence. This will give either a number or []. Then sum transforms [] into 0.
If you prefer to keep the last occurrence, change min to max or use Sardar Usama's suggestion:
result = cellfun(#(x) max([0 strfind(x, 'openmask')]), fileNames);
For a new variable y:
y(~cellfun('isempty', x)) = cell2mat(x);
It will break if a cell has more than one element tough.
What I'm trying to do
I have an array of numbers:
>> A = [2 2 2 2 1 3 4 4];
And I want to find the array indices where each number can be found:
>> B = arrayfun(#(x) {find(A==x)}, 1:4);
In other words, this B should tell me:
>> for ii=1:4, fprintf('Item %d in location %s\n',ii,num2str(B{ii})); end
Item 1 in location 5
Item 2 in location 1 2 3 4
Item 3 in location 6
Item 4 in location 7 8
It's like the 2nd output argument of unique, but instead of the first (or last) occurrence, I want all the occurrences. I think this is called a reverse lookup (where the original key is the array index), but please correct me if I'm wrong.
How can I do it faster?
What I have above gives the correct answer, but it scales terribly with the number of unique values. For a real problem (where A has 10M elements with 100k unique values), even this stupid for loop is 100x faster:
>> B = cell(max(A),1);
>> for ii=1:numel(A), B{A(ii)}(end+1)=ii; end
But I feel like this can't possibly be the best way to do it.
We can assume that A contains only integers from 1 to the max (because if it doesn't, I can always pass it through unique to make it so).
That's a simple task for accumarray:
out = accumarray(A(:),(1:numel(A)).',[],#(x) {x}) %'
out{1} = 5
out{2} = 3 4 2 1
out{3} = 6
out{4} = 8 7
However accumarray suffers from not being stable (in the sense of unique's feature), so you might want to have a look here for a stable version of accumarray, if that's a problem.
Above solution also assumes A to be filled with integers, preferably with no gaps in between. If that is not the case, there is no way around a call of unique in advance:
A = [2.1 2.1 2.1 2.1 1.1 3.1 4.1 4.1];
[~,~,subs] = unique(A)
out = accumarray(subs(:),(1:numel(A)).',[],#(x) {x})
To sum up, the most generic solution, working with floats and returning a sorted output could be:
[~,~,subs] = unique(A)
[subs(:,end:-1:1), I] = sortrows(subs(:,end:-1:1)); %// optional
vals = 1:numel(A);
vals = vals(I); %// optional
out = accumarray(subs, vals , [],#(x) {x});
out{1} = 5
out{2} = 1 2 3 4
out{3} = 6
out{4} = 7 8
Benchmark
function [t] = bench()
%// data
a = rand(100);
b = repmat(a,100);
A = b(randperm(10000));
%// functions to compare
fcns = {
#() thewaywewalk(A(:).');
#() cst(A(:).');
};
% timeit
t = zeros(2,1);
for ii = 1:100;
t = t + cellfun(#timeit, fcns);
end
format long
end
function out = thewaywewalk(A)
[~,~,subs] = unique(A);
[subs(:,end:-1:1), I] = sortrows(subs(:,end:-1:1));
idx = 1:numel(A);
out = accumarray(subs, idx(I), [],#(x) {x});
end
function out = cst(A)
[B, IX] = sort(A);
out = mat2cell(IX, 1, diff(find(diff([-Inf,B,Inf])~=0)));
end
0.444075509687511 %// thewaywewalk
0.221888202987325 %// CST-Link
Surprisingly the version with stable accumarray is faster than the unstable one, due to the fact that Matlab prefers sorted arrays to work on.
This solution should work in O(N*log(N)) due sorting, but is quite memory intensive (requires 3x the amount of input memory):
[U, X] = sort(A);
B = mat2cell(X, 1, diff(find(diff([Inf,U,-Inf])~=0)));
I am curious about the performance though.
I am sure this question must be answered somewhere else but I can't seem to find the answer.
Given a matrix M, what is the most efficient/succinct way to return two matrices respectively containing the row and column indices of the elements of M.
E.g.
M = [1 5 ; NaN 2]
and I want
MRow = [1 1; 2 2]
MCol = [1 2; 1 2]
One way would be to do
[MRow, MCol] = find(ones(size(M)))
MRow = reshape(MRow, size(M))
MCol = reshape(MCol, size(M))
But this does not seem particular succinct nor efficient.
This essentially amounts to building a regular grid over possible values of row and column indices. It can be achieved using meshgrid, which is more effective than using find as it avoids building the matrix of ones and trying to "find" a result that is essentially already known.
M = [1 5 ; NaN 2];
[nRows, nCols] = size(M);
[MCol, MRow] = meshgrid(1:nCols, 1:nRows);
Use meshgrid:
[mcol, mrow] = meshgrid(1:size(M,2),1:size(M,1))
I want to convert a matrix M into "tall-skinny" format. The resulting matrix would have rows like [r, c, M(r,c)] for every r and c in the rows and columns of M. Is there a function which does this? Alternatively, is there a function which does the reverse?
Is this what you want?
[ii jj] = ndgrid(1:size(M,1), 1:size(M,2));
T = [ii(:) jj(:) M(:)];
Reverse:
M = full(sparse(T(:,1), T(:,2), T(:,3)));
or more simply, as noted by Jumppy89,
M = full(spconvert(T));
You can do it in a different way to Luis and Divakar, using sub2ind and ind2sub for the reverse. I doubt this is a better solution than theirs though.
To convert to the "tall-skinny" format:
M = [1 1 1
1 2 2
2 1 3
2 2 4]
S=zeros([max(M(:,1)) max(M(:,2))])
I=sub2ind([max(M(:,1)) max(M(:,2))],M(:,1),M(:,2))
S(I)=M(:,3)
And the reverse:
[r,c]=ind2sub(size(S),1:numel(S))
M=[r.' c.' S(:)]
I am curious too if this would satisfy your needs -
Normal to Tall-skinny (based on meshgrid) -
[x1,x2] = meshgrid(1:size(M,1),1:size(M,2))
TSM = [x2(:) x1(:) M(:)] %// Tall-Skinny M
Tall-skinny to Normal (based on sub2ind and reshaping) -
m1 = max(TSM(:,2))
n1 = max(TSM(:,1))
TSM3 = TSM(:,3)
M_out = reshape(TSM3(sub2ind([n1 m1],TSM(:,1),TSM(:,2))),m1,n1)