Is it necessary to make a for loop to make matricies of ones based on a matrix of dimensions? - matlab

I would like to generate a set of all 1 matrices given a matrix of sizes, (dimensions in this example), but I have been having a hard time making the dimensions matrix return a size vector that ones can work with.
My first instinct is: dimensions(:,:)
I read this (incorrectly) as return a matrix of size vectors [x,y]
But this doesn't seem to work -- Is there any way to use dimensions to produce a matrix of size vectors?
I am tempted to use a loop to iterate, for i = 1 to 3 dimensions(i,:) , but I was wondering if this is the only way.
Code:
clear;
%3x2
dimensions = [32,40; %32x40 box of ones
20, 30; %20x30 box of ones
60, 10; %60x10 box of ones
];
Onesboxes = ones(dimensions(1,:));
%this works, but I really want OnesBoxes to be an array such that:
%OnesBoxes(1) = 32x40 box of ones
%OnesBoxes(2) = 20x30 box of ones
%OnesBoxes(3) = 60x10 box of ones
% if I try:
OnesBoxes = ones(dimensions);
%Error using ones: Size vector should be a row vector with real elements.
%what I want to do is pass in sizes as rows in dimensions
%passing in the size of the ones array as a single vector works:
%onessize dimensions: 1x2
onessize = [4,2];
%tTestOnes dimensions: 4x2
tTestOnes = ones(onessize);
%making dimensions a 2x3 matrix instead doesn't seem to make a difference
%(I was thinking that maybe matlab thinks of matricies as an array of
%columns instead of arrays of rows?)
%dimensions2 = [32,20,60; 40,30,10];
%tOnesBoxes2 = ones(dimensions2);
EDIT: Outputs:
Output of
onessize = [4,2];
%tTestOnes dimensions: 4x2
tTestOnes = ones(onessize);
is a 4x2 array of all ones
Output of
dimensions = [32,40; %32x40 box of ones
20, 30; %20x30 box of ones
60, 10; %60x10 box of ones
];
Onesboxes = ones(dimensions(1,:));
Is a 32x40 array of all ones
Output of
dimensions = [32,40; %32x40 box of ones
20, 30; %20x30 box of ones
60, 10; %60x10 box of ones
];
OnesBoxes = ones(dimensions);
Is an error
Error using ones Size vector should be a row vector with real
elements.

If you are okay with having a for loop, the simplest way I can think of is:
dimensions = [32,40; %32x40 box of ones
20, 30; %20x30 box of ones
60, 10; %60x10 box of ones
];
for i=1:size(dimensions,1)
OnesBoxes{i}=ones(dimensions(i,:));
end
This will create OnesBoxes like how you wanted like this:
OnesBoxes{1}% = 32x40 box of ones
OnesBoxes{2}% = 20x30 box of ones
OnesBoxes{3}% = 60x10 box of ones

You need to pass ones a vector that defines the matrix size. If you want several matrices, you need to call ones several times. To collect all resulting matrices you need a cell array because the matrices have different sizes.
You can do that using a for loop, or arrayfun, or cellfun. Here I'm using the latter:
dimensions = [ 2 3;
4 5 ]; %// input data. Each row defines a matrix size
dimCell = mat2cell(dimensions, ones(1,size(dimensions,1)), size(dimensions,2));
%// split each row into a cell, ready to be used as input for `cellfun`
result = cellfun(#(x) ones(x), dimCell, 'uniformoutput', false);
This gives a cell array of the desired matrices. In the example,
>> result
result =
[2x3 double]
[4x5 double]
>> celldisp(result)
result{1} =
1 1 1
1 1 1
result{2} =
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1

Related

How do I split a very large matrix into submatrices based on the value in a column?

I have a 5 x 600,000 matrix. I've had an idea to group the data so I want to group this matrix into submatrices based on the values in column 4.
For values between 0 and 500, I want one matrix, for values between 501 and 1000 I want another, and for values between 1001 and 1500 I want another.
How can I do this?
I currently don't have any reliable material, I have seen some examples online but they only seem to feature 2 variables (i.e. with value 1 or 0 in a column and grouping the 1s and the 0s into 2 submatrices).
I think in Matlab-speak you mean you have an nxm matrix where n=600000, m=5, but if not you can change accordingly.
Is this what you were looking to do?
n=600000;
m=5;
thisCol =4;
values_range = {[0,500];[501,1000];[1001,1500]}; % cell array of vectors
myMatrix = zeros(n,m);
myMatrix(:,thisCol) = 1:600000; % to prove it works.
theseSubMatrices = cell(length(values_range),1); % cell array of matrices
for j=1:length(values_range)
thisLow= values_range{j}(1);
thisHigh= values_range{j}(2);
theseSubMatrices{j} = myMatrix(myMatrix(:,thisCol)>=thisLow & myMatrix(:,thisCol)<=thisHigh,:);
end
If you have some data
arr = rand( 6e5, 5 ); % 5 columns / 600,000 rows
arr(:,5) = arr(:,5) .* 1500; % for this example, get column 5 into range [0,1500]
Then you can use histcounts to "bin" the 5th column according to your edges.
edges = [0, 500, 1000, 1500]; % edges to split column 5 by
[~,~,iSubArr] = histcounts( arr(:,5), edges );
And generate a cell array with one element per sub array
nSubArr = numel(edges)-1; % number of bins / subarrays
subArrs = arrayfun( #(x) arr( iSubArr == x, : ), 1:nSubArr, 'uni', 0 ); % Get a matrix per bin
Output:
subArrs =
1×3 cell array
{200521×5 double} {199924×5 double} {199555×5 double}

Average matrices in a cell array within a structure Matlab

In Matlab I have a structure AVG of size 1 x 6 which has one field averageNEST that is a cell array also of size 1 x 6.
Each averageNEST contains matrices of varying sizes (in one dimension), so for example
AVG(1).averageNEST{1,1} is of size 281 x 3 x 19 and
AVG(1).averageNEST{1,2} is of size 231 x 3 x 19
The 2nd and 3rd dimensions of the matrices are always 3 and 19, it is only the first dimension that can change.
I want to average over all the matrices contained within AVG(1).averageNEST and obtain one matrix of size X x 3 x 19 where X is the size of the smallest matrix in AVG(1).averageNEST.
Then I want to do this for all 6 averageNEST in AVG - so have a separate averaged matrix for AVG(1), AVG(2) ... AVG(6).
I have tried multiple things including trying to concatenate matrices using the following code:
for i=1:6
min_epoch = epoch + 1;
for ii=1:19
averageNEST(:,:,ii) = [AVG(i).averageNEST(1:min_epoch,:,ii)];
end
end
and then average this but it doesn't work and now I'm really confused about what I'm doing!
Can anyone help?
I am not sure if I understand what you want to do. If you want to keep only the elements up to the size of the the smallest matrix and then average those matrices you can do the following:
averageNEST = cell(size(AVG));
for iAVG = 1:numel(AVG)
nests = AVG(iAVG).averageNEST;
minsize = min(cellfun(#(x) size(x,1), nests));
reducednests = cellfun(#(y) y(1:minsize, :, :), nests, 'UniformOutput', false);
averageNEST{iAVG} = sum(cat(4, reducednests{:}), 4) / numel(nests);
end

Indices of constant consecutive values in a matrix, and number of constant values

I have a matrix with constant consecutive values randomly distributed throughout the matrix. I want the indices of the consecutive values, and further, I want a matrix of the same size as the original matrix, where the number of consecutive values are stored in the indices of the consecutive values. For Example
original_matrix = [1 1 1;2 2 3; 1 2 3];
output_matrix = [3 3 3;2 2 0;0 0 0];
I have struggled mightily to find a solution to this problem. It has relevance for meteorological data quality control. For example, if I have a matrix of temperature data from a number of sensors, and I want to know what days had constant consecutive values, and how many days were constant, so I can then flag the data as possibly faulty.
temperature matrix is number of days x number of stations and I want an output matrix that is also number of days x number of stations, where the consecutive values are flagged as described above.
If you have a solution to that, please provide! Thank you.
For this kind of problems, I made my own utility function runlength:
function RL = runlength(M)
% calculates length of runs of consecutive equal items along columns of M
% work along columns, so that you can use linear indexing
% find locations where items change along column
jumps = diff(M) ~= 0;
% add implicit jumps at start and end
ncol = size(jumps, 2);
jumps = [true(1, ncol); jumps; true(1, ncol)];
% find linear indices of starts and stops of runs
ijump = find(jumps);
nrow = size(jumps, 1);
istart = ijump(rem(ijump, nrow) ~= 0); % remove fake starts in last row
istop = ijump(rem(ijump, nrow) ~= 1); % remove fake stops in first row
rl = istop - istart;
assert(sum(rl) == numel(M))
% make matrix of 'derivative' of runlength
% don't need last row, but needs same size as jumps for indices to be valid
dRL = zeros(size(jumps));
dRL(istart) = rl;
dRL(istop) = dRL(istop) - rl;
% remove last row and 'integrate' to get runlength
RL = cumsum(dRL(1:end-1,:));
It only works along columns since it uses linear indexing. Since you want do something similar along rows, you need to transpose back and forth, so you could use it for your case like so:
>> original = [1 1 1;2 2 3; 1 2 3];
>> original = original.'; % transpose, since runlength works along columns
>> output = runlength(original);
>> output = output.'; % transpose back
>> output(output == 1) = 0; % see hitzg's comment
>> output
output =
3 3 3
2 2 0
0 0 0

Get the neighbors of a matrix element

I have a matrix and for each element I want to get the index of its surrounding elements. All these results have to be stored into a matrix in the following way. Each row of the matrix corresponds to a matrix element and each of the columns of this matrix contain s the neighbor indexes. For example, for a 4x4 matrix we will get a 16x8 result array. Some of the matrix elements do not have 8 neighbors.
There is an example, I think it is working, I there any way to avoid for loop?:
ElementNeighbors = [];
for n = 1:numel(Matrix)
NeighborsMask = [ n-1 n+1 n+size(matrix,1) n-size(Matrix,1) n-size(Matrix,1)-1 n-size(Matrix,1)+1 ...
n+size(Matrix,1)-1 n+size(Matrix,1)+1 ];
ElementNeighbors = [ElementNeighbors ; NeighborsMask ];
end
ElementNeighbors (ElementNeighbors ==0|ElementNeighbors <0) = NaN;
Given the linear indices of a matrix M(n,m), you can convince yourself that the top left neighbor of element M(i,j) = M(i-1, j-1) = M(i-1 + n * (j-2))
In "linear index" space that means the offset of this element is
-n-1
Doing this for all other locations, we find
-n-1 | -1 | n-1
-n | x | n => [-n-1, -n, -n+1, -1, +1, +n-1, +n, +n+1]
-n+1 | +1 | n+1
Thus you can create a vector offset with the above values (replacing n with the first dimension). For example, if M is (5x4), then
offset = [-6 -5 -4 -1 1 4 5 6];
You then create all the indices:
indices = bsxfun(#plus, (1:m*n), offset(:));
bsxfun is a cool shorthand for "do this function on these elements; where one element has a singleton dimension and the other doesn't, expand accordingly". You could do the same with repmat, but that creates unnecessary intermediate matrices (which can sometimes be very large).
That command will create a (8 x m*n) matrix of indices of all 8 neighbors, including ones that may not really be the neighbors... something you need to fix.
Several possible approaches:
pad the matrix before you start
don't care about wrapping, and just get rid of the elements that fall off the edge
create a mask for all the ones that are "off the edge".
I prefer the latter. "Off the edge" means:
going up in the top row
going left in the left column
going down in the bottom row
going right in the right column
In each of these four cases there are 3 indices that are 'invalid'. Their position in the above matrix can be determined as follows:
mask = zeros(size(M));
mask(:,1) = 1;
left = find(mask == 1);
mask(:,end) = 2;
right = find(mask == 2);
mask(1,:) = 3;
top = find(mask == 3);
mask(end,:) = 4;
bottom = find(mask == 4);
edgeMask = ones(8,m*n);
edgeMask(1:3, top) = 0;
edgeMask([1 4 6], left) = 0;
edgeMask([3 5 8], right) = 0;
edgeMask(6:8, bottom) = 0;
Now you have everything you need - all the indices, and the "invalid" ones. Without loops.
If you were feeling ambitious you could turn this into a cell array but it will be slower than using the full array + mask. For example if you want to find the average of all the neighbors of a value, you can do
meanNeighbor = reshape(sum(M(indices).*edgeMask, 1)./sum(edgeMask, 1), size(M));
EDIT re-reading your question I see you wanted a M*N, 8 dimension. My code is transposed. I'm sure you can figure out how to adapt it...
ATTRIBUTION #Tin helpfully suggested many great edits to the above post, but they were rejected in the review process. I cannot totally undo that injustice - but would like to record my thanks here.
EXTENDING TO DIFFERENT REGIONS AND MULTIPLE DIMENSIONS
If you have an N-dimensional image matrix M, you could find the neighbors as follows:
temp = zeros(size(M));
temp(1:3,1:3,1:3) = 1;
temp(2,2,2) = 2;
offsets = find(temp==1) - find(temp==2);
If you want a region that is a certain radius in size, you could do
sz = size(M);
[xx yy zz] = meshgrid(1:sz(1), 1:sz(2), 1:sz(3));
center = round(sz/2);
rr = sqrt((xx - center(1)).^2 + (yy - center(2)).^2 + (zz - center(3)).^2);
offsets = find(rr < radius) - find(rr < 0.001);
You can probably figure out how to deal with the problem of edges along the lines shown earlier for the 2D case.
Untested - please see if you notice any problems with the above.

Sum every n rows of matrix

Is there any way that I can sum up columns values for each group of three rows in a matrix?
I can sum three rows up in a manual way.
For example
% matrix is the one I wanna store the new data.
% data is the original dataset.
matrix(1,1:end) = sum(data(1:3, 1:end))
matrix(2,1:end) = sum(data(4:6, 1:end))
...
But if the dataset is huge, this wouldn't work.
Is there any way to do this automatically without loops?
Here are four other ways:
The obligatory for-loop:
% for-loop over each three rows
matrix = zeros(size(data,1)/3, size(data,2));
counter = 1;
for i=1:3:size(data,1)
matrix(counter,:) = sum(data(i:i+3-1,:));
counter = counter + 1;
end
Using mat2cell for tiling:
% divide each three rows into a cell
matrix = mat2cell(data, ones(1,size(data,1)/3)*3);
% compute the sum of rows in each cell
matrix = cell2mat(cellfun(#sum, matrix, 'UniformOutput',false));
Using third dimension (based on this):
% put each three row into a separate 3rd dimension slice
matrix = permute(reshape(data', [], 3, size(data,1)/3), [2 1 3]);
% sum rows, and put back together
matrix = permute(sum(matrix), [3 2 1]);
Using accumarray:
% build array of group indices [1,1,1,2,2,2,3,3,3,...]
idx = floor(((1:size(data,1))' - 1)/3) + 1;
% use it to accumulate rows (appliead to each column separately)
matrix = cell2mat(arrayfun(#(i)accumarray(idx,data(:,i)), 1:size(data,2), ...
'UniformOutput',false));
Of course all the solution so far assume that the number of rows is evenly divisble by 3.
This one-liner reshapes so that all the values needed for a particular cell are in a column, does the sum, and then reshapes the back to the expected shape.
reshape(sum(reshape(data, 3, [])), [], size(data, 2))
The naked 3 could be changed if you want to sum a different number of rows together. It's on you to make sure the number of rows in each group divides evenly.
Slice the matrix into three pieces and add them together:
matrix = data(1:3:end, :) + data(2:3:end, :) + data(3:3:end, :);
This will give an error if size(data,1) is not a multiple of three, since the three pieces wouldn't be the same size. If appropriate to your data, you might work around that by truncating data, or appending some zeros to the end.
You could also do something fancy with reshape and 3D arrays. But I would prefer the above (unless you need to replace 3 with a variable...)
Prashant answered nicely before but I would have a simple amendment:
fl = filterLength;
A = yourVector (where mod(A,fl)==0)
sum(reshape(A,fl,[]),1).'/fl;
There is the ",1" that makes the line run even when fl==1 (original values).
I discovered this while running it in a for loop like so:
... read A ...
% Plot data
hold on;
averageFactors = [1 3 10 30 100 300 1000];
colors = hsv(length(averageFactors));
clear legendTxt;
for i=1:length(averageFactors)
% ------ FILTERING ----------
clear Atrunc;
clear ttrunc;
clear B;
fl = averageFactors(i); % filter length
Atrunc = A(1:L-mod(L,fl),:);
ttrunc = t(1:L-mod(L,fl),:);
B = sum(reshape(Atrunc,fl,[]),1).'/fl;
tB = sum(reshape(ttrunc,fl,[]),1).'/fl;
length(B)
plot(tB,B,'color',colors(i,:) )
%kbhit ()
endfor