MATLAB setting matrix values in an array - matlab

I'm trying to write some code to calculate a cumulative distribution function in matlab. When I try to actually put my results into an array it yells at me.
tempnum = ordered1(1);
k=2;
while(k<538)
count = 1;
while(ordered1(k)==tempnum)
count = count + 1;
k = k + 1;
end
if(ordered1(k)~=tempnum)
output = [output;[(count/537),tempnum]];
k = k + 1;
tempnum = ordered1(k);
end
end
The errors I'm getting look like this
??? Error using ==> vertcat
CAT arguments dimensions are not consistent.
Error in ==> lab8 at 1164
output = [output;[(count/537),tempnum]];
The line to add to the output matrice was given to me by my TA. He didn't teach us much syntax throughout the year so I'm not really sure what I'm doing wrong. Any help is greatly appreciated.

If you're building the matrix output from scratch, you should make sure it hasn't already been initialized to anything. To do this, you can set it to the empty matrix at the beginning of your code:
output = [];
Also, if you know how large output is going to be, your code will run more efficiently if you preallocate the array output and index into the array to assign values instead of appending values to it. In your case, output should have the same number of rows as there are unique values in the array ordered1, so you could use the function UNIQUE to preallocate output:
nRows = numel(unique(ordered1)); %# Get the number of unique values
output = zeros(nRows,2); %# Initialize output
You would then have to keep a separate counter (say r) to track which index into output you will be adding to next:
...
output(r,:) = [count/537 tempnum]; %# Overwrite a row in output
r = r+1; %# Increment the row index
...
Some additional advice...
Even if you solve the error you are getting, you're going to run into more with the code you have above:
I believe you are actually computing a probability density function (or PDF) with your code. In order to get the cumulative distribution function (or CDF), you have to perform a cumulative sum over the final values in the first column of output. You can do this with the function CUMSUM:
output(:,1) = cumsum(output(:,1));
Your loop will throw an error when it reaches the last element of ordered1. The value of k can become 538 in your inner while loop, which will then cause an error to be thrown when you try to access ordered1(k) anywhere. To get around this, you will have to add checks to the value of k at a number of points in your code. One such point is your inner while loop, which can be rewritten as:
while (k <= 537) && (ordered1(k) == tempnum)
count = count + 1;
k = k + 1;
end
This solution uses the short-circuit AND operator &&, which will first check if (k <= 537) is true or false. If it is false (i.e. k > 537), the second logical check is skipped since its result doesn't matter, and you avoid the error that would result from evaluating ordered1(k).
Bonus MATLAB coolness...
MATLAB has a lot of cool functions that can do a lot of the work for you. One such function is ACCUMARRAY. Your TA may want you to do things using loops like you have above, but you can actually reduce your whole code to just a few lines like so:
nValues = numel(ordered1); %# Get the number of values
p = accumarray(ordered1,ones(size(ordered1)))./nValues; %# Create a PDF
output = [cumsum(p) unique(ordered1)]; %# Create the CDF output

Related

MATLAB find on multiple columns to multiple column result

Setup
I have an array of captured data. The data may be captured on just 1 device or up to a dozen devices, with each device being a column in the array. I have a prior statement which I execute on the array to then turn it into a logical array to find particular points of interest in the data. Due to the nature of the data, there are many 0's and only a few 1's. I need to return an array with the indices of the 1's so I can go back and capture the data between those points (see update below).
find is an obvious choice for a function - however, the result I need, needs to have 1 column for each device. Normally find will do a linear index regardless of the dimensions of the array.
The devices follow a pattern - but aren't exactly the same. So, complicating this is the fact that the number of 1's in each column is close to, but not guaranteed to be exactly the same depending on the exact timing the data capture is stopped (they are most often different from each other by 1 element, but could be different by more).
MATLAB CODE ATTEMPTS
Because of that difference, I can't use the following simple code:
for p = 1:np
indices( :, p ) = find( device.data.cross( :, p ) );
end
Notes:
np is the number of columns in the data = number of devices captured.
devices is a class representing the collection of devices
data is a TimeTable containing captured data on all the devices
cross is a column in the data TimeTable which contains the logical array
Even this simple code is inefficient and generates the Code Analyzer warning:
The variable 'indices' appears to change size on every loop
iteration (within a script). Consider preallocating for speed.
As expected, it doesn't work as I get an error similar to the following:
Unable to perform assignment because the size of the left side is
448-by-1 and the size of the right side is 449-by-1.
I know why I get this error - each column in an array in MATLAB has to have the same number of rows, so I can't make the assignment if the row size doesn't match. I need to pad the "short" columns somehow. In this case, repeating the last index will work for my later operations without causing an error.
I can't figure out a "good" way to do this. I can't pre-populate the array rows because I don't know how many rows there will be until I've done the find operation.
I can change the code as follows:
indices = [];
for p = 1:np
tempindices = find( devices.data.cross(:, p) );
sizediff = size( tempindices, 1 ) - size( indices, 1 );
if p > 1
if sizediff > 0
padding = repmat(indices(end, 1:(p - 1)), sizediff, 1);
indices = [indices; padding];
elseif sizediff < 0
padding = repmat(tempindices(end), abs(sizediff), 1);
tempindices = [tempindices; padding];
end
end
indices(:,p) = tempindices;
end
Note: padarray would have been useful here, but I don't have the Image Processing Toolbox so I cannot use it.
This code works, but it is very inefficient, it creates multiple otherwise unneeded variables in the workspace and generates multiple "appears to change size on every loop iteration" warnings in Code Analyzer. Is there a more efficient way to do this?
Update / Additional Information:
Some more context is needed for my issue. Given that devices.data.cross is a logical array, to just "pick" the data I want from other columns in my table (as I originally described my problem) I could select a column from devices.data.cross and pass that logical column as a subscript to get that data. I do that where it works. However, for some of the columns I need to select "chunks" of the data between the indices and that's where (I think) I need the indices. Or, at least I don't know of another way to do it.
Here is example code of how I use the indices:
for p = 1:np
for i = 2:num_indices
these_indices = indices(i-1, p):( indices(i, p) - 1 );
rmsvoltage = sqrt( mean( devices.data.voltage(these_indices).^2 ) );
end
end
This is just one routine I do on the "chunks" of data. I also have a couple of functions where these chunks of data are passed for processing.
When I understood your problem correctly, the code below should work. I'm using the approach that Cris Luengo suggested in a comment under your question.
Key element is [rowIdcs, colIdcs] = find( cros ); which gives you the subscripts of positions in cros having a value of one. Please find further comments inline.
% Create some data for testing
volt = randn(10,10);
cros = randi(10,10,10) > 9;
% Get rowIdcs and colIdcs, which have both a size of Nx1,
% with N denoting the number of ones in the mask.
% rowIdcs and colIdcs are the subscripts of the ones in the mask.
[rowIdcs, colIdcs] = find( cros );
% The number of chunks is equal to number N of ones found in the mask;
numChunks = numel( rowIdcs );
% Initilize a vector for the rms
rms = zeros( numChunks, 1 );
% Loop over the chunks
for k = 1 : numChunks
curRow = rowIdcs(k);
curCol = colIdcs(k);
% Get indices of range over neighbouring rows
chunkRowIdcs = curRow + [-1 0 1]; %i.e. these_indices in your example
% Remove indices that are out of range
chunkRowIdcs( chunkRowIdcs < 1 | chunkRowIdcs > size(volt,1) ) = [];
% Get voltages covered by chunk
chunkVoltages = volt( chunkRowIdcs, curCol );
% Get RMS over voltages
rms(k) = sqrt( mean( chunkVoltages(:).^2 ));
end

Not sure what to do about error message "Conversion to double from cell is not possible."

I'm writing a program that finds the indices of a matrix G where there is only a single 1 for either a column index or a row index and removes any found index if it has a 1 for both the column and row index. Then I want to take these indices and use them as indices in an array U, which is where the trouble comes. The indices do not seem to be stored as integers and I'm not sure what they are being stored as or why. I'm quite new to Matlab (but thats probably obvious) and so I don't really understand how types work for Matlab or how they're assigned. So I'm not sure why I',m getting the error message mentioned in the title and I'm not sure what to do about it. Any assistance you can provide would be greatly appreciated.
I forgot to mention this before but G is a matrix that only contains 1s or 0s and U is an array of strings (i think what would be called a cell?)
function A = ISClinks(U, G)
B = [];
[rownum,colnum] = size(G);
j = 1;
for i=1:colnum
s = sum(G(:,i));
if s == 1
B(j,:) = i;
j = j + 1;
end
end
for i=1:rownum
s = sum(G(i,:));
if s == 1
if ismember(i, B)
B(B == i) = [];
else
B(j,:) = i;
j = j+1;
end
end
end
A = [];
for i=1:size(B,1)
s = B(i,:);
A(i,:) = U(s,:);
end
end
This is the problem code, but I'm not sure what's wrong with it.
A = [];
for i=1:size(B,1)
s = B(i,:);
A(i,:) = U(s,:);
end
Your program seems to be structured as though it had been written in a language like C. In MATLAB, you can usually substitute specialized functions (e.g. any() ) for low-level loops in many cases. Your function could be written more efficiently as:
function A = ISClinks(U, G)
% Find columns and rows that are set in the input
active_columns=any(G,1);
active_rows=any(G,2).';
% (Optional) Prevent columns and rows with same index from being simultaneously set
%exclusive_active_columns = active_columns & ~active_rows; %not needed; this line is only for illustrative purposes
%exclusive_active_rows = active_rows & ~active_columns; %same as above
% Merge column state vector and row state vector by XORing them
active_indices=xor(active_columns,active_rows);
% Select appropriate rows of matrix U
A=U(active_indices,:);
end
This function does not cause errors with the example input matrices I tested. If U is a cell array (e.g. U={'Lorem','ipsum'; 'dolor','sit'; 'amet','consectetur'}), then return value A will also be a cell array.

In an assignment A(I) = B, the number of elements in B and I must be the same

This is my code in Matlab: How could I get all values of all 5 images saved? This code only returns the last image! I tried using IM(l) but it gives me an error: In an assignment A(I) = B, the number of elements in B and I must be the same.
Amount_measurements = 5;
IM=zeros(2097152,1);
l=1;
for l=(1:Amount_measurements)
if l < 9
%index = double(0)+double(0)+double(l+1);
index = strcat(num2str(double(0)),num2str(double(0)),num2str(double(l+1)));
elseif l < 99
index = double(0)+double(l+1);
else
index = double(l+1);
end
file_name1='trial.nii.gz';
%disp(file_name1);
jesu=load_nii(file_name1);
[x,y,z] = meshgrid(1:256,1:256,1:256);
[lx,ly,lz] = meshgrid(1:2:256,1:2:256,1:2:256);
newImage = interp3(x,y,z,jesu.img,lx,ly,lz);
IM= newImage(:);
end
I want the values newImage(:) to be stored as IM1=newImage(:) IM2=newImage(:) IM3=newImage(:) IM4=newImage(:) so on... How could I go about with it?
Since you mentioned wanting a variable-length version of IM1=newImage(:) IM2=newImage(:) IM3=newImage(:) IM4=newImage(:), you're looking for a cell array. Try
IM{l} = newImage;
instead of
IM(l) = newImage(:);
The important difference is the use of braces rather than parentheses. Use a right-hand side ofnewImage(:) if you want to reshape into a vector, just newImage if you want to preserve it as a matrix.
By using IM(l) you're trying to add an entire column vector (newImage(:)) to a single element (the l-th element) in the array IM, that's why Matlab throws the error.
You should consider concatenation: since newImage(:) is a column-vector, replace
IM= newImage(:);
with
IM=[IM newImage(:)];
but at the top of the script you should also initialize IM as
IM=[];
At the end of the loop, the resulting IM will have Amount_measurements columns where 1 column = 1 newImage(:).
Note #1: this will only work if newImage(:) always has the same length.
Note #2: if you know a priori how long the vector newImage(:) is and, again, by assuming that its length never changes, you should consider preallocating the IM matrix by replacing IM=[]; with IM=zeros(X,Amount_measurements); where X is the number of elements in newImage(:). Finally, regarding the concatenation stage, you should replace IM=[IM newImage(:)]; with IM(:,l)=newImage(:).
Note #3: as instead, if the size of newImage(:) can change you cannot rely on preallocation and matrices, but you must use cell arrays: the last instruction in your loop should be IM{l}=newImage(:);.

output iteration results from a loop in Matlab

I am trying to use a for loop to make some calculations in a cell array but at the end only the results for the last loop are displayed. I would like Matlab to display the results for all loops. Here there is the code:
slope=[];
time=[];
position= [];
for p=1:max(L) % max L gives the number of result{n}. so if max(L)=6 we have from result{1} to result{6} and therefore 6 final values that i want to get%
a=result{n}(:,1);
b=result{n}(:,2);
end
B = [ones(length(a),1) a] \ b % this is to obtain the slope and intercept of a lin. regresion
slope = B(2)
time = result{n}(end,1)-result{n}(1:1)
position = (slope.*result{n}(end,1)+intercept)-(slope.*result{n}(1:1)+intercept)
At the moment in the output that is what i get:
slope =
4.4089
time =
0.5794
position =
2.5546
This result is correct. However, these values are the ones obtained with result{6} and i need the values previous to this one.
Any help is much appreciated !
Thanks in advance!
You are making a mess with the indexes… It is a bit hard to understand what you did on your code, but it may be something like this (pseudocode since the code you gave does not have the result declared):
slope=zeros(1,max(L)); % Pre allocate zeros, one index for each interation
time=zeros(1,max(L));
position=zeros(1,max(L));
a=zeros(1,max(L));
b=zeros(1,max(L));
for p=1:max(L) % max L gives the number of result{n}. so if max(L)=6 we have from result{1} to result{6} and therefore 6 final values that i want to get%
a(p)=result{p}(:,1);
b(p)=result{p}(:,2);
B = [ones(length(a( p ),1) a( p )] \ b( p) % this is to obtain the slope and intercept of a lin. regresion
slope( p) = B(2)
time( p) = result{p}(end,1)-result{p}(1:1)
position( p) = (slope( p ).*result{p}(end,1)+intercept)-(slope ( p) .*result{p}(1)+intercept)
end
position(6) will get your value, position(5) the previous value.
The easiest way to do this is to remove the ";" for lines that you want printed to the command window. This will display all loop values you need.
for p=1:max(L)
a=result{n}(:,1)
b=result{n}(:,2)
end
Can you do all calculations inside the loop and don't block with ";" instead. If you get a result after coming out of a loop, you will only get the last one.

Matlab: how to implement a dynamic vector

I am refering to an example like this
I have a function to analize the elements of a vector, 'input'. If these elements have a special property I store their values in a vector, 'output'.
The problem is that at the begging I don´t know the number of elements it will need to store in 'output'so I don´t know its size.
I have a loop, inside I go around the vector, 'input' through an index. When I consider special some element of this vector capture the values of 'input' and It be stored in a vector 'ouput' through a sentence like this:
For i=1:N %Where N denotes the number of elements of 'input'
...
output(j) = input(i);
...
end
The problem is that I get an Error if I don´t previously "declare" 'output'. I don´t like to "declare" 'output' before reach the loop as output = input, because it store values from input in which I am not interested and I should think some way to remove all values I stored it that don´t are relevant to me.
Does anyone illuminate me about this issue?
Thank you.
How complicated is the logic in the for loop?
If it's simple, something like this would work:
output = input ( logic==true )
Alternatively, if the logic is complicated and you're dealing with big vectors, I would preallocate a vector that stores whether to save an element or not. Here is some example code:
N = length(input); %Where N denotes the number of elements of 'input'
saveInput = zeros(1,N); % create a vector of 0s
for i=1:N
...
if (input meets criteria)
saveInput(i) = 1;
end
end
output = input( saveInput==1 ); %only save elements worth saving
The trivial solution is:
% if input(i) meets your conditions
output = [output; input(i)]
Though I don't know if this has good performance or not
If N is not too big so that it would cause you memory problems, you can pre-assign output to a vector of the same size as input, and remove all useless elements at the end of the loop.
output = NaN(N,1);
for i=1:N
...
output(i) = input(i);
...
end
output(isnan(output)) = [];
There are two alternatives
If output would be too big if it was assigned the size of N, or if you didn't know the upper limit of the size of output, you can do the following
lengthOutput = 100;
output = NaN(lengthOutput,1);
counter = 1;
for i=1:N
...
output(counter) = input(i);
counter = counter + 1;
if counter > lengthOutput
%# append output if necessary by doubling its size
output = [output;NaN(lengthOutput,1)];
lengthOutput = length(output);
end
end
%# remove unused entries
output(counter:end) = [];
Finally, if N is small, it is perfectly fine to call
output = [];
for i=1:N
...
output = [output;input(i)];
...
end
Note that performance degrades dramatically if N becomes large (say >1000).