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

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!

Related

MATLAB: Applying vectors of row and column indices without looping

I have a situation analogous to the following
z = magic(3) % Data matrix
y = [1 2 2]' % Column indices
So,
z =
8 1 6
3 5 7
4 9 2
y represents the column index I want for each row. It's saying I should take row 1 column 1, row 2 column 2, and row 3 column 2. The correct output is therefore 8 5 9.
I worked out I can get the correct output with the following
x = 1:3;
for i = 1:3
result(i) = z(x(i),y(i));
end
However, is it possible to do this without looping?
Two other possible ways I can suggest is to use sub2ind to find the linear indices that you can use to sample the matrix directly:
z = magic(3);
y = [1 2 2];
ind = sub2ind(size(z), 1:size(z,1), y);
result = z(ind);
We get:
>> result
result =
8 5 9
Another way is to use sparse to create a sparse matrix which you can turn into a logical matrix and then sample from the matrix with this logical matrix.
s = sparse(1:size(z,1), y, 1, size(z,1), size(z,2)) == 1; % Turn into logical
result = z(s);
We also get:
>> result
result =
8
5
9
Be advised that this only works provided that each row index linearly increases from 1 up to the end of the rows. This conveniently allows you to read the elements in the right order taking advantage of the column-major readout that MATLAB is based on. Also note that the output is also a column vector as opposed to a row vector.
The link posted by Adriaan is a great read for the next steps in accessing elements in a vectorized way: Linear indexing, logical indexing, and all that.
there are many ways to do this, one interesting way is to directly work out the indexes you want:
v = 0:size(y,2)-1; %generates a number from 0 to the size of your y vector -1
ind = y+v*size(z,2); %generates the indices you are looking for in each row
zinv = z';
zinv(ind)
>> ans =
8 5 9

MATLAB: For loop structure [duplicate]

This question already has answers here:
Element-wise array replication in Matlab
(7 answers)
Closed 6 years ago.
This is a basic program but since I'm new to MATLAB, I'm not able to figure out the solution.
I have a column vector "Time" in which I want to print value "1" in first 147 cells, followed by "2" in 148 to 2*147 cells and so on. For that, I have written the following script:
Trial>> c=1;
Trial>> k=0;
Trial>> for i = c:146+c
Time(i,1)=1+k;
c=i;
k=k+1;
end
I know I need to iterate the loop over "Time(i,1)=1+k;" before it executes the next statement. I tried using break but that's not supposed to work. Can anyone suggest me the solution to get the desired results?(It was quite simple in C with just the use of curly braces.)
I am sure you don't want to run c=i; in every iteration.
My code should work for you:
x = 10; % Replace 10 by the max number you need in your array.
k = 1;
for i = 1 : x * 147
Time(i, 1) = k;
if rem(i, 147) == 0
k = k + 1;
end
end
This is the prime example of a piece of code that should be vectorized can help you understand vectorization. Your code can be written like this:
n = 147;
reps = 10; %% Replace this by the maximum number you want your matrix to have
Time = reshape(bsxfun(#plus, zeros(n,1), 0:reps), 1, []);
Explanation:
Let A be a column vector (1 column, n rows), and B be a row vector (1 row, m columns.
What bsxfun(#plus, A, B) will do here is to add all elements in A with all elements in B, like this:
A(1)+B(1) A(1)+B(2) A(1)+B(3) ... A(1)+B(m)
A(2)+B(1) A(2)+B(2) ............. A(2)+B(m)
............................................
A(n)+B(1) A(n)+B(2) .............. A(n)+B(m)
Now, for the two vectors we have: zeros(n,1), and 0:reps, this will give us;
0+0 0+1 0+2 0+reps
0+0 0+1 0+2 0+reps
% n rows of this
So, what we need to do now is place each column underneath each other, so that you will have the column with zeros first, then the row with ones, ... and finally the one with reps (147 in your case).
This can be achieved by reshaping the matrix:
reshape(bsxfun(#plus, zeros(n,1), 0:reps), [], 1);
^ ^ ^ ^
| | | Number of rows in the new matrix. When [] is used, the appropriate value will be chosen by Matlab
| | Number of rows in the new matrix
| matrix to reshape
reshape command
Another approach is using kron:
kron(ones(reps+1, 1) * 0:(n-1)
For the record, a review of your code:
You should always preallocate memory for matrices that are created inside loops. In this case you know it will become a matrix of dimensions ((reps+1)*n-by-1). This means you should do Time = zeros((reps+1)*n, 1);. This will speed up your code a lot.
You shouldn't use i and j as variable names in Matlab, as they denote the imaginary unit (sqrt(-1)). You can for instance do: for ii = 1:(n*147) instead.
You don't want c=i inside the loop, when the loop is supposed to go from c to c + 146. That doesn't make much sense.
You can use repmat,
x = 10; % Sequence length (or what ever it can be called)
M = repmat(1:x,147,1); % Replicate array 1:x for 147 columns
M = M(:); % Reshape the matrix so that is becomes a column vector.
I can assume that this is a task to practice for loops, but this will work.
An alternative solution may be to do
n = 147;
reps = 10;
a = ceil( (1:(n*reps)) / n);
You first construct an array with the length you want. Then you divide, and round of upwards. 1 to 147 will then become 1.

Shifting repeating rows to a new column in a matrix

I am working with a n x 1 matrix, A, that has repeating values inside it:
A = [0;1;2;3;4; 0;1;2;3;4; 0;1;2;3;4; 0;1;2;3;4]
which correspond to an n x 1 matrix of B values:
B = [2;4;6;8;10; 3;5;7;9;11; 4;6;8;10;12; 5;7;9;11;13]
I am attempting to produce a generalised code to place each repetition into a separate column and store it into Aa and Bb, e.g.:
Aa = [0 0 0 0 Bb = [2 3 4 5
1 1 1 1 4 5 6 7
2 2 2 2 6 7 8 9
3 3 3 3 8 9 10 11
4 4 4 4] 10 11 12 13]
Essentially, each repetition from A and B needs to be copied into the next column and then deleted from the first column
So far I have managed to identify how many repetitions there are and copy the entire column over to the next column and then the next for the amount of repetitions there are but my method doesn't shift the matrix rows to columns as such.
clc;clf;close all
A = [0;1;2;3;4;0;1;2;3;4;0;1;2;3;4;0;1;2;3;4];
B = [2;4;6;8;10;3;5;7;9;11;4;6;8;10;12;5;7;9;11;13];
desiredCol = 1; %next column to go to
destinationCol = 0; %column to start on
n = length(A);
for i = 2:1:n-1
if A == 0;
A = [ A(:, 1:destinationCol)...
A(:, desiredCol+1:destinationCol)...
A(:, desiredCol)...
A(:, destinationCol+1:end) ];
end
end
A = [...] retrieved from Move a set of N-rows to another column in MATLAB
Any hints would be much appreciated. If you need further explanation, let me know!
Thanks!
Given our discussion in the comments, all you need is to use reshape which converts a matrix of known dimensions into an output matrix with specified dimensions provided that the number of elements match. You wish to transform a vector which has a set amount of repeating patterns into a matrix where each column has one of these repeating instances. reshape creates a matrix in column-major order where values are sampled column-wise and the matrix is populated this way. This is perfect for your situation.
Assuming that you already know how many "repeats" you're expecting, we call this An, you simply need to reshape your vector so that it has T = n / An rows where n is the length of the vector. Something like this will work.
n = numel(A); T = n / An;
Aa = reshape(A, T, []);
Bb = reshape(B, T, []);
The third parameter has empty braces and this tells MATLAB to infer how many columns there will be given that there are T rows. Technically, this would simply be An columns but it's nice to show you how flexible MATLAB can be.
If you say you already know the repeated subvector, and the number of times it repeats then it is relatively straight forward:
First make your new A matrix with the repmat function.
Then remap your B vector to the same size as you new A matrix
% Given that you already have the repeated subvector Asub, and the number
% of times it repeats; An:
Asub = [0;1;2;3;4];
An = 4;
lengthAsub = length(Asub);
Anew = repmat(Asub, [1,An]);
% If you can assume that the number of elements in B is equal to the number
% of elements in A:
numberColumns = size(Anew, 2);
newB = zeros(size(Anew));
for i = 1:numberColumns
indexStart = (i-1) * lengthAsub + 1;
indexEnd = indexStart + An;
newB(:,i) = B(indexStart:indexEnd);
end
If you don't know what is in your original A vector, but you do know it is repetitive, if you assume that the pattern has no repeats you can use the find function to find when the first element is repeated:
lengthAsub = find(A(2:end) == A(1), 1);
Asub = A(1:lengthAsub);
An = length(A) / lengthAsub
Hopefully this fits in with your data: the only reason it would not is if your subvector within A is a pattern which does not have unique numbers, such as:
A = [0;1;2;3;2;1;0; 0;1;2;3;2;1;0; 0;1;2;3;2;1;0; 0;1;2;3;2;1;0;]
It is worth noting that from the above intuitively you would have lengthAsub = find(A(2:end) == A(1), 1) - 1;, But this is not necessary because you are already effectively taking the one off by only looking in the matrix A(2:end).

Search for 1-D sequence in multidimensional array in Matlab

I have an array with n dimensions, and I have a sequence along one dimension at a certain location on all other dimensions. How do I find the location of this sequence? Preferably without loops.
I use matlab. I know what dimension it should be in, but the sequence isnt necessarily there. Find and == dont work. I could make an nd find function using crosscorrelation but Im guessing this is already implemented and I just dont know what function to call.
example:
ND = rand(10,10,10,10);
V = ND(randi(10),randi(10),randi(10),:);
[I1, I2, I3] = find(ND==V);
Edit: The sequence to be found spans the entire dimension it is on, I did not mention this in my original formulation of the problem. Knedlsepp`s solution solves exactly the problem I had, but Luis' solution solves a more general problem for when the sequence doesn't necessarily span the entire dimension.
As there are multiple ways to interpret your question, I will clarify: This approach assumes a 1D sequence of size: numel(V) == size(ND, dimToSearch). So, for V = [1,2] and ND = [1,2,1,2] it is not applicable. If you want this functionality go with Luis Mendo's answer, if not this will likely be faster.
This will be a perfect opportunity to use bsxfun:
We start with some example data:
ND = rand(10,10,10,10);
V = ND(3,2,:,3);
If you don't have the vector V given in the correct dimension (in this case [1,1,10,1]) you can reshape it in the following way:
dimToSearch = 3;
Vdims = ones(1, ndims(ND));
Vdims(dimToSearch) = numel(V);
V = reshape(V, Vdims);
Now we generate a cell that will hold the indices of the matches:
I = cell(1, ndims(ND));
At this point we compute the size of ND if it were collapsed along the dimension dimToSearch (we compute dimToSearch according to V, as at this point it will have the correct dimensions):
dimToSearch = find(size(V)>1);
collapsedDims = size(ND);
collapsedDims(dimToSearch) = 1;
Finally the part where we actually look for the pattern:
[I{:}] = ind2sub(collapsedDims, find(all(bsxfun(#eq, ND, V), dimToSearch)));
This is done in the following way: bsxfun(#eq, ND, V) will implicitly repmat the array V so it has the same dimensions as ND and do an equality comparison. After this we do a check with all to see if all the entries in the dimension dimToSearch are equal. The calls to find and ind2sub will then generate the correct indices to your data.
Let d be the dimension along which to search. I'm assuming that the sought sequence V may be shorter than size(ND,d). So the sequence may appear once, more than once, or never along each dimension-d- "thread".
The following code uses num2cell to reshape ND into a cell array such that each dimension-d-thread is in a different cell. Then strfind is applied to each cell to determine matches with V, and the result is a cell array with the same dimensions as ND, but where the dimension d is a singleton. The contents of each cell tell the d-dimension-positions of the matches, if any.
Credit goes to #knedlsepp for his suggestion to use num2cell, which greatly simplified the code.
ND = cat(3, [1 2 1 2; 3 4 5 6],[2 1 0 5; 0 0 1 2] ); %// example. 2x4x2
V = 1:2; %// sought pattern. It doesn't matter if it's a row, or a column, or...
d = 2; %// dimension along which to search for pattern V
result = cellfun(#(x) strfind(x(:).', V(:).'), num2cell(ND,d), 'UniformOutput', 0);
This gives
ND(:,:,1) =
1 2 1 2
3 4 5 6
ND(:,:,2) =
2 1 0 5
0 0 1 2
V =
1 2
result{1,1,1} =
1 3 %// V appears twice (at cols 1 and 3) in 1st row, 1st slice
result{2,1,1} =
[] %// V doesn't appear in 2nd row, 1st slice
result{1,1,2} =
[] %// V appears appear in 1st row, 2nd slice
result{2,1,2} =
3 %// V appears once (at col 3) in 2nd row, 2nd slice
One not very optimal way of doing it:
dims = size(ND);
Vrep = repmat(V, [dims(1), dims(2), dims(3), 1]);
ND_V_dist = sqrt(sum(abs(ND.^2-Vrep.^2), 4));
iI = find(ND_V_dist==0);
[I1, I2, I3] = ind2sub([dims(1), dims(2), dims(3)], iI);

Update only one matrix element for iterative computation

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!