Matlab multiple matrix rowwise based on rule - matlab

I have this matrix
mp=
2
5
8
fp=
0.67 0.34 0
0.34 0.34 0.34
0 0.5 0.5
and this matrix
t=
1
1
1
2
3
2
3
with this rule:
if t=1 then the output = mp*(fp first row)
if t=2 then the output = mp*(fp 2nd row)
if t=3 then the output = mp*(fp 3rd row)
so the output should be like this:
output=
3
3
3
5
6,5
5
6,5
im trying using this code
but the output==t
[o p]=size(t)
[q r]=size(mp)
for i=1:o;
j=1:q;
if t(i)==j
output=mp*fp(j,:)
else
output=t(i)
end
end

One line Solution
you can use the following syntax:
output = sum(repmat(mp,1,length(t))'.*fp(t,:),2)
result
ans =
3.0400
3.0400
3.0400
5.1000
6.5000
5.1000
6.5000
example
%variables declaration
fp= [0.67 0.34 0; 0.34 0.34 0.34;0 0.5 0.5];
mp = [2; 5; 8]
t = [1,1,1,2,3,2,3]
%calculates result
output = sum(repmat(mp,1,length(t))'.*fp(t,:),2)

As your t values are already row numbers, you can directly use t values for multiplication.
mp=[2
5
8] ;
fp=[0.67 0.34 0
0.34 0.34 0.34
0 0.5 0.5] ;
t=[1
1
1
2
3
2
3] ;
iwant = cell(size(t)) ; % initilaize the required data
for i = 1:length(t) % loop for each value of _t_
iwant{i} = mp*fp(t(i),:) ;
end

Related

How to interpolate matrix to get specific values

I have this matrix in MATLAB:
x = [NaN -2 -1 0 1 2;
1 0.21 0.15 0.34 0.11 0.32;
2 0.14 0.10 0.16 0.31 0.11];
The first row represents the location of the values following X coordinates.
I shift the first row by -0.63, so x becomes:
New_x = [NaN -2.63 -1.63 -0.63 0.37 1.37;
1 0.21 0.15 0.34 0.11 0.32;
2 0.14 0.10 0.16 0.31 0.11];
How can I use interpolation to get the values at specific coordinates of the New_x matrix that we have in the x matrix? ([-2 -1 0 1 2] points)
New_xInterp = [NaN -2.63 .. -2 .. -1.63 .. -1 .. -0.63 .. 0 .. 0.37 .. 1 .. 1.37 .. 2;
1 0.21 .. ? .. 0.15 .. ? .. 0.34 .. ? .. 0.11 .. ? .. 0.32 .. ?;
2 0.14 .. ? .. 0.10 .. ? .. 0.16 .. ? .. 0.31 .. ? .. 0.11 .. ?];
I want to get the '?' values. I tried to use interp2 function but I don't know which step or 2^k-1 interpolated points between coordinates values I have to have in order to get the points like -2, -1, 0, 1, 2.
Thanks !
Since you do not have 2D data, you are only interpolating on one dimension, you only need the function interp1.
This function can work on vector or matrices if necessary, but it require a slight reorganisation of your data.
%% Input
M = [NaN -2 -1 0 1 2;
1 0.21 0.15 0.34 0.11 0.32;
2 0.14 0.10 0.16 0.31 0.11];
%% Demultiplex inputs
x = M(1,2:end).' ; % extract X values, reorder in column
y = M(2:end,2:end).' ; % extract Y values, reorder in columns
%% Interpolate
xn = sort( [x-0.63 ; x] ) ; % Generate the new_x target values
yn = interp1( x-0.63 , y , xn ,'linear','extrap') ; % Interpolate the full matrix in one go
At this point you have your new xn and yn values in columns:
xn= yn=
-2.63 0.21 0.14
-2 0.1722 0.1148
-1.63 0.15 0.1
-1 0.2697 0.1378
-0.63 0.34 0.16
0 0.1951 0.2545
0.37 0.11 0.31
1 0.2423 0.184
1.37 0.32 0.11
2 0.4523 -0.016
I would keep them like that if you have more operations to do on them later on. However, if you want it back into the format you had at the beginning, we can simply rebuild the new full matrix:
%% Rebuild global matrix
Mout = [ M(:,1) , [xn.' ; yn.'] ]
Mout =
NaN -2.63 -2 -1.63 -1 -0.63 0 0.37 1 1.37 2
1 0.21 0.1722 0.15 0.2697 0.34 0.1951 0.11 0.2423 0.32 0.4523
2 0.14 0.1148 0.1 0.1378 0.16 0.2545 0.31 0.184 0.11 -0.016
Maybe you can try interp1 + arrayfun like below
r = sort([x(1,2:end),New_x(1,2:end)]);
New_xInterp = [New_x(:,1),cell2mat(arrayfun(#(k) interp1(New_x(1,2:end),New_x(k,2:end),r),1:size(New_x,1),'UniformOutput',false).')];
which gives
New_xInterp =
NaN -2.63000 -2.00000 -1.63000 -1.00000 -0.63000 0.00000 0.37000 1.00000 1.37000 NA
1.00000 0.21000 0.17220 0.15000 0.26970 0.34000 0.19510 0.11000 0.24230 0.32000 NA
2.00000 0.14000 0.11480 0.10000 0.13780 0.16000 0.25450 0.31000 0.18400 0.11000 NA
The code above used linear interpolation. If you want other options, you can type help interp1 to see more.

Copy element of matrix to a vector MATLAB

Assume we have a matrix A (2x5), with the first row containing the numbers:
1 2 3 5 7
and the second row:
0.4 0.1 0.2 0.1 0.2
Also, there is a 10 dimensional vector B with numbers 1,2,3...10.
How can I create a new 10 dimensional vector C that will only contain the values of A (second row) when A(1,:) == B, else 0.
So the new vector C should have the form:
0.4 0.1 0.2 0 0.1 0 0.2 0 0 0
(add zero for the cells of B that are not in A).
I tried this solution but I have a problem due to the difference in dimensions between A and B.
for i=1:53
if B(i) == A(1,i)
C{1,i} = A(2,i);
else
C{1,i}=0;
end
end
Index exceeds matrix dimensions.
It's not very clear what you're after, but this at least gives the desired output:
A = [1 2 3 5 7; 0.4 0.1 0.2 0.1 0.2];
B = 1:10;
[tf,loc] = ismember(A(1,:), B);
C = zeros(1,10);
C(loc(tf)) = A(2,tf)
[I'm assuming you mean 10 element vector, rather than 10 dimensional...]
If you just want to use the first row of A as indices and the second row as assigned values, then you don't need to use B at all and you can do something like this:
A = [1 2 3 5 7; 0.4 0.1 0.2 0.1 0.2];
C = zeros(1,10);
C(A(1,:)) = A(2,:)
How about removing the for loop and do it inline using ismember function:
A = [1 2 3 5 7; 0.4 0.1 0.2 0.1 0.2];
B = 1:10;
C = zeros(1,10);
C(B(ismember(B, A(1,:)))) = A(2,ismember(A(1,:),B));
Hint: Even if we happen to have a value in A(1,:) which B does not have, this solution will work.
Using ismember and a for loop:
clc; clear;
A=[
1 2 3 5 7;
0.4 0.1 0.2 0.1 0.2
];
B = 1:10;
C = zeros(1,10);
for j = 1:10
if ismember(j, A(1,:))
C(j) = A(2, A(1,:) == j);
else
C(j) = 0;
end
end
C

Matlab - finding solution for scaling a curve

Consider two curves, for example:
x = [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20];
y1 = [0 0 -0.3 -0.8 -1.1 -1 -0.5 1 1.1 1 -0.3 -0.8 -1.1 -1 -0.5 0.1 0.05 0 0 0];
y2 = [0 -0.2 -0.3 -0.8 -2 1 2.8 2.4 1.5 1.1 2.3 -0.4 -0.2 1 1.1 1.2 1.3 0.5 -0.1 0];
I'd like to write a generalized algorithm that takes in x, y1, and y2, and scales y1 by a global scale factor, f, such that the new value of y2-y1 is as close as possible to 0. That is, y2-f*y1 is as close to 0 as possible.
How can I do this?
Try this:
% Create a function that you want to minimize
func = #(f, y1, y2)abs(sum(y2 - f*y1));
% Your example data
x = [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20];
y1 = [0 0 -0.3 -0.8 -1.1 -1 -0.5 1 1.1 1 -0.3 -0.8 -1.1 -1 -0.5 0.1 0.05 0 0 0];
y2 = [0 -0.2 -0.3 -0.8 -2 1 2.8 2.4 1.5 1.1 2.3 -0.4 -0.2 1 1.1 1.2 1.3 0.5 -0.1 0];
% Plot the before
figure()
plot(x, y2); hold all;
plot(x, y1)
% Find the optimum scale factor
f_start = 0; % May want a different starting point
f = fminsearch(#(f) func(f, y1, y2), f_start);
disp(['Scale factor = ' num2str(f)]) % print to the output
% Plot the after (scaled data)
figure()
plot(x, y2); hold all;
plot(x, f*y1)
For more information see the docs on anonymous functions and fminsearch (see example #2).
EDIT
Here is the output of the above script:
Scale factor = -2.9398
Before
After
As you can see the difference between the functions is minimized (area where y1 is greater than y2 is about the same as the area where y1 is less than y2). If you want the lines to match up as close as possible then you need to modify the minimization function like so:
func = #(f, y1, y2)sum(abs(y2 - f*y1));
I had to modify the test data for this case as it appears the data was already lined up optimally.
y1 = [0 0 -0.3 -0.8 -1.1 -1 -0.5 1 1.1 1 -0.3 -0.8 -1.1 -1 -0.5 0.1 0.05 0 0 0];
y2 = -2*y1 +1;
which gives the following output:
Scale factor = -2.9091
Before
After

Combine matrices using loop and condition in matlab

I have the following two matrices
c=[1 0 1.05
1 3 2.05
1 6 2.52
1 9 0.88
2 0 2.58
2 3 0.53
2 6 3.69
2 9 0.18
3 0 3.22
3 3 1.88
3 6 3.98]
f=[1 6 3.9
1 9 9.1
1 12 9
2 0 0.3
2 3 0.9
2 6 1.2
2 9 2.5
3 0 2.7]
And the final matrix should be
n=[1 6 2.52 3.9
1 9 0.88 9.1
2 0 2.58 0.3
2 3 0.53 0.9
2 6 3.69 1.2
2 9 0.18 2.5
3 0 3.22 2.7]
The code I used gives as a result only the last row of the previous matrix [n].
for j=1
for i=1:rs1
for k=1
for l=1:rs2
if f(i,j)==c(l,k) && f(i,j+1)==c(l,k+1)
n=[f(i,j),f(i,j+1),f(i,j+2), c(l,k+2)];
end
end
end
end
end
Can anyone help me on this?
Is there something more simple?
Thanks in advance
You should learn to use set operations and avoid loops wherever possible. Here intersect could be extremely useful:
[u, idx_c, idx_f] = intersect(c(:, 1:2) , f(:, 1:2), 'rows');
n = [c(idx_c, :), f(idx_f, end)];
Explanation: by specifying the 'rows' flag, intersect finds the common rows in c and f, and their indices are given in idx_c and idx_f respectively. Use vector subscripting to extract matrix n.
Example
Let's use the example from your question:
c = [1 0 1.05;
1 3 2.05
1 6 2.52
1 9 0.88
2 0 2.58
2 3 0.53
2 6 3.69
2 9 0.18
3 0 3.22
3 3 1.88
3 6 3.98];
f = [1 6 3.9
1 9 9.1
1 12 9
2 0 0.3
2 3 0.9
2 6 1.2
2 9 2.5
3 0 2.7];
[u, idx_c, idx_f] = intersect(c(:, 1:2) , f(:, 1:2), 'rows');
n = [c(idx_c, :), f(idx_f, end)];
This should yield the desired result:
n =
1.0000 6.0000 2.5200 3.9000
1.0000 9.0000 0.8800 9.1000
2.0000 0 2.5800 0.3000
2.0000 3.0000 0.5300 0.9000
2.0000 6.0000 3.6900 1.2000
2.0000 9.0000 0.1800 2.5000
3.0000 0 3.2200 2.7000
According to this answer on Mathworks support you can use join from the statistics toolbox, specifically in your case, an inner join.
Unfortunately I don't have access to my computer with matlab on it, but give it a try and let us know how/if it works.
You can reduce the number of loops by comparing both the first and second columns of at once, then using the "all" function to only collapse the values if they both match. The following snippet replicates the "n" array you had provided.
n = [];
for r1 = 1:size(c, 1)
for r2 = 1:size(f,1)
if all(c(r1, [1 2]) == f(r2, [1 2]))
n(end+1, 1:4) = [c(r1,:) f(r2,3)];
end
end
end
If you insist on doing this in a loop you need to give n the proper dimension according
to the loop counter you are using, or concatenate it to itself of each iteration (this can be very slow for big matrices). For example, writing:
for j=1
for i=1:rs1
for k=1
for l=1:rs2
m=m+1;
if f(i,j)==c(l,k) && f(i,j+1)==c(l,k+1)
n(m,:)=[f(i,j),f(i,j+1),f(i,j+2), c(l,k+2)];
end
end
end
end
end
will save into the m-th row the for numbers when the loop reaches a counter value of m.
However, just be aware that this can be done also without a nested loop and an if condition, in a vectorized way. For example, instead of the condition if f(i,j)==c(l,k)... you can use ismember etc...
How about without any for loops at all (besides in native code)
mf = size(f,1);
mc = size(c,1);
a = repmat(c(:,1:2),1,mf);
b = repmat(reshape((f(:,1:2))',1,[]),mc,1);
match = a == b;
match = match(:, 1 : 2 : 2*mf) & match(:, 2 : 2 : 2*mf);
crows = nonzeros(diag(1:mc) * match);
frows = nonzeros(match * diag(1:mf));
n = [c(crows,:),f(frows,3)]

Remove any column with same values in MATLAB

I need to remove any column in a matrix which has same values in it. I designed it using a for-loop in MATLAB. I wanted to know if a better/faster way exists using vectorization.
mat = [ 0.56 0.2 1 0 45; 0.566 0.2 4 0 45; 0.52 0.2 6 0 45; 0.56 0.2 6 0 41 ];
[row col] = size(mat) ;
bitmat = true(1,col) ;
for i = 2:row, tf = (mat(i-1,:) == mat(i,:)) ; bitmat = bitmat & tf ; end
mat(:,bitmat) = [] ;
Thanks!
Here's a simple one-liner using the functions DIFF and ANY:
mat = mat(:,any(diff(mat,1)));