intersection of interpolated line and an interpolated curve matlab - matlab

I have astraightline with the following data
xinter=[1.13 1.36 1.62 1.81 2.00 2.30 2.61 2.83 3.05 3.39]
yinter=[0.10 0.25 0.40 0.50 0.60 0.75 0.90 1.00 1.10 1.25]
and I want to find the intersection with a result of an interpolated data
of a curve such as below
a50= [0.77 0.73 0.77 0.85 0.91 0.97 1.05 1.23 1.43 1.53 1.62 1.71 1.89 2.12 2.42];
a25= [0.51 0.60 0.70 0.80 0.85 0.90 0.96 1.09 1.23 1.30 1.36 1.41 1.53 1.67];
vel25=[0.43 0.35 0.30 0.27 0.25 0.24 0.22 0.21 0.22 0.24 0.25 0.27 0.30 0.35];
vel50=[0.68 0.57 0.49 0.43 0.40 0.38 0.36 0.34 0.36 0.38 0.40 0.43 0.49 0.57 0.68 ];
% back up original data, just for final plot
bkp_a50 = a50 ; bkp_vel50 = vel50 ;
% make second x vector monotonic
istart = find( diff(a50)>0 , 1 , 'first') ;
a50(1:istart-1) = [] ;
vel50(1:istart-1) = [] ;
% prepare a 3rd dimension vector (from 25 to 50)
T = [repmat(25,size(a25)) ; repmat(40,size(a50)) ] ;
% merge all observations together
A = [ a25 ; a50] ;
V = [vel25 ; vel50] ;
% find the minimum domain on which data can be interpolated
% (anything outside of that will return NaN)
Astart = max( [min(a25) min(a50)] ) ;
Astop = min( [max(a25) max(a50)] ) ;
% use the function 'griddata'
[TI,AI] = meshgrid( 25:40 , linspace(Astart,Astop,10) ) ;
VI = griddata(T,A,V,TI,AI) ;
% plot all the intermediate curves
%plot(AI,VI)
hold on
% the original curves
%plot(a25,vel25,'--k','linewidth',2)
%plot(bkp_a50,bkp_vel50,'--k','linewidth',2)
% Highlight the curve at T = 30 ;
c30 = find( TI(1,:) == 40 ) ;
plot(AI(:,c30),VI(:,c30),'--r','linewidth',2)
xinter=[1.13 1.36 1.62 1.81 2.00 2.30 2.61 2.83 3.05 3.39]
yinter=[0.10 0.25 0.40 0.50 0.60 0.75 0.90 1.00 1.10 1.25]
x1inter=(AI(:,c30))';
y1inter=(VI(:,c30))';
yy2 = interp1(xinter, yinter, x1inter,'spline')
plot(xinter,yinter, '--k','linewidth',2)
idx = find((y1inter - yy2) < eps, 1); %// Index of coordinate in array
px = x1inter(idx)
py = y1inter(idx)
plot(px, py, 'ro', 'MarkerSize', 18)
But there is an error in the result when I modify x1inter

You can use piecewise polynomial curvefitting and the fzero function to find the intersection point:
pp1 = pchip(xinter,yinter); % Curve 1
pp2 = pchip(AI(:,c30),VI(:,c30)); % Curve 2
fun = #(x) ppval(pp1,x) - ppval(pp2,x); % Curve to evaluate
xzero = fzero(fun,mean(xinter)) % intersection x value
yzero = ppval(pp1,xzero)
plot(xzero, yzero, 'bo', 'MarkerSize', 18)

Related

vectorise foor loop with a variable that is incremented in each iteration

I am trying to optimise the running time of my code by getting rid of some for loops. However, I have a variable that is incremented in each iteration in which sometimes the index is repeated. I provide here a minimal example:
a = [1 4 2 2 1 3 4 2 3 1]
b = [0.5 0.2 0.3 0.4 0.1 0.05 0.7 0.3 0.55 0.8]
c = [3 5 7 9]
for i = 1:10
c(a(i)) = c(a(i)) + b(i)
end
Ideally, I would like to compute it by writting:
c(a) = c(a) + b
but obviously it would not give me the same results since I have to recalculate the value for the same index several times so this way to vectorise it would not work.
Also, I am working in Matlab or Octave in case that this is important.
Thank you very much for any help, I am not sure that it is possible to be vectorise.
Edit: thank you very much for your answers so far. I have discovered accumarray, which I did not know before and also understood why changing the for loop between Matlab and Octave was giving me such different times. I also understood my problem better. I gave a too simple example which I thought I could extend, however, what if b was a matrix?
(Let's forget about c at the moment):
a = [1 4 2 2 1 3 4 2 3 1]
b =[0.69 -0.41 -0.13 -0.13 -0.42 -0.14 -0.23 -0.17 0.22 -0.24;
0.34 -0.39 -0.36 0.68 -0.66 -0.19 -0.58 0.78 -0.23 0.25;
-0.68 -0.54 0.76 -0.58 0.24 -0.23 -0.44 0.09 0.69 -0.41;
0.11 -0.14 0.32 0.65 0.26 0.82 0.32 0.29 -0.21 -0.13;
-0.94 -0.15 -0.41 -0.56 0.15 0.09 0.38 0.58 0.72 0.45;
0.22 -0.59 -0.11 -0.17 0.52 0.13 -0.51 0.28 0.15 0.19;
0.18 -0.15 0.38 -0.29 -0.87 0.14 -0.13 0.23 -0.92 -0.21;
0.79 -0.35 0.45 -0.28 -0.13 0.95 -0.45 0.35 -0.25 -0.61;
-0.42 0.76 0.15 0.99 -0.84 -0.03 0.27 0.09 0.57 0.64;
0.59 0.82 -0.39 0.13 -0.15 -0.71 -0.84 -0.43 0.93 -0.74]
I understood now that what I would be doing is rowSum per group, and given that I am using Octave I cannot use "splitapply". I tried to generalise your answers, but accumarray would not work for matrices and also I could not generalise #rahnema1 solution. The desired output would be:
[0.34 0.26 -0.93 -0.56 -0.42 -0.76 -0.69 -0.02 1.87 -0.53;
0.22 -1.03 1.53 -0.21 0.37 1.54 -0.57 0.73 0.23 -1.15;
-0.20 0.17 0.04 0.82 -0.32 0.10 -0.24 0.37 0.72 0.83;
0.52 -0.54 0.02 0.39 -1.53 -0.05 -0.71 1.01 -1.15 0.04]
that is "equivalent" to
[sum(b([1 5 10],:))
sum(b([3 4 8],:))
sum(b([6 9],:))
sum(b([2 7],:))]
Thank you very much, If you think I should include this in another question instead of adding the edit I will do so.
Original question
It can be done with accumarray:
a = [1 4 2 2 1 3 4 2 3 1];
b = [0.5 0.2 0.3 0.4 0.1 0.05 0.7 0.3 0.55 0.8];
c = [3 5 7 9];
c(:) = c(:) + accumarray(a(:), b(:));
This sums the values from b in groups defined by a, and adds that to the original c.
Edited question
If b is a matrix, you can use
full(sparse(repmat(a, 1, size(b,1)), repelem(1:size(b,2), size(b,1)), b))
or
accumarray([repmat(a, 1, size(b,1)).' repelem(1:size(b,2), size(b,1)).'], b(:))
Matrix multiplication and implicit expansion and can be used (Octave):
nc = numel(c);
c += b * (1:nc == a.');
For input of large size it may be more memory efficient to use sparse matrix:
nc = numel(c);
nb = numel(b);
c += b * sparse(1:nb, a, 1, nb, nc);
Edit: When b is a matrix you can extend this solution as:
nc = numel(c);
na = numel(a);
out = sparse(a, 1:na, 1, nc, na) * b;

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.

How to calculate the area between two curves

Based on the following code:
clear vars;
close all;
x1 = [0 0 0.01 0.09 0.1 0.11 0.2 0.3 0.35 0.50 0.64 0.8 1]
y1 = [0.05 0.10 0.15 0.20 0.25 0.30 0.38 0.42 0.45 0.48 0.52 0.86 1]
x2 = [0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 0.9 0.9 1]
y2 = [0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 0.9 0.9 1]
plot(x1, y1); hold on;
plot(x2, y2);
I need to calculate the area (green area) between the two curves, for example:
How can I calculate it?
This area is the difference of the two curves integral in the specified domain between each intersection (as mentioned by MBo). Hence, you can find the intersections using InterX and then use trapz to do this:
P = InterX([x1;y1],[x2;y2]);
area = 0;
% for each segment
% each segment is between P(1,i) and P(1, i+1)
% So we can find xsegments with idx = find(x < P(1,i+1) && x > P(1,i)) and [P(1,i) x(idx) P(1,i+1)]
% ...
area = area + abs(trapz(xsegment1i,ysegment1i) - trapz(xsegment2i,ysegment2i));
Since one of the curves is a straight line you can rotate then add up the areas from the new x axis.
The line is at 45 degrees. So the rotation matrix is
cos 45 sin 45
-sin 45 cos 45
Multiply each point in the second curve by that matrix. That gives points with the line as the new x axis. Now use area of the triangle (0.5 * width * height) to add up the areas of the fragments.

ode45 function error

I'm trying to simulate nonlinear vehicle braking system with ode45.
I take a short time to learning MATLAB. then, I don't know why error occur.
It would be very appreciated if you could point out errors, and tell me how to solve.
code 1. main script
code 2. function
code 3. errors
clear;
global m f Jw rw Fz Cd p A bw fw Nw g uw seta Te Tb T p0
x = [0 0.025 0.05 0.1 0.125 0.15 0.175 0.2 0.25 0.3 0.35 0.4 0.45 0.5 0.55 0.6 0.65 0.7 0.75 0.8 0.85 0.9 0.95 1]
y = [0 0.225 0.45 0.65 0.685 0.705 0.69 0.68 0.65 0.635 0.63 0.6275 0.625 0.6225 0.62 0.6175 0.615 0.6125 0.610 0.6075 0.6050 0.6 0.5975 0.5950]
%plot(x,y)
p0=polyfit(x,y,6)
%y=polyval(p,x)
m = 1400; f = 0.01; Jw = 0.65; rw = 0.31; Fz = 3560.0; Cd = 0.5; p = 1.202; A = 1.95;
bw = 0.0; fw = 0.0; Nw = 4; g = 9.81; uw = 0.0; seta = 0.0; Te = 0.0; Tb = 1000.0; T = Te - Tb;
[t,i] = ode45(#dott,[0.0 1.0],[20 20]);
plot(t,i);
axis([0 1 0 20]);
legend('x1','x2');
function xdot = dott(t,x)
global m f Jw rw Fz Cd p A Nw Te Tb p0
X = [0 0.025 0.05 0.1 0.125 0.15 0.175 0.2 0.25 0.3 0.35 0.4 0.45 0.5 0.55 0.6 0.65 0.7 0.75 0.8 0.85 0.9 0.95 1];
Y = [0 0.225 0.45 0.65 0.685 0.705 0.69 0.68 0.65 0.635 0.63 0.6275 0.625 0.6225 0.62 0.6175 0.615 0.6125 0.610 0.6075 0.6050 0.6 0.5975 0.5950];
%UNTITLED2 Summary of this function goes here
%Detailed explanation goes here
xdot = zeros(2,1);
Y=p0(1,1)*((x(2)-x(1))/x(1))^6+p0(1,2)*((x(2)-x(1))/x(1))^5+p0(1,3)*((x(2)-x(1))/x(1))^4+p0(1,4)*((x(2)-x(1))/x(1))^3+p0(1,5)*((x(2)-x(1))/x(1))^2+p0(1,6)*((x(2)-x(1))/x(1))^1+p0(1,1)*((x(2)-x(1))/x(1));
xdot(1)=(-0.5*p*Cd*A(x(2)/(1+Y)*rw)*(x(2)/(1+Y)*rw)-f*m+Nw*Y*Fz)/(rw*m);
xdot(2)=(Te-Tb-rw*Y*Fz)/Jw;
end
??? Subscript indices must either be real positive integers or logicals.
Error in ==> dott at 9
xdot(1)=(-0.5*p*Cd*A(x(2)/(1+Y)*rw)*(x(2)/(1+Y)*rw)-f*m+Nw*X*Fz)/(rw*m);
Error in ==> odearguments at 98
f0 = feval(ode,t0,y0,args{:}); % ODE15I sets args{1} to yp0.
Error in ==> ode45 at 172
[neq, tspan, ntspan, next, t0, tfinal, tdir, y0, f0, odeArgs, odeFcn, ...
Error in ==> a at 14
[t,i] = ode45(#dott,[0.0 1.0],[20 20]);
I'm pretty sure your error is in this statement:
A(x(2)/(1+Y)*rw)
The way you write it, you're trying to use x(2)/(1+Y)*rw as an index to the scalar A. I guess you want to multiply is this way:
... A * (x(2) / (1 + Y) * rw) ...
To make the code more readable:
Use spaces. A long compact line is really hard to read.
Split long lines into several using three dots ...
Something like this is easier to read in my opinion:
Y = p0(1,1) * ((x(2) - x(1)) / x(1))^6 + p0(1,2) * ...
((x(2) - x(1)) / x(1))^5 + p0(1,3) * ((x(2) - x(1)) / x(1))^4 ...
+ p0(1,4) * ((x(2) - x(1)) / x(1))^3 + p0(1,5) * ((x(2) - x(1)) / ...
x(1))^2 + p0(1,6) * ((x(2) - x(1)) / x(1))^1 + p0(1,1) * ...
((x(2) - x(1)) / x(1));

finding out the scaling factors to match two curves with fmincon in matlab

This is a follow up question related to how to find out the scaling factors to match two curves in matlab?
I use the following code to figure out the scaling factors to match two curves
function err = sqrError(coeffs, x1, y1, x2, y2)
y2sampledInx1 = interp1(coeffs(1)*x2,y2,x1);
err = sum((coeffs(2)*y2sampledInx1-y1).^2);
end
and I used fmincon to optimize the result.
options = optimset('Algorithm','active-set','MaxFunEvals',10000,'TolCon',1e-7)
A0(1)=1; A0(2)=1; LBA1=0.1; UBA1=5; LBA2=0.1; UBA2=5;
LB=[LBA1 LBA2]; UB=[UBA1 UBA2];
coeffs = fmincon(#(c) sqrError(c,x1, y1, x2, y2),A0,[],[],[],[],LB,UB,[],options);
when I test with my data with the function,
x1=[-0.3
-0.24
-0.18
-0.12
-0.06 0
0.06
0.12
0.18
0.24
0.3
0.36
0.42
0.48
0.54
0.6
0.66
0.72
0.78
0.84
0.9
0.96
1.02
1.08
1.14
1.2
1.26
1.32
1.38
1.44
1.5
1.56
1.62
1.68
1.74
1.8
1.86
1.92
1.98
2.04 ] y1=[0.00
0.00
0.00
0.01
0.03
0.09
0.13
0.14
0.14
0.16
0.20
0.22
0.26
0.34
0.41
0.52
0.62
0.72
0.81
0.91
0.95
0.99
0.98
0.96
0.90
0.82
0.74
0.66
0.58
0.52
0.47
0.40
0.36
0.32
0.27
0.22
0.19
0.15
0.12
0.10 ];
x2=[-0.3
-0.24
-0.18
-0.12
-0.06 0
0.06
0.12
0.18
0.24
0.3
0.36
0.42
0.48
0.54
0.6
0.66
0.72
0.78
0.84
0.9
0.96
1.02
1.08
1.14
1.2
1.26
1.32
1.38
1.44
1.5
1.56
1.62
1.68
1.74
1.8
1.86
1.92
1.98
2.04 ]; y2=[0.00
0.00
0.00
0.00
0.05
0.15
0.15
0.13
0.11
0.11
0.13
0.18
0.24
0.33
0.43
0.54
0.66
0.76
0.84
0.90
0.93
0.94
0.94
0.91
0.87
0.81
0.75
0.69
0.63
0.55
0.49
0.43
0.37
0.32
0.27
0.23
0.19
0.16
0.13
0.10 ];
The error message shows up as follows:
??? Error using ==> interp1 at 172 NaN is not an appropriate value for
X.
Error in ==> sqrError at 2 y2sampledInx1 =
interp1(coeffs(1)*x2,y2,x1);
Error in ==> #(c)sqrError(c,x1,y1,x2,y2)
Error in ==> nlconst at 805
f =
feval(funfcn{3},x,varargin{:});
Error in ==> fmincon at 758
[X,FVAL,LAMBDA,EXITFLAG,OUTPUT,GRAD,HESSIAN]=...
Error in ==>coeffs = fmincon(#(c) sqrError(c,x1, y1, x2,
y2),A0,[],[],[],[],LB,UB,[],options);
What is wrong in the code and how should I get around with it.
Thanks for the help.
Your scaling is likely pushing the interpolated axis out of range of the x-axis of the data. i.e.
x1 < min(x2*coeffs(1)) or x1 > max(x2*coeffs(1)) for at least one x1 and the value of coeffs(1) chosen by the fitting algorithm
You can fix this by giving an extrapolation value for data outside the range. Alternately, you can use extrapolation to guess at these values. So try one of these
y2sampledInx1 = interp1(coeffs(1)*x2,y2,x1,'Linear', 'Extrap');
y2sampledInx1 = interp1(coeffs(1)*x2,y2,x1,'Linear', Inf);
y2sampledInx1 = interp1(coeffs(1)*x2,y2,x1,'Linear', 1E18); %if Inf messes with the algorithm