What I'm trying to do: given a 2D matrix, get the column indices of the elements in each row that satisfy some particular condition.
For example, say my matrix is
M = [16 2 3 13; 5 11 10 8; 9 7 6 12; 4 14 15 1]
and my condition is M>6. Then my desired output would be something like
Indices = {[1 4]'; [2 3 4]'; [1 2 4]'; [2 3]';}
After reading the answers to this similar question I came up with this partial solution using find and accumarray:
[ix, iy] = find(M>6);
Indices = accumarray(ix,iy,[],#(iy){iy});
This gives very nearly the results I want -- in fact, the indices are all right, but they're not ordered the way I expected. For example, Indices{2} = [2 4 3]' instead of [2 3 4]', and I can't understand why. There are 3 occurrences of 2 in ix, at indices 3, 6, and 9. The corresponding values of iy at those indices are 2, 3, and 4, in that order. What exactly is creating the observed order? Is it just arbitrary? Is there a way to force it to be what I want, other than sorting each element of Indices afterwards?
Here's one way to solve it with arrayfun -
idx = arrayfun(#(x) find(M(x,:)>6),1:size(M,1),'Uni',0)
Display output wtih celldisp(idx) -
idx{1} =
1 4
idx{2} =
2 3 4
idx{3} =
1 2 4
idx{4} =
2 3
To continue working with accumarray, you can wrap iy with sort to get your desired output which doesn't look too pretty maybe -
Indices = accumarray(ix,iy,[],#(iy){sort(iy)})
Output -
>> celldisp(Indices)
Indices{1} =
1
4
Indices{2} =
2
3
4
Indices{3} =
1
2
4
Indices{4} =
2
3
accumarray is not guaranteed to preserve order of each chunk of its second input (see here, and also here). However, it does seem to preserve it when the first input is already sorted:
[iy, ix] = find(M.'>6); %'// transpose and reverse outputs, to make ix sorted
Indices = accumarray(ix,iy,[],#(iy){iy}); %// this line is the same as yours
produces
Indices{1} =
1
4
Indices{2} =
2
3
4
Indices{3} =
1
2
4
Indices{4} =
2
3
Related
I want to find all possible variations (combinations) of a vector, choosing various numbers of elements from that vector.
For example, suppose I have the vector:
x = [1 2 3 4 5];
I can determine the number of combinations for each number of chosen elements:
x = [1 2 3 4 5]';
n = numel(x);
for k = 1:n
combs(k) = nchoosek(n,k);
end
sum(combs)
This results in:
combs = 5 10 10 5 1
sum(combs) = 31
I want a way to store all 31 of these combinations in an array, for example a cell array, with n cells, within each is an array in which each row is a vector combination of the elements.
e.g. at k = 4:
combs{4} =
1 2 3 4
1 2 3 5
1 2 4 5
1 3 4 5
2 3 4 5
Is there an existing function that does this, or what would be the most simple approach to this?
Call nchoosek with a vector as first input, using arrayfun (or equivalently for) to loop over the number of picked elements:
n = 5;
combs = arrayfun(#(k) nchoosek(1:n,k), 1:n, 'UniformOutput', false);
Here is an approach using dec2bin , find and accumarray:
x = [1 2 3 4 5];
[a b] = find(dec2bin(1:2^numel(x)-1)=='1');
combs = accumarray(a,x(b),[],#(c){c});
I've got a vector A = [6 5 7 7 4] and want to obtain the ranks as either [3 2 4 5 1] or [3 2 5 4 1] - I don't mind which. The answer is a vector in which each element is replaced by the rank it holds. This indicates to me that the fifth element is the smallest, then the second element is the second smallest, and so on.
I thought of doing [~,~,rnk] = unique(A), however that doesn't work, and produces instead [3 2 4 4 1].
How can I obtain the solution with no tied ranks?
It's almost a duplicate of this question.
We use sort twice, first sorting the array to get the index and then sort the index.
A = [6 5 7 7 4];
[~, rnk] = sort(A);
[~, rnk] = sort(rnk);
rnk =
3 2 4 5 1
I got a 4-by-n matrix, like
A =
1 5 9
3 0 6
2 3 10
7 8 4
What I want to do with A is getting each half column of A as
Line1Point1 = [1 3]
Line1Point2 = [2 7]
Line2Point1 = [5 0]
Line2Point2 = [3 8]
Line3Point1 = [9 6]
Line3Point2 = [10 4]
How could I do that? I’m pretty new to matlab coding.. Any help is really appreciated..
Cheers
Use reshape function, for example:
>> A = [1 5 9;
3 0 6;
2 3 10;
7 8 4];
>> reshape(A,2,6)
ans =
1 2 5 3 9 10
3 7 0 8 6 4
Storing such information as many variables is generally a bad idea
Some options for storing and accessing are
Cell array
Line=mat2cell(A,[2,2],ones(1,size(A,2))).'
access with
Line{2,1}
ans =
5
0
Indexing
as other answers
Anonymous Function
Line=#(l,p)A(2*p-1:2*p,l)
access with
Line(2,1)
ans =
5
0
Structure
Not really a useful solution, more for interests sake
for ii=1:size(A,2);for jj=1:2;Line(ii).Point(jj).Value=A(2*jj-1:2*jj,ii);end;end
access with
Line(2).Point(1).Value
ans =
5
0
A(1:2,1) will give you first half of the first column.
A(3:4,1) will give you second half of the first column.
A(1:2,2) will give you first half of the second column.
A(3:4,2) will give you second half of the second column.
A(1:2,3) will give you first half of the third column.
A(3:4,3) will give you second half of the third column.
You can create the variables with the eval function, which executes the input string. Using eval is commonly regarded as bad practice since it is horrible to debug.
Nevertheless, here's the code:
A = [1 5 9; 3 0 6; 2 3 10; 7 8 4];
for ii = 1:length(A(1,:))
eval(['Line' num2str(ii) 'Point1 = A(1:2, ii)' ]);
eval(['Line' num2str(ii) 'Point2 = A(3:4, ii)' ]);
end
% Now all variables are created - for example: Line2Point1
A more elegant solution could be to store the vectors in a cell array. You can acces the first vectors for example by typing: c{1,1}
c = cell(length(A(1,:)),2)
for ii = 1:length(A(1,:))
c{ii,1} = A(1:2, ii);
c{ii,2} = A(3:4, ii);
end
I would suggest using 3D arrays to store and then access those values.
Code
N = size(A,1)/2;
LinePoint = permute(reshape(A,N,size(A,1)/N,[]),[1 3 2])
Here,
2nd dimension indices (columns) would represent Line IDs
3rd dimension indices would represent Point IDs.
Thus, the representative 3D array would be - LinePoint(:,LineID,PointID).
Example run
For your given A, we would have LinePoint as -
LinePoint(:,:,1) =
1 5 9
3 0 6
LinePoint(:,:,2) =
2 3 10
7 8 4
Thus,
Line1Point1 would be denoted by LinePoint(:,1,1)
Line1Point2 would be denoted by LinePoint(:,1,2)
Line2Point1 would be denoted by LinePoint(:,2,1)
Line2Point2 would be denoted by LinePoint(:,2,2)
Line3Point1 would be denoted by LinePoint(:,3,1)
Line3Point2 would be denoted by LinePoint(:,3,2)
I would like to get the cumsum of an vector, but need the first element of the vector to count itself as well. An example:
a = [1 2 3 4 5]
and the result needs to look as follow:
2 3 6 10 15
Presumably you already know about the cumsum function so, have you not tried?:
a = [1 2 3 4 5];
s = cumsum(a);
s(1) = s(1)+a(1)
which returns
s =
2 3 6 10 15
By default, MATLAB's sort function deals with ties/repeated elements by preserving the order of the elements, that is
>> [srt,idx] = sort([1 0 1])
srt =
0 1 1
idx =
2 1 3
Note that the two elements with value 1 in the input arbitrarily get assigned index 2 and 3, respectively. idx = [3 1 2], however, would be an equally valid sort.
I would like a function [srt,all_idx] = sort_ties(in) that explicitly returns all possible values for idx that are consistent with the sorted output. Of course this would only happen in the case of ties or repeated elements, and all_idx would be dimension nPossibleSorts x length(in).
I got started on a recursive algorithm for doing this, but quickly realized that things were getting out of hand and someone must have solved this before! Any suggestions?
I had a similar idea to what R. M. suggested. However, this solution is generalized to handle any number of repeated elements in the input vector. The code first sorts the input (using the function SORT), then loops over each unique value to generate all the permutations of the indices for that value (using the function PERMS), storing the results in a cell array. Then these index permutations for each individual value are combined into the total number of permutations for the sorted index by replicating them appropriately with the functions KRON and REPMAT:
function [srt,all_idx] = sort_ties(in,varargin)
[srt,idx] = sort(in,varargin{:});
uniqueValues = srt(logical([1 diff(srt)]));
nValues = numel(uniqueValues);
if nValues == numel(srt)
all_idx = idx;
return
end
permCell = cell(1,nValues);
for iValue = 1:nValues
valueIndex = idx(srt == uniqueValues(iValue));
if numel(valueIndex) == 1
permCell{iValue} = valueIndex;
else
permCell{iValue} = perms(valueIndex);
end
end
nPerms = cellfun('size',permCell,1);
for iValue = 1:nValues
N = prod(nPerms(1:iValue-1));
M = prod(nPerms(iValue+1:end));
permCell{iValue} = repmat(kron(permCell{iValue},ones(N,1)),M,1);
end
all_idx = [permCell{:}];
end
And here are some sample results:
>> [srt,all_idx] = sort_ties([0 2 1 2 2 1])
srt =
0 1 1 2 2 2
all_idx =
1 6 3 5 4 2
1 3 6 5 4 2
1 6 3 5 2 4
1 3 6 5 2 4
1 6 3 4 5 2
1 3 6 4 5 2
1 6 3 4 2 5
1 3 6 4 2 5
1 6 3 2 4 5
1 3 6 2 4 5
1 6 3 2 5 4
1 3 6 2 5 4
Consider the example A=[1,2,3,2,5,6,2]. You want to find the indices where 2 occurs, and get all possible permutations of those indices.
For the first step, use unique in combination with histc to find the repeated element and the indices where it occurs.
uniqA=unique(A);
B=histc(A,uniqA);
You get B=[1 3 1 1 1]. Now you know which value in uniqA is repeated and how many times. To get the indices,
repeatIndices=find(A==uniqA(B==max(B)));
which gives the indices as [2, 4, 7]. Lastly, for all possible permutations of these indices, use the perms function.
perms(repeatIndices)
ans =
7 4 2
7 2 4
4 7 2
4 2 7
2 4 7
2 7 4
I believe this does what you wanted. You can write a wrapper function around all this so that you have something compact like out=sort_ties(in). You probably should include a conditional around the repeatIndices line, so that if B is all ones, you don't proceed any further (i.e., there are no ties).
Here is a possible solution I believe to be correct, but it's somewhat inefficient because of the duplicates it generates initially. It's pretty neat otherwise, but I still suspect it can be done better.
function [srt,idx] = tie_sort(in,order)
L = length(in);
[srt,idx] = sort(in,order);
for j = 1:L-1 % for each position in sorted array, look for repeats following it
for k = j+1:L
% if repeat found, add possible permutations to the list of possible sorts
if srt(j) == srt(k)
swapped = 1:L; swapped(j) = k; swapped(k) = j;
add_idx = idx(:,swapped);
idx = cat(1,idx,add_idx);
idx = unique(idx,'rows'); % remove identical copies
else % because already sorted, know don't have to keep looking
break;
end
end
end