how to fit a curve in the form of A = (L^x)(D^y) in Matlab? - matlab

i have some response data as vector A where the variables are L and D.
I just want to find the coefficients for L and D which will fit my data in the form mentioned in the title.
I want to fit a curved line, and not a surface.
I feel it should be fairly simple, but reading a few old answers also didn't help my case.
Is there some easy way to do this?
In case u want to see the data, here it is:
A = [0 0.06 0.12 0.44 0.56 0.94 1 1 0 0.04 0.58 0.74 0.86 1 1]
L = [100 100 100 100 100 100 100 100 43.7 49.7 56 61.5 65 77 93.8]
D = [11.3 10.1 8.9 8.5 8.1 7.7 6.5 5.3 5 5 5 5 5 5 5]
Thanks a lot.
More info:
I wrote the above equation as logA = xlogL + ylogD, and tried to use
X = [ones(size(logL)) logL logD];
b = regress(logA,X);
but Matlab didn't return any coefficients, it just gave b = NaN NaN NaN

Jos from mathworks forum gave me the correct answer. Here it is:
nlm = fitnlm([L(:) D(:)], A, 'y~(x1^b1)*(x2^b2)', [0 0])
In case you dont have fitnlm, NonLinearModel.fit will also do. In fact, I used the latter.
Hope this helps someone.

Related

Resampling of an audio signal with new time array

I am asking an upgraded and more focused version of my query as I think I remained unable to explain properly in my last query.
I want to down sample my signal based on new time array.
I have time array and sample array.
t = [0 2 3 7 8 9 10 11 12 17 18 19 20];
A = [0 0 1 2 3 5.2 0 -1.4 0 2 2.7 2 2.3];
New time array is:
Tx = 1:4:25;
I am using interpolation proposed by Andrei Davydov on stackoverflow but I think I am at fault some point.
Can someone point me out where I am at fault? Thanks a lot in advance.
If the value of A is same at Tx as w.r.t (t)then use that value and if no value is found interpolate value and assign that value to new time .
Example Code:
t = [0 2 3 7 8 9 10 11 12 17 18 19 20 ];
A = [0 0 1 2 3 5.2 0 -1.4 0 2 2.7 2 2.3 ];
plot(t,A)
Tx = 1:4:25;
B = interp1(t,A,Tx); %re-make example data to have decimal points on the x-axis
y = resample(B, 1, 2);
T = 0.05;
Ty = T / (1 / 2);
ty = (0:length(y)-1)*Ty;
figure
plot(Tx,B,'b')
hold on
plot(ty,y,'r')
plot(t,A,'g')
hold off
Link to my previous question is attached here.
Resampling of time signal in MATLAB
Note :
This is now exactly what i want so this is more clear and mature.
I am using MATLAB version 2012b so please provide me solution w.r.t that as there are few Matlab build in command which don't work in 2012b.
The main Your problem - you try to extrapolate, as original time ends at 20, but Yours ends at 25. Try this code:
clc
t = [0 2 3 7 8 9 10 11 12 17 18 19 20 ];
A = [0 0 1 2 3 5.2 0 -1.4 0 2 2.7 2 2.3 ];
Tx = 1:4:25; % Are you sure you want to extrapolate?
% max(Tx1)>max(t)
% This variant is WITHOUT extrapolation
B = interp1(t,A,Tx);
% This variant is WITH extrapolation and WHOLE time-series interpolated
% cubically
extrapBcub=interp1(t,A,Tx,'pcchip');
% If you want to have linear (default) interpolation, but cubic
% extrapolation then
extrapBlin=[B(~isnan(B)), extrapBcub(isnan(B))];
It gives the following figure:
f=figure('Position',[50 50 1500 800])
h1=subplot(1,2,1);
hold all
h(1)=plot(t,A,'-ok','LineWidth',3)
h(2)=plot(Tx,B,'-ob','LineWidth',9)
h(3)=plot(Tx,extrapBcub,':or','LineWidth',7)
h(4)=plot(Tx,extrapBlin,'-og','LineWidth',3)
xlabel('time')
ylabel('signal')
set(gca,'Box','off','Color','none','FontSize',14,'LineWidth',2)
legend({'Original data','No extrapolation','Cubic all',...
'Linear interpolation+cubic extrapolation'},'Location','SouthOutside',...
'FontSize',22)
legend boxoff
h2=subplot(1,2,2);
hold all
h3 = copyobj(h(end:-1:1), h2) % copy plots just for scaling
ylim([-2 6])
xlabel('time')
ylabel('signal')
set(gca,'Box','off','Color','none','FontSize',14,'LineWidth',2)

mean based on maximum value of a matrix

This post follows up another post: find common value of one matrix in another matrix
As I explained there, I have one matrix MyMatrix 2549x13double
Few example lines from MyMatrix:
-7.80 -4.41 -0.08 2.51 6.31 6.95 4.97 2.91 0.66 -0.92 0.31 1.24 -0.07
4.58 5.87 6.18 6.23 5.20 4.86 5.02 5.33 3.69 1.36 -0.54 0.28 -1.20
-6.22 -3.77 1.18 2.85 -3.55 0.52 3.24 -7.77 -8.43 -9.81 -6.05 -5.88 -7.77
-2.21 -3.21 -4.44 -3.58 -0.89 3.40 6.56 7.20 4.30 -0.77 -5.09 -3.18 0.43
I have identified the maximum value for each row of matrix MyMatrix as following:
[M Ind] = max(MyMatrix, [], 2);
Example lines I obtain in M:
6.95
6.23
3.24
7.20
Now, I would like to select in MyMatrix the 2 values before and after the maximum value as found in M, as I will need to calculate the average of these 5 values. So, in the example, I would like to select:
2.51 6.31 6.95 4.97 2.91
5.87 6.18 6.23 5.20 4.86
-3.55 0.52 3.24 -7.77 -8.43
3.40 6.56 7.20 4.30 -0.77
and to create a new column in MyMatrix with the mean of these 5 values.
Following the code by #Dan, taken from the previous post:
colInd = bsxfun(#plus,PeakInd, -2:2);
MyMatrixT = MyMatrix.';
rowIndT = colInd.';
linIndT = bsxfun(#plus,rowIndT,0:size(MyMatrixT,1):size(MyMatrixT,1)*(size(MyMatrixT,2)-1));
resultT = MyMatrixT(linIndT);
result = resultT.';
mean(result,2)
MyMatrix = [MyMatrix, mean(result,2)];
Here is the new part of the post, regarding the issue when the maximum value is near the edges.
When the maximum is the first or last column of MyMatrix, I would like to have NaN.
Instead, when the maximum is in the second column, I would like to calculate the mean considering one column preceding the maximum, the maximum value, and two columns following the maximum.
While, when the maximum is in the second last column, I would like to consider the two columns preceding the maximum, the maximum value, and only one column following the maximum.
I would be extremely grateful if you could help me. Many thanks!
Instead of creating a 2D array with NaNs plus nanmean, you could use min/max to get the right indexes:
pad = 2;
[~, Ind] = max(MyMatrix, [], 2);
minCol = max(1, Ind-pad);
maxCol = min(size(MyMatrix, 2), Ind+pad);
result = arrayfun(#(row, min_, max_) mean(MyMatrix(row, min_:max_)),...
(1:size(MyMatrix, 1)).', minCol, maxCol);
If you have the Image Processing Toolbox, you can also use padarray, e.g.
B = padarray(magic(5),[0 2],NaN);
B =
NaN NaN 17 24 1 8 15 NaN NaN
NaN NaN 23 5 7 14 16 NaN NaN
NaN NaN 4 6 13 20 22 NaN NaN
NaN NaN 10 12 19 21 3 NaN NaN
NaN NaN 11 18 25 2 9 NaN NaN
(...if you don't have padarray, just manually add 2 NaN columns on either side) then using some bsxfun + sub2ind we get the desired result:
pad_sz = 2;
B = padarray(magic(5),[0 pad_sz],NaN);
[~,I] = nanmax(B,[],2); % by using nanmax we "explicitly" say we ignore NaNs.
colInd = bsxfun(#plus,-pad_sz:pad_sz,I);
linearInd = sub2ind(size(B), repmat((1:5).',[1,size(colInd,2)]), colInd);
picks = B(linearInd);
res = nanmean(picks,2);
% or combine the last 3 lines into:
% res = nanmean(B(sub2ind(size(B), repmat((1:5).',[1,size(colInd,2)]), colInd)),2);
res = res + 0./~(I == pad_sz+1 | I == size(B,2)-pad_sz); %add NaN where needed.

two matrix output into a single txt file in matlab

I've two matrix output from two Matlab scripts and I'd like to write both results in different columns of the same GUI output in a txt file.
Could you please help me?
I tried some different methods (try to create cell array, or use fprintf for arrays of different sizes) and understood that #GameOfThrows's method is really works.
I realize it in this way:
x = [1 2 3 4 5];
y = [10 20 30 40 50 60 70 80 90];
[m,i] = max( [numel(x) numel(y)]);
if i == 1
y(end+1:numel(x))=NaN;
else
x(end+1:numel(y))=NaN;
end
a = [x; y];
fileID = fopen('data1.txt','w');
fprintf(fileID,'%6.2f %12.2f\r\n',a);
My data1.txt:
1.00 10.00
2.00 20.00
3.00 30.00
4.00 40.00
5.00 50.00
NaN 60.00
NaN 70.00
NaN 80.00
NaN 90.00

Combine data matrices of different frequencies

In MATLAB, how could I combine two matrices of data measured at different frequencies such that the result is indexed at the higher frequency? Since the data measured at the lower frequency will have many unknown values in the result, I would like to replace them with the last known value in the matrix. There is a lot of data so a vectorized solution would be preferred. I've added some sample data below.
Given:
index1 data1 index2 data2
1 2.1 2 30.5
2 3.3 6 32.0
3 3.5 9 35.0
4 3.9 13 35.5
5 4.5 17 34.5
6 5.0 20 37.0
7 5.2 ... ...
8 5.7
9 6.8
10 7.9
... ...
Result:
index1 data1 data2
1 2.1 NaN
2 3.3 30.5
3 3.5 30.5
4 3.9 30.5
5 4.5 30.5
6 5.0 32.0
7 5.2 32.0
8 5.7 32.0
9 6.8 35.0
10 7.9 35.0
... ... ...
EDIT:
I think the following post is close to what I need, but I'm not sure how to transform the solution to fit my problem.
http://www.mathworks.com/matlabcentral/newsreader/view_thread/260139
EDIT (Several Months Later):
I've recently come across this excellent little function that I think may be of use to anyone who lands on this post:
function yi = interpLast(x,y,xi)
%INTERPLAST Interpolates the input data to the last known value.
% Note the index data should be input in ASCENDING order.
inds = arrayfun(#findinds, xi);
yi = y(inds);
function ind = findinds(val)
ind = find(x<=val,1,'last');
if isempty(ind)
ind = 1;
end
end
end
Credit goes here: http://www.mathworks.com/support/solutions/en/data/1-48KETY/index.html?product=SL&solution=1-48KETY
The problem is one of run length decoding. See section 15.5.2 of Matlab array manipulation tips and tricks (which is an eye-opening read for any Matlab enthusiast).
Here's using the method with your example (I'm using octave but the code is identical for Matlab):
octave:33> a=[2,30.5;6,32;9,35;13,35.5;17,34.5;20,37]
a =
2.0000 30.5000
6.0000 32.0000
9.0000 35.0000
13.0000 35.5000
17.0000 34.5000
20.0000 37.0000
octave:34> i=a(:,1)-1
i =
1
5
8
12
16
19
octave:35> j=zeros(1,i(end))
j =
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
octave:36> j(i(1:end-1)+1)=1
j =
0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0
octave:37> j(1)=1
j =
1 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0
octave:38> val=a(:,2)
val =
30.500
32.000
35.000
35.500
34.500
37.000
octave:39> x=val(cumsum(j))
x =
30.500
32.000
32.000
32.000
32.000
35.000
35.000
35.000
35.500
35.500
35.500
35.500
34.500
34.500
34.500
34.500
37.000
37.000
37.000
And pad the beginning with NaN as needed.
I recently had the same problem as you: I had data, measured by different systems, which had to be synchronized and processed.
My solution consisted of putting the measurement data and time information (frequency, time at start of measurements) in a class object. Then I implemented a multiplication, addition, etc. method for that class that automatically took care of all the necessary things, being:
upsampling the lower frequency signal (with linear interpolation (interp1)
shifting one of the signals, so the data lines up in time
cutting off the non-overlapping data set at beginning and end (with two different systems you never start or stop measuring at the same time, so there is some excess data)
actually performing the multiplication
returning the result as a new class object
Next to that there were other functions of which you can guess what they do: plot, lpf, mean, getTimeAtIndex, getIndexAtTime, ...
This allowed me to simply do
signalsLabview = importLabViewSignals(LabViewData);
signalsMatlab = importMatlabSignals(MatlabData, 100); %hz
hydrPower = signalsLabview.flow * signalsMatlab.pressure;
plot(hydrPower);
or things like that. If you have a lot of these signals on which you have to do some math, this really helps and results in clear code. Otherwise you have a lot of general code just for doing the syncing, shifting, trimming around each operation. Also for quickly checking things it's easy.
If you have to do this things a lot, I think it's definitely worth investing some time in it to build a proper framework.
Unfortunately I don't think I can disclose this code (IP and such), but it wasn't rocket science.

Matlab: "grouping mean"

Suppose I have the vectors:
y = [1 1.01 1.02 1.03 2 2.01 2.02 3 3.01 3.02 3.03];
c = [0 0 0 0 1 1 1 2 2 2 2 ];
Is there a vectorized way to get a "grouping mean", that is, the mean value of y for each unique value of c? (This is a simplified example; I have something similar but the vector size is in the thousands and there are hundreds of values of c)
I can do it in a for-loop, just wondering if it could be vectorized. Here's my for-loop implementation:
function [my,mc] = groupmean(y,c)
my = [];
mc = [];
for ci = unique(c)'
mc(end+1) = ci;
my(end+1) = mean(y(c==ci));
end
Short answer:
>> y = [1 1.01 1.02 1.03 2 2.01 2.02 3 3.01 3.02 3.03];
>> c = [0 0 0 0 1 1 1 2 2 2 2 ];
>> groupmeans = accumarray(c'+1,y',[],#mean)
groupmeans =
1.015
2.01
3.015
To explain the above: accumarray is a bit cryptic, but extremely useful and worth getting to know (and very fast). The first input is a vector (they need to be column vectors, which is why it's c' and y') that groups the rows of the second input vector. The elements need to be positive integers (for some reason), which is why I've added 1 to c'. The last input is a handle to a function that is applied as an accumulator to each group of the values in y.
Hope that makes sense! If not, doc accumarray :)