Get final variable for a code with loops on Matlab - matlab

I have a code with two for loops. The code is working properly. The problem is that at the end I would like to get a variable megafinal with the results for all the years. The original varaible A has 3M rows, so it gives me an error because the size of the megafinal changes with each loop iteration and matlab stops running the code. I guess it’s a problem of inefficiency. Does anyone know a way to get this final variable despite of the size?
y = 1997:2013;
for i=1:length(y)
A=b(cell2mat(b(:,1))==y(i),:);
%Obtain the absolute value of the difference
c= cellfun(#minus,A(:,3),A(:,4));
c=abs(c);
c= num2cell(c);
A(:,end+1) = c;
%Delete rows based on a condition
d = (abs(cell2mat(A(:,8)) - cell2mat(A(:,7))));
[~, ind1] = sort(d);
e= A(ind1(end:-1:1),:);
[~, ind2,~] = unique(strcat(e(:,2),e(:, 6)));
X= e(ind2,:);
(…)
for j = 2:length(X)
if strcmp(X(j,2),X(j-1,2)) == 0
lin2 = j-1;
%Sort
X(lin1:lin2,:) = sortrows(X(lin1:lin2,:),13);
%Rank
[~,~,f]=unique([X{lin1:lin2,13}].');
g=accumarray(f,(1:numel(f))',[],#mean);
X(lin1:lin2,14)=num2cell(g(f));
%Score
out1 = 100 - ((cell2mat(X(lin1:lin2,14))-1) ./ size(X(lin1:lin2,:),1))*100;
X(lin1:lin2,15) = num2cell(out1);
lin1 = j;
end
end
%megafinal(i)=X
end

Make megafinal a cell array. This will account for the varying sizes of X at each iteration. As such, simply do this:
megafinal{i} = X;
To access a cell element, you just have to do megafinal{num}, where num is any index you want.

Related

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.

Vectorizing logic to check if index matches

I have the following function that works perfectly, but I would like to apply vectorization to it...
for i = 1:size(centroids,1)
centroids(i, :) = mean(X(idx == i, :));
end
It checks if idx matches the current index and if it does, it calculates the mean value for all the X values that correspond to that index.
This is my attempt at vectorization, my solution does not work and I know why...
centroids = mean(X(idx == [1:size(centroids,1)], :));
The following idx == [1:size(centroids,1)] breaks the code. I have no idea how to check if idx equals to either of the numbers from 1 to size(centroids,1).
tl:dr
Get rid of the for loop through vectorization
One option is to use arrayfun;
nIdx = size(centroids,1);
centroids = arrayfun(#(ii) mean(X(idx==ii,:)),1:nIdx, 'UniformOutput', false);
centroids = vertcat(centroids{:})
Since the output of a single function call is not necessarily a scalar, the UniformOutput option has to be set to false. Thus, arrayfun returns a cell array and you need to vertcat it to get the desired double array.
you can split the matrix into cells and take the mean from each cell using cellfun (which applies a loop in its inner operation):
generate data:
dim = 10;
N = 400;
nc = 20;
idx = randi(nc,[N 1]);
X = rand(N,dim);
centroids = zeros(nc,dim);
mean using loop (the question's method)
for i = 1:size(centroids,1)
centroids(i, :) = mean(X(idx == i, :));
end
vectorizing:
% split X into cells by idx
A = accumarray(idx, (1:N)', [nc,1], #(i) {X(i,:)});
% mean of each cell
C = cell2mat(cellfun(#(x) mean(x,1),A,'UniformOutput',0));
maximum absolute error between the methods:
max(abs(C(:) - centroids(:))) % about 1e-16

MATLAB list manipulation

Consider I have a code segment as follows:
Case 1
n = 20;
for i = 2 : n
mat = rand([2,i]);
mat = [mat, mat(:,1)]; %add the first column to the last
%feed the variable 'mat' to a function
end
Case 2
n = 10;
list = [];
for i = 1 : n
a = rand([2,1]);
b = rand([2,2])
list = [list, [a,b]];
end
In this way, MATLAB gives the below suggestion:
The variable 'mat' appears to change size on every loop. Consider preallocating for speed up.
The variable 'list' appears to change size on every loop. Consider preallocating for speed up.
I am a MATLAB newconer, So I would like to know how to deal with this issue. How to do this in native MATLAB style? Thanks in advance.
I'll focus on the second case, as it's the only one that makes sense:
n = 10;
list = [];
for i = 1 : n
a = rand([2,1]);
b = rand([2,2])
list = [list, [a,b]];
end
That you are doing here, for each loop, is to create two vectors with random numbers, a and b. a has dimension 2x1, and b has dimension 2x2. Then, you concatenate these two, with the matrix list.
Note that each call to rand are independent, so rand(2,3) will behave the same way [rand(2,2), rand(2,1)] does.
Now, since you loop 10 times, and you add rand(2,3) every time, you're essentially doing [rand(2,2), rand(2,1), rand(2,2), rand(2,1) ...]. This is equivalent to rand(2,30), which is a lot faster. Therefore, "Consider preallocating for speed up."
Now, if your concatenations doesn't contain random matrices, but are really the output from some function that can't output the entire matrix you want, then preallocate and insert it to the matrix using indices:
Let's define a few functions:
function x = loopfun(n)
x = n*[1; 2];
end
function list = myfun1(n)
list = zeros(2, n);
for ii = 1:n
list(:,ii) = loopfun(ii);
end
end
function list = myfun2(n)
list = [];
for ii = 1:n
list = [list, loopfun(ii)];
end
end
f1 = #() myfun1(100000); f2 = #() myfun2(100000);
fprintf('Preallocated: %f\nNot preallocated: %f\n', timeit(f1), timeit(f2))
Preallocated: 0.141617
Not preallocated: 0.318272
As you can see, the function with preallocation is twice as fast as the function with an increasing sized matrix. The difference is smaller if there are few iterations, but the general idea is the same.
f1 = #() myfun1(5); f2 = #() myfun2(5);
fprintf('Preallocated: %f\nNot preallocated: %f\n', timeit(f1), timeit(f2))
Preallocated: 0.000010
Not preallocated: 0.000018

For loop for storing value in many different matrix

I have written a code that stores data in a matrix, but I want to shorten it so it iterates over itself.
The number of matrices created is the known variable. If it was 3, the code would be:
for i = 1:31
if idx(i) == 1
C1 = [C1; Output2(i,:)];
end
if idx(i) == 2
C2 = [C2; Output2(i,:)];
end
if idx(i) == 3
C3 = [C3; Output2(i,:)];
end
end
If I understand correctly, you want to extract rows from Output2 into new variables based on idx values? If so, you can do as follows:
Output2 = rand(5, 10); % example
idx = [1,1,2,2,3];
% get rows from Output which numbers correspond to those in idx with given value
C1 = Output2(find(idx==1),:);
C2 = Output2(find(idx==2),:);
C3 = Output2(find(idx==3),:);
Similar to Marcin i have another solution. Here i predefine my_C as a cell array. Output2 and idx are random generated and instead of find i just use logical adressing. You have to convert the data to type cell {}
Output2 = round(rand(31,15)*10);
idx = uint8(round(1+rand(1,31)*2));
my_C = cell(1,3);
my_C(1,1) = {Output2(idx==1,:)};
my_C(1,2) = {Output2(idx==2,:)};
my_C(1,3) = {Output2(idx==3,:)};
If you want to get your data back just use e.g. my_C{1,1} for the first group.
If you have not 3 but n resulting matrices you can use:
Output2 = round(rand(31,15)*10);
idx = uint8(round(1+rand(1,31)*(n-1)));
my_C = cell(1,n);
for k=1:n
my_C(1,k) = {Output2(idx==k,:)};
end
Where n is a positive integer number
I would recommend a slighty different approach. Except for making the rest of the code more maintainable it may also slightly speed up the execution. This due to that matlab uses a JIT compiler and eval must be recompiled every time. Try this:
nMatrices = 3
for k = 1:nMatrices
C{k} = Output2(idx==k,:);
end
As patrik said in the comments, naming variables like this is poor practice. You would be better off using cell arrays M{1}=C1, or if all the Ci are the same size, even just a 3D array M, for example, where M(:,:,1)=C1.
If you really want to use C1, C2, ... as you variable names, I think you will have to use eval, as arielnmz mentioned. One way to do this in matlab is
for i=1:3
eval(['C' num2str(idx(i)) '=[C' num2str(idx(i)) ';Output2(' num2str(i) ',:)];'])
end
Edited to add test code:
idx=[2 1 3 2 2 3];
Output2=rand(6,4);
C1a=[];
C2a=[];
C3a=[];
for i = 1:length(idx)
if idx(i) == 1
C1a = [C1a; Output2(i,:)];
end
if idx(i) == 2
C2a = [C2a; Output2(i,:)];
end
if idx(i) == 3
C3a = [C3a; Output2(i,:)];
end
end
C1=[];
C2=[];
C3=[];
for i=1:length(idx)
eval(['C' num2str(idx(i)) '=[C' num2str(idx(i)) ';Output2(' num2str(i) ',:)];'])
end
all(C1a(:)==C1(:))
all(C2a(:)==C2(:))
all(C3a(:)==C3(:))

Matlab imfilter function implemenation

I am trying to write a function that implements imfilter function. but getting this error.
??? Subscript indices must either be real positive integers or logicals
at this point
s= size(img);
Find below the code snippet
s = size(img);
Ix = zeros(s);
Iy = zeros(s);
for i = 1:s
for j = 1:s
temp = img(i-1:i+1,j-1:j+1) .* Gx;
Ix(i,j) = sum(temp(:));
end
end
Please is there anything Im doing wrong?
EDITED CODE
s = size(img);
Ix = zeros(s);
Iy = zeros(s);
for i = 2:s(1)-1
for j = 2:s(2)-1
temp = img(i-1:i+1,j-1:j+1) .* Gx;
Ix(i,j) = sum(temp(:));
end
end
If it genuinely happens at the point of calling s= size(img);, then you probably have a variable size in your workspace which is shadowing the size function.
In addition, there are a couple of possible issues with your loop. First, you can't use zero as in index in MATLAB. Hence, when you have i = 1, j = 1 at the start of your loop, you would expect the temp line to return the Subscript indices error.
the output of size, presuming img is a greyscale image, is going to be two numbers. When you do i = 1:s, it will ignore the second one. This is fine so long as your image is square but will not do what you expect if it isn't.
Finally, have a look at conv2 for cases like this rather than creating a loop.