Is there an efficient way (e.g., without using for loops manually) to create a matrix in Matlab that enumerates the 2-D coordinates of a matrix of a given size?
For example, if I'm given an m x n matrix, I want the resulting mn x 2 matrix to be as follows:
1 1
1 2
1 3
...
1 n
2 1
2 2
...
m 1
m 2
...
m n
Thanks in advance!
mat = [1 2;3 4;5 6;7 8;9 10];
[m,n] = size(mat);
vec = [kron(1:m,ones(1,n)); kron(ones(1,m),1:n)]'
1 1
1 2
2 1
2 2
3 1
3 2
4 1
4 2
5 1
5 2
Robert P. has a correct (and elegant) answer with a nifty use of kron, but just for fun here's the alternative with ndgrid,
>> mat=zeros(5,2);
>> [nn,mm] = ndgrid(1:size(mat,2),1:size(mat,1))
>> vec = [mm(:) nn(:)]
vec =
1 1
1 2
2 1
2 2
3 1
3 2
4 1
4 2
5 1
5 2
Related
If B=[1; 2] and A=[B B B...(n times B)], how to obtain the matrix C corresponding to all the possible combinations between the column vectors of A .i.e. I want to get the combinations between n copies of the same vector.
For example, for n=3:
A =
1 1 1
2 2 2
So, C can be obtained using the function from File Exchange 'allcomb(varargin)':
C=allcomb(A(:,1),A(:,2),A(:,3))
C =
1 1 1
1 1 2
1 2 1
1 2 2
2 1 1
2 1 2
2 2 1
2 2 2
In my case n is variable. How to obtain C for any value of n?
You can put the repetitions in a cell, and use the {:} syntax to put all cell elements as inputs to allcomb
n = 3;
B = [1,2];
A = repmat( {B}, n, 1 );
C = allcomb( A{:} ); % allcomb is FileExchange.
% combvec is a documented alternative.
Output:
C =
1 1 1
1 1 2
1 2 1
1 2 2
2 1 1
2 1 2
2 2 1
2 2 2
Since the alphabets for each of the places is the same, this is really a base conversion. MATLAB only accepts integer bases, but we can use that integer as an index into the alphabet B:
B=[1; 2];
n = 3;
b = numel(B);
for k = 0:(b^n-1) % loop over all possible combinations
C(k+1,:) = dec2base(k, b, n);
end
C = C - '0' + 1; % convert 0..b-1 (in chars) into 1..b (in ints) for indexing
C = B(C); % index into alphabet B
Results:
>> C
C =
1 1 1
1 1 2
1 2 1
1 2 2
2 1 1
2 1 2
2 2 1
2 2 2
The last line of the script doesn't appear to do much in this case because the alphabet happens to be the same range as our indices, but changing the alphabet to B = [7; 14] will correctly result in:
C =
7 7 7
7 7 14
7 14 7
7 14 14
14 7 7
14 7 14
14 14 7
14 14 14
Funnily enough, allcomb from MATLAB File Exchange seems to be what you want.
allcomb([1; 2],[1; 2], [1; 2])
ans =
1 1 1
1 1 2
1 2 1
1 2 2
2 1 1
2 1 2
2 2 1
2 2 2
To do it for any n, simply construct the matrix with:
>> n = 3;
>> repmat(B, 1, n)
ans =
1 1 1
2 2 2
If I have this vector:
x = [1 1 1 1 1 2 2 2 3 4 4 6 6 6 6]
I would like to get the position of each unique number according to itself.
y = [1 2 3 4 5 1 2 3 1 1 2 1 2 3 4]
At the moment I'm using:
y = sum(triu(x==x.')) % MATLAB 2016b and above
It's compact but obviously not memory efficient.
For the pure beauty of MATLAB programming I would avoid using a loop. Do you have a better simple implementation ?
Context:
My final goal is to sort the vector x but with the constraint that a number that appear N times has the priority over another number that has appeared more than N times:
[~,ind] = sort(y);
x_relative_sort = x(ind);
% x_relative_sort = 1 2 3 4 6 1 2 4 6 1 2 6 1 6 1
Assuming x is sorted, here's one vectorized alternative using unique, diff, and cumsum:
[~, index] = unique(x);
y = ones(size(x));
y(index(2:end)) = y(index(2:end))-diff(index).';
y = cumsum(y);
And now you can apply your final sorting:
>> [~, ind] = sort(y);
>> x_relative_sort = x(ind)
x_relative_sort =
1 2 3 4 6 1 2 4 6 1 2 6 1 6 1
If you have positive integers you can use sparse matrix:
[y ,~] = find(sort(sparse(1:numel(x), x, true), 1, 'descend'));
Likewise x_relative_sort can directly be computed:
[x_relative_sort ,~] = find(sort(sparse(x ,1:numel(x),true), 2, 'descend'));
Just for variety, here's a solution based on accumarray. It works for x sorted and containing positive integers, as in the question:
y = cell2mat(accumarray(x(:), x(:), [], #(t){1:numel(t)}).');
You can be more memory efficient by only comparing to unique(x), so you don't have a large N*N matrix but rather N*M, where N=numel(x), M=numel(unique(x)).
I've used an anonymous function syntax to avoid declaring an intermediate matrix variable, needed as it's used twice - this can probably be improved.
f = #(X) sum(cumsum(X,2).*X); y = f(unique(x).'==x);
Here's my solution that doesn't require sorting:
x = [1 1 1 1 1 2 2 2 3 4 4 6 6 6 6 1 1 1];
y = cell2mat( splitapply(#(v){cumsum(v)},x,cumsum(logical([1 diff(x)]))) ) ./ x;
Explanation:
% Turn each group new into a unique number:
t1 = cumsum(logical([1 diff(x)]));
% x = [1 1 1 1 1 2 2 2 3 4 4 6 6 6 6 1 1 1];
% t1 = [1 1 1 1 1 2 2 2 3 4 4 5 5 5 5 6 6 6];
% Apply cumsum separately to each group:
t2 = cell2mat( splitapply(#(v){cumsum(v)},x,t1) );
% t1 = [1 1 1 1 1 2 2 2 3 4 4 5 5 5 5 6 6 6];
% t2 = [1 2 3 4 5 2 4 6 3 4 8 6 12 18 24 1 2 3];
% Finally, divide by x to get the increasing values:
y = t2 ./ x;
% x = [1 1 1 1 1 2 2 2 3 4 4 6 6 6 6 1 1 1];
% t2 = [1 2 3 4 5 2 4 6 3 4 8 6 12 18 24 1 2 3];
Supose there is a Matrix
A =
1 3 2 4
4 2 5 8
6 1 4 9
and I have a Vector containing the "class" of each column of this matrix for example
v = [1 , 1 , 2 , 3]
How can I sum the columns of the matrix to a new matrix as column vectors each to the column of their class? In this example columns 1 and 2 of A would added to the first column of the new matrix, column 2 to the 3 to the 2nd, column 4 the the 3rd.
Like
SUM =
4 2 4
6 5 8
7 4 9
Is this possible without loops?
One of the perfect scenarios to combine the powers of accumarray and bsxfun -
%// Since we are to accumulate columns, first step would be to transpose A
At = A.' %//'
%// Create a vector of linear IDs for use with ACCUMARRAY later on
idx = bsxfun(#plus,v(:),[0:size(A,1)-1]*max(v))
%// Use ACCUMARRAY to accumulate rows from At, i.e. columns from A based on the IDs
out = reshape(accumarray(idx(:),At(:)),[],size(A,1)).'
Sample run -
A =
1 3 2 4 6 0
4 2 5 8 9 2
6 1 4 9 8 9
v =
1 1 2 3 3 2
out =
4 2 10
6 7 17
7 13 17
An alternative with accumarray in 2D. Generate a grid with the vector v and then apply accumarray:
A = A.';
v = [1 1 2 3];
[X, Y] = ndgrid(v,1:size(A,2));
Here X and Y look like this:
X =
1 1 1
1 1 1
2 2 2
3 3 3
Y =
1 2 3
1 2 3
1 2 3
1 2 3
Then apply accumarray:
B=accumarray([X(:) Y(:)],A(:)),
SUM = B.'
SUM =
4 2 4
6 5 8
7 4 9
As you see, using [X(:) Y(:)] create the following array:
ans =
1 1
1 1
2 1
3 1
1 2
1 2
2 2
3 2
1 3
1 3
2 3
3 3
in which the vector v containing the "class" is replicated 3 times since there are 3 unique classes that are to be summed up together.
EDIT:
As pointed out by knedlsepp you can get rid of the transpose to A and B like so:
[X2, Y2] = ndgrid(1:size(A,1),v);
B = accumarray([X2(:) Y2(:)],A(:))
which ends up doing the same. I find it a bit more easier to visualize with the transposes but that gives the same result.
How about a one-liner?
result = full(sparse(repmat(v,size(A,1),1), repmat((1:size(A,1)).',1,size(A,2)), A));
Don't optimize prematurely!
The for loop performs fine for your problem:
out = zeros(size(A,1), max(v));
for i = 1:numel(v)
out(:,v(i)) = out(:,v(i)) + A(:,i);
end
BTW: With fine, I mean: fast, fast, fast!
I'd like to transform a vector
v = [1,2,3,4]
into a matrix (4x4) with diagonals like this:
1 2 3 4
2 1 2 3
3 2 1 2
4 3 2 1
You want to generate a Toeplitz matrix, so you should use the toeplitz function.
v = [ 1 2 3 4 ];
out = toeplitz(v)
out =
1 2 3 4
2 1 2 3
3 2 1 2
4 3 2 1
If A is your 4x4 array, just do:
A(1:5:end) = 1:4;
Best
v = [1 2 3 4];
A = gallery('circul',v);
B = triu(A,0)+triu(A,1)';
given matrix A of size 6 by 6 contain blocks of numbers,each block of size 2 by 2, and outher reference matrix R of size 2 by 12 also contain blocks of numbers, each block of size 2 by 2. the perpse of the whole process is to form a new matrix, called the Index matrix, contain index's that refer to the position of the blocks within the matrix A based on the order of the blocks within the reference matrix R. and here is an exemple
matrix A:
A =[1 1 2 2 3 3;
1 1 2 2 3 3;
1 1 3 3 4 4;
1 1 3 3 4 4;
4 4 5 5 6 6;
4 4 5 5 6 6 ]
matrix R:
R=[1 1 2 2 3 3 4 4 5 5 6 6;
1 1 2 2 3 3 4 4 5 5 6 6 ]
the new matrix is:
Index =[1 2 3;
1 3 4;
4 5 6]
any ideas ?
With my favourite three guys - bsxfun, permute, reshape for an efficient and generic solution -
blksz = 2; %// blocksize
num_rowblksA = size(A,1)/blksz; %// number of blocks along rows in A
%// Create blksz x blksz sized blocks for A and B
A1 = reshape(permute(reshape(A,blksz,num_rowblksA,[]),[1 3 2]),blksz^2,[])
R1 = reshape(R,blksz^2,1,[])
%// Find the matches with "bsxfun(#eq" and corresponding indices
[valid,idx] = max(all(bsxfun(#eq,A1,R1),1),[],3)
%// Or with PDIST2:
%// [valid,idx] = max(pdist2(A1.',reshape(R,blksz^2,[]).')==0,[],2)
idx(~valid) = 0
%// Reshape the indices to the shapes of blocked shapes in A
Index = reshape(idx,[],num_rowblksA).'
Sample run with more random inputs -
>> A
A =
2 1 1 2
1 2 2 1
1 1 1 1
2 2 2 2
1 2 2 1
1 2 1 1
>> R
R =
2 1 1 1 1 2 2 2 1 1 1 1
2 1 2 1 1 2 2 1 2 2 2 1
>> Index
Index =
0 0
5 5
3 0