Update only one matrix element for iterative computation - matlab

I have a 3x3 matrix, A. I also compute a value, g, as the maximum eigen value of A. I am trying to change the element A(3,3) = 0 for all values from zero to one in 0.10 increments and then update g for each of the values. I'd like all of the other matrix elements to remain the same.
I thought a for loop would be the way to do this, but I do not know how to update only one element in a matrix without storing this update as one increasingly larger matrix. If I call the element at A(3,3) = p (thereby creating a new matrix Atry) I am able (below) to get all of the values from 0 to 1 that I desired. I do not know how to update Atry to get all of the values of g that I desire. The state of the code now will give me the same value of g for all iterations, as expected, as I do not know how to to update Atry with the different values of p to then compute the values for g.
Any suggestions on how to do this or suggestions for jargon or phrases for me to web search would be appreciated.
A = [1 1 1; 2 2 2; 3 3 0];
g = max(eig(A));
% This below is what I attempted to achieve my solution
clear all
p(1) = 0;
Atry = [1 1 1; 2 2 2; 3 3 p];
g(1) = max(eig(Atry));
for i=1:100;
p(i+1) = p(i)+ 0.01;
% this makes a one giant matrix, not many
%Atry(:,i+1) = Atry(:,i);
g(i+1) = max(eig(Atry));
end

This will also accomplish what you want to do:
A = #(x) [1 1 1; 2 2 2; 3 3 x];
p = 0:0.01:1;
g = arrayfun(#(x) eigs(A(x),1), p);
Breakdown:
Define A as an anonymous function. This means that the command A(x) will return your matrix A with the (3,3) element equal to x.
Define all steps you want to take in vector p
Then "loop" through all elements in p by using arrayfun instead of an actual loop.
The function looped over by arrayfun is not max(eig(A)) but eigs(A,1), i.e., the 1 largest eigenvalue. The result will be the same, but the algorithm used by eigs is more suited for your type of problem -- instead of computing all eigenvalues and then only using the maximum one, you only compute the maximum one. Needless to say, this is much faster.

First, you say 0.1 increments in the text of your question, but your code suggests you are actually interested in 0.01 increments? I'm going to operate under the assumption you mean 0.01 increments.
Now, with that out of the way, let me state what I believe you are after given my interpretation of your question. You want to iterate over the matrix A, where for each iteration you increase A(3, 3) by 0.01. Given that you want all values from 0 to 1, this implies 101 iterations. For each iteration, you want to calculate the maximum eigenvalue of A, and store all these eigenvalues in some vector (which I will call gVec). If this is correct, then I believe you just want the following:
% Specify the "Current" A
CurA = [1 1 1; 2 2 2; 3 3 0];
% Pre-allocate the values we want to iterate over for element (3, 3)
A33Vec = (0:0.01:1)';
% Pre-allocate a vector to store the maximum eigenvalues
gVec = NaN * ones(length(A33Vec), 1);
% Loop over A33Vec
for i = 1:1:length(A33Vec)
% Obtain the version of A that we want for the current i
CurA(3, 3) = A33Vec(i);
% Obtain the maximum eigen value of the current A, and store in gVec
gVec(i, 1) = max(eig(CurA));
end
EDIT: Probably best to paste this code into your matlab editor. The stack-overflow automatic text highlighting hasn't done it any favors :-)
EDIT: Go with Rody's solution (+1) - it is much better!

Related

Assigning matrix elements to variables in a data set

Hello I'm new to Matlab.
I've written this script :
k2=2*pi();
z1 = 1;
z2 = 2;
z3 = 4;
for l = linspace(0,1,11)
A = [ -1 1 1 0 ; 1 z1/z2 -z1/z2 0 ; 0 exp(-i*k2*l) exp(i*k2*l) -1 ; 0 exp(- i*k2*l) -exp(i*k2*l) -z2/z3];
B = [ 1 ; 1 ; 0 ; 0];
D = inv(A);
C = mtimes(D,B) ;
display(C)
r = C(1,1); % this is supposed to set r = the 1,1 element in the matrix C
t = C(1,4); % see above
end
My idea for taking the values of r and t from C didnt appear to work. How can I do this properly?
Also I want to plot a graph of |r|,|t|, arg(r) and arg(t) for each value of l, my for loop overwrites the values of r and t? how can I either plot one point per loop or make r and t assign the new values so that they become lists of data.
Thanks a lot!
Matlab sets the first dimension of a matrix as row number (i.e. y position).
So you want t=C(4, 1), as you should see that the size of C is 4x1. As a note Matlab is quite good at suppressing singleton dimensions so you could do also do C(1) and C(4).
For your second point you want to set a particular element of r and t in each loop. This is the same as when you access at particular element of C when setting the values. For your case you can use the index l to determine the element. Remembering that in matlab arrays start at element 1 (not 0 as in many other languages). So you want something like r(l+1)=C(1); (or change l to start at 1).
In the more general case if you are not looping over an integer for some reason you may need to create a separate counter variable which you increase in the loop. Also it is good practice to preallocate such arrays when the size is known beforehand, often by r=zeros(11, 1) or similar (note: zeros(11) is an 11x11 matrix). This isn't significant in this case but can drastically increase execution time for large multi-dimensional arrays so is a good practice.

element by element matrix multiplication in Matlab

So I have the following matrices:
A = [1 2 3; 4 5 6];
B = [0.5 2 3];
I'm writing a function in MATLAB that will allow me to multiply a vector and a matrix by element as long as the number of elements in the vector matches the number of columns. In A there are 3 columns:
1 2 3
4 5 6
B also has 3 elements so this should work. I'm trying to produce the following output based on A and B:
0.5 4 9
2 10 18
My code is below. Does anyone know what I'm doing wrong?
function C = lab11(mat, vec)
C = zeros(2,3);
[a, b] = size(mat);
[c, d] = size(vec);
for i = 1:a
for k = 1:b
for j = 1
C(i,k) = C(i,k) + A(i,j) * B(j,k);
end
end
end
end
MATLAB already has functionality to do this in the bsxfun function. bsxfun will take two matrices and duplicate singleton dimensions until the matrices are the same size, then perform a binary operation on the two matrices. So, for your example, you would simply do the following:
C = bsxfun(#times,mat,vec);
Referencing MrAzzaman, bsxfun is the way to go with this. However, judging from your function name, this looks like it's homework, and so let's stick with what you have originally. As such, you need to only write two for loops. You would use the second for loop to index into both the vector and the columns of the matrix at the same time. The outer most for loop would access the rows of the matrix. In addition, you are referencing A and B, which are variables that don't exist in your code. You are also initializing the output matrix C to be 2 x 3 always. You want this to be the same size as mat. I also removed your checking of the length of the vector because you weren't doing anything with the result.
As such:
function C = lab11(mat, vec)
[a, b] = size(mat);
C = zeros(a,b);
for i = 1:a
for k = 1:b
C(i,k) = mat(i,k) * vec(k);
end
end
end
Take special note at what I did. The outer-most for loop accesses the rows of mat, while the inner-most loop accesses the columns of mat as well as the elements of vec. Bear in mind that the number of columns of mat need to be the same as the number of elements in vec. You should probably check for this in your code.
If you don't like using the bsxfun approach, one alternative is to take the vector vec and make a matrix out of this that is the same size as mat by stacking the vector vec on top of itself for as many times as we have rows in mat. After this, you can do element-by-element multiplication. You can do this stacking by using repmat which repeats a vector or matrices a given number of times in any dimension(s) you want. As such, your function would be simplified to:
function C = lab11(mat, vec)
rows = size(mat, 1);
vec_mat = repmat(vec, rows, 1);
C = mat .* vec_mat;
end
However, I would personally go with the bsxfun route. bsxfun basically does what the repmat paradigm does under the hood. Internally, it ensures that both of your inputs have the same size. If it doesn't, it replicates the smaller array / matrix until it is the same size as the larger array / matrix, then applies an element-by-element operation to the corresponding elements in both variables. bsxfun stands for Binary Singleton EXpansion FUNction, which is a fancy way of saying exactly what I just talked about.
Therefore, your function is further simplified to:
function C = lab11(mat, vec)
C = bsxfun(#times, mat, vec);
end
Good luck!

Loop through matrix, find row values that meet constraint, create new column

I am sure something similar has been answered before, but I am new to MATLAB and currently stuck with a very simple problem. I have a matrix M and I would like to do the following: Loop through all the values of a column C and if any of these values are not equal to some value, x copy the corresponding values over into another column in the matrix (call it Z), while leaving those values that did not satisfy the condition alone.
I have tried the following, but it's not doing anything:
rows = size(M,1);
for i = 1:rows
if M(:,x) ~= 0
then M(:,Z) = M(:,x)
end
end
Looking at your comments, this is the gist of what you want to accomplish:
Given a matrix A, choose two columns in this matrix: x and Z.
Given a value d, we wish to find all values within x that are different than some value, d.
Take these locations in x and copy them over to the corresponding locations in Z.
Let's do this step by step. Given your example in your comments:
% // Step #1
A = [1 2 3; 4 5 6; 7 8 10];
x = 1; % //Column x
Z = 3; % //Column Z
d = 1; % //Value to compare to
% //Step #2
loc = A(:,x) ~= d;
%//Step #3
A(loc, Z) = A(loc, x);
Since you're new to MATLAB, let's go through this slowly. The first step is pretty basic MATLAB syntax. It's choosing your parameters as well as some basic set up. The second step will give us a logical array that tells us which rows in column x are not equal to d. The last step will find these corresponding rows in column Z and copy these values over from column x over to Z.
The great thing about MATLAB is that it does these kinds of vectorized operations natively. The best thing for optimization is to try to avoid for loops as much as you can. Only use for loops if you have to repeat heavy computations, or situations where you absolutely cannot avoid for loops.
As a bonus, let's see how MATLAB displays each step that we've talked above (except the first one as it's redundant).
% //Step #2
loc = A(:,x) ~= d
>> loc
loc =
0
1
1
%// Step #3
A(loc, Z) = A(loc, x)
>> A =
1 2 3
4 5 4
7 8 7
Note that loc is a logical array such that 0 is false, and 1 is true. As such, rows 2 and 3 satisfy the condition that these values are not equal to d.
Also, FWIW, welcome to StackOverflow!

Finding closest (not equal) rows in two matrices matlab

I have two matrices A(m X 3) and B(n X 3); where m >> n.
Numbers in B have close or equal values to numbers in A.
I want to search closest possible values from A to the values present in B in a way that at the end of search, A will reduced to (n X 3).
There are two main issues:
Only a complete row from A can be compared to a complete row in B, where numbers in each column of A and B are varying independently.
Numbers in A and B may be as close as third place of decimal (e.g. 20.101 and 20.103)
I hope I am clear in asking my question.
Does anybody know about any function already present in matlab for this thing?
Depending on how you look at the task, here are two different approaches
Minimum Distance to Each Row in Second Matrix
Two ways to look at this: (1) closest point in A for each point in B, or (2) closest point in B for each point in A.
Closest point in A
For each point in B you can find the closest point in A (e.g. Euclidean distance), as requested in comments:
% Calculate all MxN high-dimensional (3D space) distances at once
distances = squeeze(sum(bsxfun(#minus,B,permute(A,[3 2 1])).^2,2));
% Find closest row in A for each point in B
[~,ik] = min(distances,[],2)
Make an array the size of B containing these closest points in A:
Anew = A(ik,:)
This will implicitly throw out any points in A that are too far from points in B, as long as each point in B does have a match in A. If each point in B does not necessarily have a "match" (point at an acceptable distance) in A, then it is necessary to actively reject points based on distances, resulting in an output that would be shorter than B. This solution seems out of scope.
Closest point in B
Compute the Euclidenan distance from each point (row) in A to each point in B and identify the closest point in B:
distances = squeeze(sum(bsxfun(#minus,A,permute(B,[3 2 1])).^2,2));
[~,ik] = min(distances,[],2)
Make an array the size of A containing these closest points in B:
Anew = B(ik,:)
The size of Anew in this approach is the same as A.
Merging Similar Points in First Matrix
Another approach is to use the undocumented _mergesimpts function.
Consider this test data:
>> B = randi(5,4,3)
B =
1 4 4
2 3 4
1 3 4
3 4 5
>> tol = 0.001;
>> A = repmat(B,3,1) + tol * rand(size(B,1)*3,3)
A =
1.0004 4.0005 4.0000
2.0004 3.0005 4.0008
1.0004 3.0009 4.0002
3.0008 4.0005 5.0004
1.0006 4.0004 4.0007
2.0008 3.0007 4.0004
1.0009 3.0007 4.0007
3.0010 4.0005 5.0004
1.0002 4.0003 4.0007
2.0001 3.0001 4.0007
1.0007 3.0006 4.0004
3.0001 4.0003 5.0000
Merge similar rows in A according to a specified tolerance, tol:
>> builtin('_mergesimpts',A,tol,'average')
ans =
1.0004 4.0004 4.0005
1.0007 3.0007 4.0005
2.0005 3.0005 4.0006
3.0006 4.0004 5.0003
Merge similar rows, using B to get expected numbers
>> builtin('_mergesimpts',[A; B],tol,'first')
ans =
1 3 4
1 4 4
2 3 4
3 4 5
To replace each row of A by the closest row of B
You can use pdist2 to compute distance between rows, and then the second output of min to find the index of the minimum-distance row:
[~, ind] = min(pdist2(B,A,'euclidean')); %// or specify some other distance
result = B(ind,:);
The advantage of this approach is that pdist2 lets you specify other distance functions, or even define your own. For example, to use L1 distance change first line to
[~, ind] = min(pdist2(B,A,'cityblock'));
To retain rows of A which are closest to rows of B
Use pdist2 as above. For each row of A compute the minimum distance to rows of B. Retain the n rows of A with lowest value of that minimum distance:
[~, ii] = sort(min(pdist2(B,A,'euclidean'))); %// or use some other distance
result = A(ii(1:n),:);
Try this code -
%% Create data
m=7;
n=4;
TOL = 0.0005;
A = rand(m,3)/100;
B = rand(n,3)/100;
B(2,:) = A(5,:); % For testing that the matching part of the second row from B must be the fifth row from A
%% Interesting part
B2 = repmat(reshape(B',1,3,n),[m 1]);
closeness_matrix = abs(bsxfun(#minus, A, B2));
closeness_matrix(closeness_matrix<TOL)=0;
closeness_matrix_mean = mean(closeness_matrix,2); % Assuming that the "closeness" for the triplets on each row can be measured by the mean value of them
X1 = squeeze(closeness_matrix_mean);
[minval,minind] = min(X1,[],1);
close_indices = minind';
A_final = A(close_indices,:)

frequency of each vector value in another vector matlab

I need to calculate the frequency of each value in another vector in MATLAB.
I can use something like
for i=1:length(pdata)
gt(i)=length(find(pf_test(:,1)==pdata(i,1)));
end
But I prefer not to use loop because my dataset is quite large. Is there anything like histc (which is used to find the frequency of values in one vector) to find the frequency of one vector value in another vector?
If your values are only integers, you could do the following:
range = min(pf_test):max(pf_test);
count = histc(pf_test,range);
gt = count(ismember(range,a));
gt(~ismember(unique(a),b)) = 0;
If you can't guarantee that the values are integers, it's a bit more complicated. One possible method of it would be the following:
%restrict yourself to values that appear in the second vector
filter = ismember(pf_test,pdata);
% sort your first vector (ignore this if it is already sorted)
spf_test = sort(pf_test);
% Find the first and last occurrence of each element
[~,last] = unique(spf_test(filter));
[~,first] = unique(spf_test(filter),'first');
% Initialise gt
gt = zeros(length(pf_test));
% Fill gt
gt(filter) = (last-first)+1;
EDIT: Note that I may have got the vectors the wrong way around - if this doesn't work as expected, switch pf_test and pdata. It wasn't immediately clear to me which was which.
You mention histc. Why are you not using it (in its version with two input parameters)?
>> pdata = [1 1 3 2 3 1 4 4 5];
>> pf_test = 1:6;
>> histc(pdata,pf_test)
ans =
3 1 2 2 1 0