Related
Suppose I have a matrix of dimension [4x4], and a vector of [16x1], I need to multiply every 4 element in the vector in one element in the matrix, (instead of multiplying element in row by element in vector), how can I do that using loop ?
For example here below, the results of the first four elements in the resulted vector as shown in the below example, then the same thing for the second, third and fourth rows in the matrix. :
So the results in that example is supposed to be [16x1]
Thank you
Using kron you can use this one-liner:
%A = [1 2 3 4; 5 6 7 8; 9 10 11 12; 13 14 15 16];
%v = [2 2 2 2 0 0 0 0 1 1 1 1 3 3 3 3].';
sum(kron(A,ones(4,4)).'.*v).'/4
I use the kronecker tensor product to "replicate" 4x4 time the A matrice. After that it's pure algebra.
This is just matrix multiplication in disguise... If your tall vector was a matrix of the same size as the matrix shown, where each highlighted block is a row, it's matrix multiplication. We can set this up, then reshape back into a vector.
You can use indexing to turn this into simple matrix multiplication. A question I answered already today details how the below indexing works using bsxfun, then we just reshape at the end:
% Setup
A = [1 2 3 4; 5 6 7 8; 9 10 11 12; 13 14 15 16];
v = [2 2 2 2 0 0 0 0 1 1 1 1 3 3 3 3].';
% Matrix mutliplication
r = numel(v)/size(A,1);
b = A * v( bsxfun( #plus, (1:r:numel(v)).', 0:r-1 ) );
% Make result a column vector
b = reshape( b.', [], 1 );
See if this is what you want:
A = [1 2 3 4; 5 6 7 8; 9 10 11 12; 13 14 15 16];
v = [2 2 2 2 0 0 0 0 1 1 1 1 3 3 3 3].';
r = reshape(sum(bsxfun(#times, permute(A, [3 2 1]), permute(reshape(v, 1, [], size(A,2)), [2 3 1])), 2), [], 1);
which gives
r =
17
17
17
17
41
41
41
41
65
65
65
65
89
89
89
89
There are details that I assumed, but this shoudl do the trick:
A=reshape(1:16,4,4).';
b=repelem([2,0,1,3],1,4).';
c=[];
for row=1:size(A,1)
c=[ c; sum(reshape(repelem(A(row,:),4).*b.',4,[]),2)];
end
I am assuming here that your demo for the vector is just a bad example and that you wont have repeated values, otherwise an easier version can be achieved by just not doing 3/4ths of the multiplications.
If you do not have access to repelem, have a look at alterative codes that do the same thing:Element-wise array replication in Matlab
I have the following vector:
x = [6 7 8 9 10 11 17 18 19 20];
I have the duration vector, d, which counts the elements in x:
d = [6 4] => d = [x(1:6) x(7:10)];
So I want to find the entrances and exits of d(1) and d(2):
d(1) = x(1:6) => r1 = [6 11];
d(2) = x(7:10) => r2 = [17 20];
So finally I want to reshape into the following matrix:
result = [r1; r2] = [6 11; 17 20];
Anyone has any idea?
One way of constructing your result would be to use cumsum to help construct your indices.
result = x([cumsum([1 d(1:end-1)]); cumsum(d)]).';
6 11
17 20
If you don't mind breaking this into two lines, you could easily do the same with the following which may be more performant since you're only computing the cumulative sum once.
C = cumsum(d);
result = x([1, C(1:end-1)+1; C]).';
This constructs a matrix containing the first and last indices of each group specified in d. We can then use these to index directly into x.
starts = cumsum([1 d(1:end-1)])
1 7
ends = cumsum(d)
6 10
indices = [starts; ends]
1 7
6 10
x(indices).'
6 11
17 20
If there is a vector like this,
T = [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]
(the size of vector T can be flexible)
How can I get a array of 'sum of divisions'?
For example,
fn(T, 5) = [ (1+2+3+4+5) , (6+7+8+9+10), (11+12+13+14+15) , 16]
One option, which doesn't require the padding of zeros on the original array, is the use of accumarray and ceil:
div = 5;
out = accumarray(ceil((1:numel(T))/div).',T(:))
Another option using cumsum and diff instead:
div = 5;
T(ceil(numel(T)/div)*div) = 0;
cs = cumsum(T)
out = diff( [0 cs(div:div:end) ] )
Edit: once the padding is done, cumsum and diff are a little overkill and one should proceed as in Bentoy's answer.
Another way, close to the 2nd option of thewaywewalk:
div = 5;
T(ceil(numel(T)/div)*div) = 0;
out = sum(reshape(T,div,[])).'; % transpose if you really want a column vector
Also, one one-liner solution (I prefer this one):
out = blockproc(T,[1 5], #(blk) sum(blk.data), 'PadPartialBlocks',true);
Don't forget to set the parameter 'PadPartialBlocks', this is the key of avoiding explicit padding.
There is an in-built function vec2mat in Communications System Toolbox to convert a vector into a 2D matrix that cuts off after every N elements and puts into separate rows, padding the leftover places at the end with zeros to maintain 2D size . So, after using vec2mat, summing all the rows would be enough to give you the desired output. Here's the implementation -
sum(vec2mat(T,5),2)
Sample run -
>> T = 1:16;
>> vec2mat(T,5)
ans =
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 0 0 0 0
>> sum(vec2mat(T,5),2)
ans =
15
40
65
16
I have a 12-D array and am using each dimension as an index value in an optimization problem.
A(:,:,i1,i2,i3,i4,i5,i6,i7,i8,i9,i10)
each index value i is a value from 1 to 5.
I want to sort A from greatest to least and keep track of the indices so I know which indices correspond to to what value of A.
So my ideal output would be a 2 column cell/array with one column being the value and the other other column being the index values.
For a simple 3D example: say I have a 3D array: A(:,:,i1).
Where:
A(:,:,1) = 2
A(:,:,2) = 6
A(:,:,3) = 13
A(:,:,4) = 11
A(:,:,5) = 5
I would like my output to be:
13 3
11 4
6 2
5 5
2 1
EDIT:
assume I have 1x1x3x3 sized input such that
A(1,1,1,1) = 3
A(1,1,2,1) = 1
A(1,1,3,1) = 23
A(1,1,1,2) = 12
A(1,1,2,2) = 9
A(1,1,3,2) = 8
A(1,1,1,3) = 33
A(1,1,2,3) = 14
A(1,1,3,3) = 6
the expected output would be:
33 [1,1,1,3]
23 [1,1,3,1]
14 [1,1,2,3]
12 [1,1,1,2]
9 [1,1,2,2]
8 [1,1,3,2]
6 [1,1,3,3]
3 [1,1,1,1]
1 [1,1,2,1]
This should be a generic code for any multi-dimensional input array -
%// Sort A and get the indices
[sorted_vals,sorted_idx] = sort(A(:),'descend');
%// Set storage for indices as a cell array and then store sorted indices into it
c = cell([1 numel(size(A))]);
[c{:}] = ind2sub(size(A),sorted_idx);
%// Convert c to the requested format and concatenate with cell arary version of
%// sorted values for the desired output
out = [num2cell(sorted_vals) mat2cell([c{:}],ones(1,numel(A)),numel(size(A)))];
The generic code owes its gratitude to this fine solution.
I guess this is what you want:
b=A(:);
[sorted_b,ind]=sort(b,'descend');
[dim1,dim2,dim3,dim4]=ind2sub(size(A),ind);
%arranging in the form you want
yourCell=cell(size(b,1),2);
yourCell(:,1)=mat2cell(sorted_b,ones(size(b,1),1),1);
%arranging indices -> maybe vectorized way is there for putting values in "yourCell"
for i=1:size(b,1)
yourCell{i,2}=[dim1(i) dim2(i) dim3(i) dim4(i)];
end
For the array A, given by you, my output looks like:
33 [1,1,1,3]
23 [1,1,3,1]
14 [1,1,2,3]
12 [1,1,1,2]
9 [1,1,2,2]
8 [1,1,3,2]
6 [1,1,3,3]
3 [1,1,1,1]
1 [1,1,2,1]
which matches with your output.
I am considering an easy algorithm to rank my 2D array and mark their rank in the same size of the 2D array.
For example, I have a matrix in below:
[0 2 15 34;
0 15 21 24;
0 3 5 8;
1 14 23 29]
The output should be as follow:
[1 5 10 16;
1 10 12 14;
1 6 7 8;
4 9 13 15]
I am kind of new to matlab, I not sure if the matlab have the functionality to directly do it. Or it would be even better if you could provide some ideas for implementing the algorithm. Thank you very much!
If I understand correctly, you want to replace each element by its rank. I offer three ways to do it; the third seems to be what you want.
Let your example data be defined as
data = [0 2 15 34;
0 15 21 24;
0 3 5 8;
1 14 23 29];
This assigns equal ranks to equal data values (as in your example), but doesn't skip ranks in that case (your example seems to do so):
[~, ~, vv] = unique(data(:));
result = reshape(vv, size(data));
With your example data, this gives
result =
1 3 8 13
1 8 9 11
1 4 5 6
2 7 10 12
This assigns different ranks to equal data values (so skipping ranks is out of the question):
[~, vv] = sort(data(:));
[~, vv] = sort(vv);
result = reshape(vv, size(data));
With your example data,
result =
1 5 11 16
2 10 12 14
3 6 7 8
4 9 13 15
This assigns equal ranks to equal data values, and in that case it skips ranks:
[~, vv] = sort(data(:));
[~, vv] = sort(vv);
[~, jj, kk] = unique(data(:), 'first');
result = reshape(vv(jj(kk)), size(data));
With your example data,
result =
1 5 10 16
1 10 12 14
1 6 7 8
4 9 13 15
Another approach, single-line: for each entry, find how many other entries are smaller, and add 1:
result = reshape(sum(bsxfun(#lt,data(:),data(:).'))+1, size(data));