In MatLab, I have a matrix SimC which has dimension 22 x 4. I re-generate this matrix 10 times using a for loop.
I want to end up with a matrix U that contains SimC(1) in rows 1 to 22, SimC(2) in rows 23 to 45 and so on. Hence U should have dimension 220 x 4 in the end.
Thank you!!
Edit:
nTrials = 10;
n = 22;
U = zeros(nTrials * n , 4) %Dimension of the final output matrix
for i = 1 : nTrials
SimC = SomeSimulation() %This generates an nx4 matrix
U = vertcat(SimC)
end
Unfortunately the above doesn't work as U = vertcat(SimC) only gives back SimC instead of concatenating.
vertcat is a good choice, but it will result in a growing matrix. This is not good practice on larger programs because it can really slow down. In your problem, though, you aren't looping through too many times, so vertcat is fine.
To use vertcat, you would NOT pre-allocate the full final size of the U matrix...just create an empty U. Then, when invoking vertcat, you need to give it both matrices that you want to concatenate:
nTrials = 10;
n = 22;
U = [] %create an empty output matrix
for i = 1 : nTrials
SimC = SomeSimulation(); %This generates an nx4 matrix
U = vertcat(U,SimC); %concatenate the two matrices
end
The better way to do this, since you already know the final size, is to pre-allocate your full U (as you did) and then put your values into U via computing the correct indices. Something like this:
nTrials = 10;
n = 22;
U = U = zeros(nTrials * n , 4); %create a full output matrix
for i = 1 : nTrials
SimC = SomeSimulation(); %This generates an nx4 matrix
indices = (i-1)*n+[1:n]; %here are the rows where you want to put the latest output
U(indices,:)=SimC; %copies SimC into the correct rows of U
end
Related
I wish to fill a N x M x W matrix ‘S’ with the data from matrices ‘P’ and ‘Q’. They are defined below and illustrated in the attached image. Also, we know for sure that n_1 + n_2 = N, m < M, so all the data may fit in the ‘S’ matrix.
S = zeros(M,N,W);
P = rand(m,n_1,W);
Q = rand(m,n_2,W);
I wish to combine ‘P’ and ‘Q’ in a manner specified by 3 other matrices, ‘Line_num’, ‘P_col’ and ‘Q_col’, described below and in the middle part of the attached image.
P_col = randperm(N); P_col = P_col(1:n_1); % 1 x n_1 matrix
Q_col = setxor(P_col, 1:1:N); % 1 x n_2 matrix
Line_num is a matrix composed of W vectors of the form aa:1:bb, where bb-aa = m and aa is chosen at random for each vector.
The important thing is that in this case the data along the 3rd dimension in all these matrixes represent W different test cases, with the data being different and not to be mixed between each other.
To fill ‘S’ one may proceed in two logical steps (although if it can be done in one I shall be glad)
combine Q and P into an intermediate matrix Y of shape m x N x W by
interweaving their columns. The columns specified in ‘Q_col’ are
taken from Q (using the vector index) and put in the matrix Y (using
the vector value). The same goes for P.
For each of the W vectors composing Line_num and arrays composing S,
use the values in the vector Line_num to spread out Y to the
corresponding rows in S, meanwhile maintaining their top to bottom
order.
I wish to achieve this without for-loops as I am looking to ‘vectorize’ my code and thus improve its running speed.
I have had a look at this post and this post, which are similar to what I desire. However they are simpler as the numbers to be extracted are constant. Maybe something similar would be appropriate?
Thank you for your help :)
Link to the image aforementioned
EDIT: here is an example code with a for-loop of what I want (my problem is that I want to get rid of the loop)
W = 4;
N = 10; n_1 = 4; n_2 = 6;
M = 20; m = 5;
P_col = [1,3,5,8]; % 1 x n_1 matrix
Q_col = setxor(P_col, 1:1:N); % 1 x n_2 matrix
line_num(:,:,1) = [1,5,10,15,18];
line_num(:,:,2) = [2,3,8,11,12];
line_num(:,:,3) = [4,7,8,14,19];
line_num(:,:,4) = [2,6,13,15,16];
S = zeros(M,N,W);
P = rand(m,n_1,W);
Q = rand(m,n_2,W);
for w=1:W
line_num_I = line_num(:,:,w);
S(line_num_I,Q_col,w) = Q(:,:,w);
S(line_num_I,P_col,w) = P(:,:,w);
end
Here is a vectorized solution. I'm not sure if it is more efficient than loop version specially when the size of data is large.
S ( reshape(line_num,[],1,W) ...
+ ([Q_col-1 P_col-1]) * M ...
+ (reshape(0:W-1,1,1,[]))*M*N ...
) ...
= ...
[reshape(Q,[],W);reshape(P,[],W)];
Here implicit expansion is used to convert subscripts to indices. Equivalently bsxfun can be used to compute linear indices:
S ( ...
bsxfun(#plus, ...
reshape(line_num,[],1,W), ...
bsxfun (#plus, ...
([Q_col-1 P_col-1]) * M, ...
(reshape(0:W-1,1,1,[]))*M*N ...
) ...
) ...
) ...
= ...
[reshape(Q,[],W);reshape(P,[],W)];
*Here You can find how to convert 3D index to lindex.
So I ended up finding the answer. For those of you that it may interest, the above for-loop may be replaced by
% 1. Combine columns
mixed_col = zeros(m,N,W);
mixed_col(:,Q_col,:) = Q(:,:,:);
mixed_col(:,P_col,:) = P(:,:,:);
mixed_col = permute(mixed_col,[2,1,3]); % turn 3D matrix into 2D [1]
mixed_col = reshape(mixed_col,N,[],1)';
% 2. Combine lines
S = reshape(S,M*w,N,1); % turn 3D matrix into 2D [2]
line_num_v = permute(line_num + reshape((0:1:(W-1)).*M,1,1,W),[2,1,3]); % turn 3D matrix into 2D [3]
line_num_v = reshape(line_num_v,[],1,1);
S(line_num_v,:) = mixed_col(:,:); % combine using three 2D matrices
S = permute(reshape(S',N,M,W),[2,1,3]);
This involves lots of reshaping but I don't have a simpler answer.
Thanks again for your help.
I've written a function that generates a sparse matrix of size nxd
and puts in each column 2 non-zero values.
function [M] = generateSparse(n,d)
M = sparse(d,n);
sz = size(M);
nnzs = 2;
val = ceil(rand(nnzs,n));
inds = zeros(nnzs,d);
for i=1:n
ind = randperm(d,nnzs);
inds(:,i) = ind;
end
points = (1:n);
nnzInds = zeros(nnzs,d);
for i=1:nnzs
nnzInd = sub2ind(sz, inds(i,:), points);
nnzInds(i,:) = nnzInd;
end
M(nnzInds) = val;
end
However, I'd like to be able to give the function another parameter num-nnz which will make it choose randomly num-nnz cells and put there 1.
I can't use sprand as it requires density and I need the number of non-zero entries to be in-dependable from the matrix size. And giving a density is basically dependable of the matrix size.
I am a bit confused on how to pick the indices and fill them... I did with a loop which is extremely costly and would appreciate help.
EDIT:
Everything has to be sparse. A big enough matrix will crash in memory if I don't do it in a sparse way.
You seem close!
You could pick num_nnz random (unique) integers between 1 and the number of elements in the matrix, then assign the value 1 to the indices in those elements.
To pick the random unique integers, use randperm. To get the number of elements in the matrix use numel.
M = sparse(d, n); % create dxn sparse matrix
num_nnz = 10; % number of non-zero elements
idx = randperm(numel(M), num_nnz); % get unique random indices
M(idx) = 1; % Assign 1 to those indices
Essentially I have an image mask and I want to find the width of the image in each column. Is there a way to vectorise this for speed? I tried to figure out a way with arrayfun but haven't hit on anything yet.
r = zeros(1,cols);
for i = 1 : cols
r(i) = range(find(img(:,i)));
end
The following code does the same as yours in a vectorized manner:
imglog = img~=0; %// convert to 0 and 1 values
[~, i1] = max(imglog); %// i1 is the position of the first 1
[~, i2] = max(flipud(imglog)); %// size(img,1)+1-i2 is the position of the last 1
r = size(img,1)+1-i2 - i1;
It exploits the fact that the second output of max gives the position of the first maximizer (for each column).
I am not sure whether this is faster, but num2cell + cellfun seems to be the only way to vectorize general functions on columns:
r = cellfun(#(x)range(find(x)),num2cell(img,1));
find + unique approach -
[row1,col1] = find(img);
[~,start1] = unique(col1,'first');
[~,stop1] = unique(col1);
r = row1(stop1) - row1(start1);
I need to make a scilab / MATLAB program that averages the values of a 3D matrix in cubes of a given size(N x N x N).I am eternally grateful to anyone who can help me.
Thanks in advance
In MATLAB, mat2cell and cellfun make a great team for working on N-dimensional non-overlapping blocks, as I think is the case in the question. An example scenario:
[IN]: A = [30x30x30] array
[IN]: bd = [5 5 5], size of cube
[OUT]: B = [6x6x6] array of block means
To accomplish the above, the solution is:
dims = [30 30 30]; bd = [5 5 5];
A = rand(dims);
f = floor(dims./bd);
remDims = mod(dims,bd); % handle dims that are not a multiple of block size
Ac = mat2cell(A,...
[bd(1)*ones(f(1),1); remDims(1)*ones(remDims(1)>0)], ....
[bd(2)*ones(f(2),1); remDims(2)*ones(remDims(2)>0)], ....
[bd(3)*ones(f(3),1); remDims(3)*ones(remDims(3)>0)] );
B = cellfun(#(x) mean(x(:)),Ac);
If you need a full size output with the mean values replicated, there is a straightforward solution involving the 'UniformOutput' option of cellfun followed by cell2mat.
If you want overlapping cubes and the same size output as input, you can simply do convn(A,ones(blockDims)/prod(blockDims),'same').
EDIT: Simplifications, clarity, generality and fixes.
N = 10; %Same as OP's parameter
M = 10*N;%The input matrix's size in each dimensiona, assumes M is an integer multiple of N
Mat = rand(M,M,M); % A random input matrix
avgs = zeros((M/N)^3,1); %Initializing output vector
l=1; %indexing
for i=1:M/N %indexing 1st coord
for j=1:M/N %indexing 2nd coord
for k=1:M/N % indexing third coord
temp = Mat((i-1)*N+1:i*N,(j-1)*N+1:j*N,(k-1)*N+1:k*N); %temporary copy
avg(l) = mean(temp(:)); %averaging operation on the N*N*N copy
l = l+1; %increment indexing
end
end
end
The for loops and copying can be eliminated once you get the gist of indexing.
Suppose I want to find the size of a matrix, but can't use any functions such as size, numel, and length. Are there any neat ways to do this? I can think of a few versions using loops, such as the one below, but is it possible to do this without loops?
function sz = find_size(m)
sz = [0, 0]
for ii = m' %' or m(1,:) (probably faster)
sz(1) = sz(1) + 1;
end
for ii = m %' or m(:,1)'
sz(2) = sz(2) + 1;
end
end
And for the record: This is not a homework, it's out of curiosity. Although the solutions to this question would never be useful in this context, it is possible that they provide new knowledge in terms of how certain functions/techniques can be used.
Here is a more generic solution
function sz = find_size(m)
sz = [];
m(f(end), f(end));
function r = f(e)
r=[];
sz=[sz e];
end
end
Which
Works for arrays, cell arrays and arrays of objects
Its time complexity is constant and independent of matrix size
Does not use any MATLAB functions
Is easy to adapt to higher dimensions
For non-empty matrices you can use:
sz = [sum(m(:,1)|1) sum(m(1,:)|1)];
But to cover empty matrices we need more function calls
sz = sqrt([sum(sum(m*m'|1)) sum(sum(m'*m|1))]);
or more lines
n=m&0;
n(end+1,end+1)=1;
[I,J]=find(n);
sz=[I,J]-1;
Which both work fine for m=zeros(0,0), m=zeros(0,10) and m=zeros(10,0).
Incremental indexing and a try-catch statement works:
function sz = find_size(m)
sz = [0 0];
isError = false;
while ~isError
try
b = m(sz(1) + 1, :);
sz(1) = sz(1) + 1;
catch
isError = true;
end
end
isError = false;
while ~isError
try
b = m(:, sz(2) + 1);
sz(2) = sz(2) + 1;
catch
isError = true;
end
end
end
A quite general solution is:
[ sum(~sum(m(:,[]),2)) sum(~sum(m([],:),1)) ]
It accepts empty matrices (with 0 columns, 0 rows, or both), as well as complex, NaN or inf values.
It is also very fast: for a 1000 × 1000 matrix it takes about 22 microseconds in my old laptop (a for loop with 1e5 repetitions takes 2.2 seconds, measured with tic, toc).
How this works:
The keys to handling empty matrices in a unified way are:
empty indexing (that is, indexing with []);
the fact that summing along an empty dimension gives zeros.
Let r and c be the (possibly zero) numbers of rows and columns of m. m(:,[]) is an r × 0 empty vector. This holds even if r or c are zero. In addition, this empty indexing automatically provides insensitivity to NaN, inf or complex values in m (and probably accounts for the small computation time as well).
Summing that r × 0 vector along its second dimension (sum(m(:,[]),2)) produces a vector of r × 1 zeros. Negating and summing this vector gives r.
The same procedure is applied for the number of columns, c, by empty-indexing in the first dimension and summing along that dimension.
The find command has a neat option to get the last K elements:
I = find(X,K,'last') returns at most the last K indices corresponding to the nonzero entries of the arrayX`.
To get the size, ask for the last k=1 elements. For example,
>> x=zeros(256,4);
>> [numRows,numCols] = find(x|x==0, 1, 'last')
numRows =
256
numCols =
4
>> numRows0 = size(x,1), numCols0 = size(x,2)
numRows0 =
256
numCols0 =
4
You can use find with the single output argument syntax, which will give you numel:
>> numEl = find(x|x==0, 1, 'last')
numEl =
1024
>> numEl0 = numel(x)
numEl0 =
1024
Another straightforward, but less interesting solution uses whos (thanks for the reminder Navan):
s=whos('x'); s.size
Finally, there is format debug.