I have a 2D matrix where the № of columns is always a multiple of 3 (e.g. 250×27) - due to a repeating organisation of the results (A,B,C, A,B,C, A,B,C, and so forth). I wish to reshape this matrix to create a new matrix with 3 columns - each containing the aggregated data for each type (A,B,C) (e.g. 2250×3).
So in a matrix of 250×27, all the data in columns 1,4,7,10,13,16,19,22,25 would be merged to form the first column of the resulting reshaped matrix.
The second column in the resulting reshaped matrix would contain all the data from columns 2,5,8,11,14,17,20,23,26 - and so forth.
Is there a simple way to do this in MATLAB? I only know how to use reshape if the columns I wanted to merge were adjacent (1,2,3,4,5,6) rather than non-adjacent (1,4,7,10,13,16) etc.
Shameless steal from #Divakar:
B = reshape( permute( reshape(A,size(A,1),3,[]), [1,3,2]), [], 3 );
Let A be your matrix. You can save every third column in one matrix like:
(Note that you don't have to save them as matrices separately but it makes this example easier to read).
A = rand(27); %as test
B = A(:,1:3:end);
C = A(:,2:3:end);
D = A(:,3:3:end);
Then you use reshape:
B = reshape(B,[],1);
C = reshape(C,[],1);
D = reshape(D,[],1);
And finally put it all together:
A = [B C D];
You can just treat every set of columns as a single item and do three reshapes together. This should do the trick:
[save as "reshape3.m" file in your Matlab folder to call it as a function]
function out = reshape3(in)
[~,C]=size(in); % determine number of columns
if mod(C,3) ~=0
error('ERROR: Number of rows must be a multiple of 3')
end
R_out=numel(in)/3; % number of rows in output
% Reshape columns 1,4,7 together as new column 1, column 2,5,8 as new col 2 and so on
out=[reshape(in(:,1:3:end),R_out,1), ...
reshape(in(:,2:3:end),R_out,1), ...
reshape(in(:,3:3:end),R_out,1)];
end
Lets suppose you have a 3x6 matrix A
A = [1 2 3 4 5 6;6 5 4 3 2 1;2 3 4 5 6 7]
A =
1 2 3 4 5 6
6 5 4 3 2 1
2 3 4 5 6 7
you extract the size of the matrix
b =size(A)
and then extract each third column for a single row
c1 = A((1:b(1)),[1:3:b(2)])
c2 = A((1:b(1)),[2:3:b(2)])
c3 = A((1:b(1)),[3:3:b(2)])
and put them in one matrix
A_result = [c1(:) c2(:) c3(:)]
A_result =
1 2 3
6 5 4
2 3 4
4 5 6
3 2 1
5 6 7
My 2 cents:
nRows = size(matrix, 1);
nBlocks = size(matrix, 2) / 3;
matrix = reshape(matrix, [nRows 3 nBlocks]);
matrix = permute(matrix, [1 3 2]);
matrix = reshape(matrix, [nRows * nBlocks 1 3]);
matrix = reshape(matrix(:), [nRows * nBlocks 3]);
Here's my 2 minute take on it:
rv = #(x) x(:);
ind = 1:3:size(A,2);
B = [rv(A(:,ind)) rv(A(:,ind+1)) rv(A(:,ind+2))];
saves a few ugly reshapes, may be a bit slower though.
If you have the Image Processing Toolbox, im2col is a very handy solution:
out = im2col(A,[1 4], 'distinct').'
Try Matlab function mat2cell, I think this form is allowed.
X is the "start matrix"
C = mat2cell(X, [n], [3, 3, 3]); %n is the number of rows, repeat "3" as many times as you nedd
%extract every matrix
C1 = C{1,1}; %first group of 3 columns
C2 = C{1,2}; %second group of 3 columns
%repeat for all your groups
%join the matrix with vertcat
Cnew = vertcat(C1,C2,C3); %join as many matrix n-by-3 as you have
I've two matrix a and b and I'd like to combine the rows in a way that in the first row I got no duplicate value and in the second value, columns in a & b which have the same row value get added together in new matrix. i.e.
a =
1 2 3
8 2 5
b =
1 2 5 7
2 4 6 1
Desired outputc =
1 2 3 5 7
10 6 5 6 1
Any help is welcomed,please.
For two-row matrices
You want to add second-row values corresponding to the same first-row value. This is a typical use of unique and accumarray:
[ii, ~, kk] = unique([a(1,:) b(1,:)]);
result = [ ii; accumarray(kk(:), [a(2,:) b(2,:)].').'];
General case
If you need to accumulate columns with an arbitrary number of columns (based on the first-row value), you can use sparse as follows:
[ii, ~, kk] = unique([a(1,:) b(1,:)]);
r = repmat((1:size(a,1)-1).', 1, numel(kk));
c = repmat(kk.', size(a,1)-1, 1);
result = [ii; full(sparse(r,c,[a(2:end,:) b(2:end,:)]))];
I have a cell of length n where each number is a numeric array of varying length.
eg
C = { [ 1 2 3] ; [ 4 1 ] ; [ 28 5 15] }
And a 4xn numeric array
eg
A = [[ 1 2 3 4] ; [ 5 6 7 8 ] ; [ 9 10 11 12]]
I'd like to filter the numeric array A based on the content in cell C.
The filter may be to return all rows in A which have a 28 in the corresponding element in C.
ans = [ 9 10 11 12 ]
Or, the filter may be to return all rows in A which have a 1 in the first column of C or a 5 in the second column of C.
ans = [[ 1 2 3 4] ; [ 9 10 11 12]]
Hope this makes sense! It's the correlation the vectors in the cell to the main array which I'm struggling with
Cellfun makes this relatively straightforward - design the function that returns a logical vector matching your filter requirements (i.e., it maps each vector in C to a single logical scalar depending on the conditions), and make this the first input to cellfun. Your cell array is the second input. The output of this will be your nx1 "filter" vector. Then apply this along the dimension of A that has length n, and use a colon operator in the other dimension.
First one:
A(cellfun(#(x) ismember(28, x), C), :);
ans =
9 10 11 12
Second one:
A(cellfun(#(x) (x(1)==1) || (x(2)==5), C), :)
ans =
1 2 3 4
9 10 11 12
I am looking for non-repeated rows in a matrix.
Assume:
A =
8 1
2 2
2 2
2 2
2 2
3 6
5 7
5 7
I would like to get "B" which is:
B=
8 1
3 6
Please mind C=unique(A,'rows') will give us unique rows of "A" which include repeated and non-repeated arrays and only remove repetitious rows. It means:
C =
2 2
3 6
5 7
8 1
"C" is not the one that I am looking for.
Any help would be greatly appreciated!
Use the second and third outputs of unique as follows:
[~, ii, jj] = unique(A,'rows');
kk = find(histc(jj,unique(jj))==1);
B = A(sort(ii(kk)),:);
Or use this more direct bsxfun-based approach:
B = A(sum(squeeze(all(bsxfun(#eq, A.', permute(A, [2 3 1])))))==1,:);
These two approaches work in quite generally: A may have any number of columns, and may contain non-integer values.
If A always has two columns and contains only integer values, you can also do it with accumarray, using the sparse option (sixth input argument) to save memory in case of large values:
[ii jj] = find(accumarray(A, 1, [], #sum, 0, true)==1);
B = [ii jj];
Or you can use sparse instead of accumarray:
[ii jj] = find(sparse(A(:,1),A(:,2),1)==1);
B = [ii jj];
If you don't care about the order of the rows, try this -
[C,~,ic] = unique(A,'rows','legacy')
B = C(histc(ic,unique(ic))==1,:)
I wish to locate the first position of each unique number from a vector but without a for loop:
e.g
a=[1 1 2 2 3 4 2 1 3 4];
and I can obtain the unique number by having:
uniq=unique(a);
where uniq = [1 2 3 4]
What I want is to obtain each number's first appearance location, any ideas????
first_pos = [1 3 5 6]
where 1 is firstly appear in position 1, 4 is firstly appear in the sixth position from the vector
ALSO, what about the position of the second appearance??
second_pos = [2 4 9 10]
Thank you very much
Use the second output of unique, and use the 'first' option:
>> A = [1 1 2 2 3 4 2 1 3 4];
>> [a,b] = unique(A, 'first')
a =
1 2 3 4 %// the unique values
b =
1 3 5 6 %// the first indices where these values occur
To find the locations of the second occurrences,
%// replace first occurrences with some random number
R = rand;
%// and do the same as before
A(b) = R;
[a2,b2] = unique(A, 'first');
%// Our random number is NOT part of original vector
b2(a2==R)=[];
a2(a2==R)=[];
with this:
b2 =
2 4 9 10
Note that there will have to be at least 2 occurrences of each number in the vector A if the sizes of b and b2 are to agree (this was not the case before your edit).