help me to do nonlinear fitting use lsqcurvefit

5 ビュー (過去 30 日間)
nirwana
nirwana 2024 年 12 月 19 日
コメント済み: Mathieu NOE 2025 年 1 月 7 日
この 質問 は Mathieu NOE さんによってフラグが設定されました
please help me, I am begginer in matlab and thsi is my first time doing nonlinear fitting, I have problem to fit data with the model non linear fitting (here I use lsqcurvefit), the flow of my code is something like this
  • get autocorrelation function(ACF), isolate half of it
  • get envelop of ACF,
  • fit envelope of ACF with equation E=1/t^2 exp (-2*pi*f*Qc) to get optimum Qc (with f is my freq=3, t is time lapse)
here the ilustration from source that i use
Here is my code, but i don't really trust my self since the Qc value is not match with range (is uppose to get Wc between 100-400) but using my code i get thousand. Please is anyone can help me to clarify my code
clear; clc; %close all;
% Define the folder containing ACF files
folder_path = pwd; % Adjust the path to your ACF files
file_pattern = 'cBPCen3_s6h_ACF_HGSD_HHZ_2023-12_2023-12-01T00_00.txt'; % Match ACF filenames
files = dir(fullfile(folder_path, file_pattern));
% Define the model function for nonlinear fitting
f=3;
m=2;
model = @(Qc, t) (1 ./ t.^m).*exp(-2 * pi * f * t ./ Qc);
% Initial guess for Qc
Qc_initial = 110;
options = optimset('Display', 'off'); % Suppress output
% File to store Qc_fitted_envelope results
results_file = fullfile(folder_path, 'Qc_fitted_envelope_results.txt');
if exist(results_file, 'file')
delete(results_file); % Clear old results if file exists
end
% Loop over each ACF file
for i = 1:length(files)
% Load ACF file
file_name = files(i).name;
file_path = fullfile(folder_path, file_name);
acf = load(file_path);
% Extract the ACF data
y_obs = acf((length(acf)/2):(end))'; % Adjust this as needed
% Compute the squared envelope of the ACF
envel = envelope(y_obs,5,'peak')';
envel = envel(1:round(0.5 * length(envel)));
tenvel = (1:length(envel))';
% Perform nonlinear optimization for envelope
Qc_fitted_envelope = lsqcurvefit(@(Qc, tenvel) model(Qc, tenvel), Qc_initial, tenvel, envel, [], [], options);
y_fitted_envelope = model(Qc_fitted_envelope, tenvel);
rms_error_envelope = sqrt(mean((envel - y_fitted_envelope).^2));
% Plot results for each file - DEACTIVATE FOR BIG LOOPING
figure;
plot(tenvel/100,envel, 'k-', 'DisplayName', 'Envelope (Observed)');
hold on;
plot(tenvel/100, y_fitted_envelope, 'r-', 'LineWidth', 1.5, 'DisplayName', sprintf('Envelope (Fitted, RMS=%.4f)', rms_error_envelope));
xlabel('Time (s)');
ylabel('Amplitude');
title(sprintf('Envelope Fit - Qc = %.4f, RMS Error = %.4f', Qc_fitted_envelope, rms_error_envelope));
legend('Location', 'northeast');
%set(gca, 'YScale', 'log')
grid on;
% Save the envelope to a text file - DO YOU REALLY NEED SAVE THIS??
% output_file_name = ['envel_' file_name];
% output_file_path = fullfile(folder_path, output_file_name);
% save(output_file_path, 'envelope', '-ascii');
% fprintf('Envelope saved to %s\n', output_file_name);
% Save the Qc_fitted_envelope result to the text file
fid = fopen(results_file, 'a'); % Open in append mode
fprintf(fid, '%s\t%.4f\n', file_name, Qc_fitted_envelope);
fclose(fid);
% Display results in the command window
fprintf('File: %s\n', file_name);
fprintf(' Envelope: Fitted Qc = %.4f, RMS Error = %.4f\n\n', Qc_fitted_envelope, rms_error_envelope);
end
  6 件のコメント
Mathieu NOE
Mathieu NOE 2025 年 1 月 2 日
maybe you should share the publication so we could see the larger context
nirwana
nirwana 2025 年 1 月 4 日
hi @Mathieu NOE, happy new year too. Thanks for still reply in this post.
After read again my code, I realize that I put acf time vector without considering my sampling rate. Kindly check here my revision. parameter "part" i used to chose how much data that I want to fit in. 1 means 100percent data i used to fit, but eventhough i change it to 50% or 25%, the Qc that i get sems does not really change.
Related with second question
"a second point is why the first plot (of A) shows a peak amplitude of autocorrelation which is not one ? "
I also has no ide about that. the author does not explain it in detail. (the publication already mention by @William Rose below, thanks a lot)
the Please give me advise if something that strange in my code.

サインインしてコメントする。

採用された回答

William Rose
William Rose 2025 年 1 月 4 日
I agree with the comments of @Mathieu NOE. He raises very good points - including the recommendation to share the source of the figure.
The source is https://academic.oup.com/gji/article/168/3/1029/2042341: Wegler & Sens-Schonfelder. 2007. "Fault zone monitoring with passive image interferometry. Geophys J Intern 168(3): 1029–1033. https://doi.org/10.1111/j.1365-246X.2006.03284.x
There are two things to note in order to get the fit to work:
1. The y-axis units are "arbitrary", and the authors say "the envelope E(t) should obey E(t) ∼ 1/t2  exp(−2πft/Qc)". The "~" above means "proportional to". Therefore, we must fit the unknown constant of proportionality. Thus the predicted y-value is
yPred=A*t.^(-2).*exp(-2*pi*f*t/Qc); where A and Qc are unknown, and f=3 Hz (specified in the text).
2. The figure shows E and fitted E on a logarithmic y-axis. I had unsatisfactory results when I used fmincon() and when I used fitdata() to estimate A and Qc by minimizing the sum squared error between y and yPred. Specifically, fmincon() and fitdata() stop on the initial quess, at least when the initial guess for [Qc,A] is [1,1]. This happens because the objective function (the sum squared error) is quite insensitive to changes in Qc and A. To fix this, I minimize the sum squared difference between log(y) and log(yPred). With this adjustment, the minimization routine gives good results, as shown in the plot below.
My fitted Qc=133; the authors get Qc=150. The difference may be due to the fact that I digitized data from a figure, and interpolated between my samples. Therefore the data I fitted does not exactly equal the data which Wegler & Sens-Schonfelder fitted.
Code to fit the data, and the file of points digitized from Figure 2B, are attached.
  9 件のコメント
William Rose
William Rose 2025 年 1 月 7 日
@Mathieu NOE, you're welcome, and thank you for showing how to do the minimizaiton with fminsearch(). Thank you for showing that, when using data digitized from the published figure 2B, you get a fit like the fit I found (Qc=133). Your use of norm() as the objective function is more elegant than what I did. The value r^2=0.28 for the fit is rather low. One could argue that, since we fitted the published data by minimizing the differences of the log of the data and the log of the model, we should use the logs when computing r^2. I bet that if we did that, r^2 for the fit to the published data would be larger.
@Mathieu NOE, thank you for attaching the interesting and useful function env_secanrt.m.
@Mathieu NOE says "the ACF data looks very different from the digitized plot" I agree. Here is a plot comparing the squared autocorrelation of W. & S.-S., 2007 to the squared autocorrelation of nirwana.
data1=load('cBPCen3_s6h_ACF_HGSD_HHZ_2024-04_2024-04-05T12_00.txt');
acf1=data1(5001:end);
acf1sq=acf1.^2;
t1=(0:length(acf1)-1)'/100;
data2=load('plot-data.csv');
t2=data2(:,1); acf2sq=data2(:,2);
semilogy(t1,acf1sq,'-r.',t2,acf2sq,'-bx')
title('Seismogram ACF^2'); xlabel('Time (s)'); ylabel('ACF^2')
legend('nirwana','W&SS, 2007'); grid on
@Mathieu NOE and I found Qc=133 when fitting the log of the WSS data from 2 to 20 Hz. This Qc is close to the value Qc=150 reportsed by WSS 2007. @Mathieu NOE says "I get bizarre results with squared acf or squared envelope in both cases", when fitting the data supplied by @nirwana. When I fit the ACF^2 data of @nirwana, I get the following:
% Fit the nirwana data from t=2 to t=20.
t1fit=t1(t1>=2 & t1<=20);
acf1sqfit=acf1sq(t1>=2 & t1<=20);
f=1; % center frequency of bandpass filter used to record seismogram (Hz)
fnc = @(p)sumsqerr(p,t1fit,acf1sqfit,f);
p0=[1,1]; % initial guess for [Qc,A]
p=fmincon(fnc,p0,[],[],[],[],[1e-3,0],[1e4,1e6]);
Local minimum possible. Constraints satisfied. fmincon stopped because the size of the current step is less than the value of the step size tolerance and constraints are satisfied to within the value of the constraint tolerance.
Qc=p(1);
A=p(2);
fprintf('Qc=%.2g, A=%.2g.\n',p)
Qc=99, A=0.0035.
% Plot ACF^2 from nirwana and ACF^2 predicton from the fit
yPred=A*t1fit.^(-2).*exp(-2*pi*f*t1fit/Qc);
figure; semilogy(t1,acf1sq,'-r.',t1fit,yPred,'-g.')
title(['ACF^2, Qc=',num2str(Qc,'%.1f')]);
xlabel('Time (s)'); ylabel('ACF^2')
legend('nirwana','Model Fit'); grid on
The fitted value Qc=99 is within the bounds of reason. The fit includes adjustable parameter A, a scaling factor.
Mathieu NOE
Mathieu NOE 2025 年 1 月 7 日
yep
definitively there is a big difference between the two data sets . No just a magnitude factor (that would simply lead to different A values) but the shape / slope are not the same. There must be a reason why, but this has probably to be searched in the process used to generate the data.

サインインしてコメントする。

その他の回答 (0 件)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by