How Can fit a curve to step function? - matlab

I am trying to fit curve to a step function. I tried no. of approaches like using sigmoid function,using ratio of polynomials, fitting Gauss function to derivative of step, but none of them are looking okay. Now, I came up with the idea of creating a perfect step and compute convolution of perfect step to a Gauss function and find best fit parameter using non-linear regression.
But this is also not looking good.
I am writing here code both for Sigmoid and convolution approach.
First with Sigmoid Function fit:
Function:
function d=fit_sig(param,x,y)
a=param(1);
b=param(2);
d=(a./(1+exp(-b*x)))-y;
end
main code:
a=1, b=0.09;
p0=[a,b];
sig_coff=lsqnonlin(#fit_sig,p0,[],[],[],xavg_40s1,havg_40s1);
% Plot the original and experimental data.
sig_new = sig_coff(1)./(1+exp(-sig_coff(2)*xavg_40s1));
d= havg_40s1-step_new;
figure;
plot(xavg_40s1,havg_40s1,'.-r',xavg_40s1,sig_new,'.-b');
xlabel('x-pixel'); ylabel('dz/dx (mm/pixel)'); axis square;
This is not working at all. I think my initial guesses are wrong. I tried multiple numbers but could not get it correct. I tried using curve fitting tool too but that is also not working.
Code for creating perfect step:
h=ones(1,numel(havg_40s1)); %height=1mm
h(1:81)=-0.038;
h(82:end)=1.002; %or 1.0143
figure;
plot(xavg_40s1,havg_40s1,'k.-', 'linewidth',1.5, 'markersize',16);
hold on
plot(xavg_40s1,h,'.-r','linewidth',1.5,'markersize',12);
Code using convolution approach:
Function:
function d=fit_step(param,h,x,y)
A=param(1);
mu=param(2);
sigma=param(3);
d=conv(h,A*exp(-((x-mu)/sigma).^2),'same')-y;
end
main code:
param1=[0.2247 8.1884 0.0802];
step_coff=lsqnonlin(#fit_step,param1,[],[],[],h,dx_40s1,havg_40s1);
% Plot the original and experimental data.
step_new = conv(h,step_coff(1)*exp(-((dx_40s1-step_coff(2))/step_coff(3)).^2),'same');
figure;
plot(xavg_40s1,havg_40s1,'.-r',xavg_40s1,step_new,'.-b');
This is close but edge of step has been shifted plus corners are looking sharper than measured step.
Could someone please help me out the best way to fit a step function or any suggestion to improve the code??
X data:
12.6400 12.6720 12.7040 12.7360 12.7680 12.8000 12.8320 12.8640 12.8960 12.9280 12.9600 12.9920 13.0240 13.0560 13.0880 13.1200 13.1520 13.1840 13.2160 13.2480 13.2800 13.3120 13.3440 13.3760 13.4080 13.4400 13.4720 13.5040 13.5360 13.5680 13.6000 13.6320 13.6640 13.6960 13.7280 13.7600 13.7920 13.8240 13.8560 13.8880 13.9200 13.9520 13.9840 14.0160 14.0480 14.0800 14.1120 14.1440 14.1760 14.2080 14.2400 14.272 14.3040 14.3360 14.3680 14.4000 14.4320 14.4640 14.4960 14.5280 14.5600 14.5920 14.6240 14.6560 14.6880 14.7200 14.7520 14.7840 14.8160 14.8480 14.8800 14.9120 14.9440 14.9760 15.0080 15.0400 15.0720 15.1040 15.1360 15.1680 15.2000 15.2320 15.2640 15.2960 15.3280 15.3600 15.3920 15.4240 15.4560 15.4880 15.5200 15.5520 15.5840 15.6160 15.6480 15.6800 15.7120 15.7440 15.7760 15.8080 15.8400 15.8720 15.9040 15.9360
15.9680 16.0000 16.0320 16.0640 16.0960 16.1280 16.1600 16.1920 16.2240 16.2560 16.2880 16.3200 16.3520 16.3840 16.4160 16.4480 16.4800 16.5120 16.5440 16.5760 16.6080 16.6400 16.6720 16.7040 16.7360 16.7680 16.8000 16.8320 16.8640 16.8960 16.9280 16.9600 16.9920 17.0240 17.0560 17.0880 17.1200 17.1520 17.1840 17.2160 17.2480 17.2800 17.3120 17.3440 17.3760 17.4080 17.4400 17.4720 17.5040 17.5360 17.5680 17.6000 17.6320 17.6640 17.6960 17.7280 17.7600
Y Data:
-0.0404 -0.0405 -0.0350 -0.0406 -0.0412 -0.0407 -0.0378 -0.0405 -0.0337 -0.0417 -0.0413 -0.0387 -0.0352 -0.0373 -0.0369 -0.0388 -0.0384 -0.0351 -0.0401 -0.0314 -0.0375 -0.0390 -0.0330 -0.0343 -0.0341 -0.0369 -0.0424 -0.0369 -0.0309 -0.0387 -0.0346 -0.0433 -0.0410 -0.0355 -0.0343 -0.0396 -0.0369 -0.0400 -0.0377 -0.0330 -0.0416 -0.0348 -0.0380 -0.0338 -0.0349 -0.0359 -0.0418 -0.0336 -0.0375 -0.0309 -0.0362 -0.0422 -0.0437 -0.0352 -0.0303 -0.0335 -0.0358 -0.0467 -0.0341 -0.0306 -0.0322 -0.0338 -0.0418 -0.0417 -0.0299 -0.0264 -0.0308 -0.0352 -0.0330 -0.0261 -0.0088 -0.0071 0.0013 0.0012 0.0151 0.0352 0.0475 0.0764 0.1423 0.2617 0.4057 0.6241 0.8076 0.8872 0.9248 0.9340 0.9395 0.9514 0.9650 0.9708 0.9875 0.9852 0.9955 0.9971 0.9966 0.9981 0.9983 0.9932 1.0013 1.0011 0.9961 1.0044 0.9994 1.0028 1.0028 0.9996 1.0009 1.0024 1.0027 1.0075 1.0017 1.0001 1.0033 1.0062 1.0071 1.0032 1.0026 1.0027 1.0062 1.0063 0.9981 1.0025 0.9994 1.0075 1.0026 1.0035 1.0018 0.9999 1.0045 1.0067 0.9980 1.0044 0.9976 0.9976 1.0087 1.0026 1.0010 0.9997 1.0025 0.9943 1.0098 0.9964 0.9994 0.9973 0.9997 1.0084 1.0035 0.9974 0.9967 0.9967 1.0013 1.0060 1.0026 0.9960 0.9970 0.9987 1.0054 1.0048 0.9952 0.9937 0.9972
Attached are the images of the measured step and fitted curve.

Why not take a simple approach? Smooth your data, compute it’s derivative, then find the max of that derivative. You can do the first two steps by convolving with the derivative of a Gaussian, which is easy to generate.
The location of the max is the shift of the step function you’re trying to fit. The mean of the values to the left and the mean of the values to the right are the low and high values of the step function.
From first principles (toolboxes will make all of these steps simpler), the Gaussian gradient is computed like this:
x = [12.6400 12.6720 12.7040 12.7360 12.7680 12.8000 12.8320 12.8640 12.8960 12.9280 12.9600 12.9920 13.0240 13.0560 13.0880 13.1200 13.1520 13.1840 13.2160 13.2480 13.2800 13.3120 13.3440 13.3760 13.4080 13.4400 13.4720 13.5040 13.5360 13.5680 13.6000 13.6320 13.6640 13.6960 13.7280 13.7600 13.7920 13.8240 13.8560 13.8880 13.9200 13.9520 13.9840 14.0160 14.0480 14.0800 14.1120 14.1440 14.1760 14.2080 14.2400 14.272 14.3040 14.3360 14.3680 14.4000 14.4320 14.4640 14.4960 14.5280 14.5600 14.5920 14.6240 14.6560 14.6880 14.7200 14.7520 14.7840 14.8160 14.8480 14.8800 14.9120 14.9440 14.9760 15.0080 15.0400 15.0720 15.1040 15.1360 15.1680 15.2000 15.2320 15.2640 15.2960 15.3280 15.3600 15.3920 15.4240 15.4560 15.4880 15.5200 15.5520 15.5840 15.6160 15.6480 15.6800 15.7120 15.7440 15.7760 15.8080 15.8400 15.8720 15.9040 15.9360 15.9680 16.0000 16.0320 16.0640 16.0960 16.1280 16.1600 16.1920 16.2240 16.2560 16.2880 16.3200 16.3520 16.3840 16.4160 16.4480 16.4800 16.5120 16.5440 16.5760 16.6080 16.6400 16.6720 16.7040 16.7360 16.7680 16.8000 16.8320 16.8640 16.8960 16.9280 16.9600 16.9920 17.0240 17.0560 17.0880 17.1200 17.1520 17.1840 17.2160 17.2480 17.2800 17.3120 17.3440 17.3760 17.4080 17.4400 17.4720 17.5040 17.5360 17.5680 17.6000 17.6320 17.6640 17.6960 17.7280 17.7600];
y = [-0.0404 -0.0405 -0.0350 -0.0406 -0.0412 -0.0407 -0.0378 -0.0405 -0.0337 -0.0417 -0.0413 -0.0387 -0.0352 -0.0373 -0.0369 -0.0388 -0.0384 -0.0351 -0.0401 -0.0314 -0.0375 -0.0390 -0.0330 -0.0343 -0.0341 -0.0369 -0.0424 -0.0369 -0.0309 -0.0387 -0.0346 -0.0433 -0.0410 -0.0355 -0.0343 -0.0396 -0.0369 -0.0400 -0.0377 -0.0330 -0.0416 -0.0348 -0.0380 -0.0338 -0.0349 -0.0359 -0.0418 -0.0336 -0.0375 -0.0309 -0.0362 -0.0422 -0.0437 -0.0352 -0.0303 -0.0335 -0.0358 -0.0467 -0.0341 -0.0306 -0.0322 -0.0338 -0.0418 -0.0417 -0.0299 -0.0264 -0.0308 -0.0352 -0.0330 -0.0261 -0.0088 -0.0071 0.0013 0.0012 0.0151 0.0352 0.0475 0.0764 0.1423 0.2617 0.4057 0.6241 0.8076 0.8872 0.9248 0.9340 0.9395 0.9514 0.9650 0.9708 0.9875 0.9852 0.9955 0.9971 0.9966 0.9981 0.9983 0.9932 1.0013 1.0011 0.9961 1.0044 0.9994 1.0028 1.0028 0.9996 1.0009 1.0024 1.0027 1.0075 1.0017 1.0001 1.0033 1.0062 1.0071 1.0032 1.0026 1.0027 1.0062 1.0063 0.9981 1.0025 0.9994 1.0075 1.0026 1.0035 1.0018 0.9999 1.0045 1.0067 0.9980 1.0044 0.9976 0.9976 1.0087 1.0026 1.0010 0.9997 1.0025 0.9943 1.0098 0.9964 0.9994 0.9973 0.9997 1.0084 1.0035 0.9974 0.9967 0.9967 1.0013 1.0060 1.0026 0.9960 0.9970 0.9987 1.0054 1.0048 0.9952 0.9937 0.9972];
sigma = 3;
cutoff = ceil(4*sigma);
kernel = -cutoff:cutoff;
kernel = -kernel .* exp(-0.5 * kernel.^2 / sigma.^2);
grad = conv(y,kernel,'same');
We can find the maximum sample with max:
[~,ii] = max(grad);
This is the sample nearest the middle of the transition point. We can refine this location by fitting a parabola to the 3 samples around the peak:
px = x(ii-1:ii+1).';
py = grad(ii-1:ii+1).';
% solve the equation: py = [px.*px, px, ones(3,1)] * params;
params = [px.*px, px, ones(3,1)] \ py;
x_max = -params(2)/(2*params(1));
Finally, we might want to include the y values before and after the transition into the fitting:
left = median(y(x<x_max));
right = median(y(x>x_max));
(Though we might want to assume that left=0 and right=1.)
Plotting:
plot(x,y)
hold on
plot([x(1),x_max,x_max,x(end)],[left,left,right,right])
To fit a full error function (which is the integral of the Gaussian) we just need one more step: above we fitted a parabola to the three samples around the maximum, now instead we fit a parabola to the logarithm of the y values (see this other Q&A for an explanation), and select all y values above 0.2 times the peak value for this fit to avoid fitting to noise. This is approximately 2 sigma, and should be enough to obtain an accurate estimate of the Gaussian peak. The parameters of the Gaussian peak are also the parameters to the smoothed error function, we can correct the estimated sigma for this additional smoothing:
% using grad from the code above (as well as x and y)
[m,ii] = max(grad);
w = sum(grad > m * 0.2) / 2;
px = x(ii-w:ii+w).';
py = log(grad(ii-w:ii+w)).';
% solve the equation: py = [px.*px, px, ones(3,1)] * params;
params = [px.*px, px, ones(size(px))] \ py;
% obtain Gaussian parameters
fitted_mu = -params(2)/(2*params(1));
fitted_sigma = sqrt(-0.5/params(1));
% correct for smoothing applied
fitted_sigma = sqrt(fitted_sigma^2 - (sigma*mean(diff(x)))^2);
% evaluated fitted function
fitted_y = (erf((x-fitted_mu)/fitted_sigma) + 1) / 2 * (right-left) + left;
clf
plot(x,y)
hold on
plot(x,fitted_y)

Here is an example graphical fitter using your data and a sigmoid from my equation search. This example uses the standard scipy differential_evolution genetic algorithm module to determine initial parameter estimates for curve fitting, and that module uses the Latin Hypercube algorithm to ensure a thorough search of parameter space requiring bounds within which to search. In this example the search bounds are derived from the data. Note that it is much easier to estimate ranges for the initial parameter estimates rather than specific values.
import numpy, scipy, matplotlib
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy.optimize import differential_evolution
import warnings
xData = numpy.array([12.6400, 12.6720, 12.7040, 12.7360, 12.7680, 12.8000, 12.8320, 12.8640, 12.8960, 12.9280, 12.9600, 12.9920, 13.0240, 13.0560, 13.0880, 13.1200, 13.1520, 13.1840, 13.2160, 13.2480, 13.2800, 13.3120, 13.3440, 13.3760, 13.4080, 13.4400, 13.4720, 13.5040, 13.5360, 13.5680, 13.6000, 13.6320, 13.6640, 13.6960, 13.7280, 13.7600, 13.7920, 13.8240, 13.8560, 13.8880, 13.9200, 13.9520, 13.9840, 14.0160, 14.0480, 14.0800, 14.1120, 14.1440, 14.1760, 14.2080, 14.2400, 14.272, 14.3040, 14.3360, 14.3680, 14.4000, 14.4320, 14.4640, 14.4960, 14.5280, 14.5600, 14.5920, 14.6240, 14.6560, 14.6880, 14.7200, 14.7520, 14.7840, 14.8160, 14.8480, 14.8800, 14.9120, 14.9440, 14.9760, 15.0080, 15.0400, 15.0720, 15.1040, 15.1360, 15.1680, 15.2000, 15.2320, 15.2640, 15.2960, 15.3280, 15.3600, 15.3920, 15.4240, 15.4560, 15.4880, 15.5200, 15.5520, 15.5840, 15.6160, 15.6480, 15.6800, 15.7120, 15.7440, 15.7760, 15.8080, 15.8400, 15.8720, 15.9040, 15.9360, 15.9680, 16.0000, 16.0320, 16.0640, 16.0960, 16.1280, 16.1600, 16.1920, 16.2240, 16.2560, 16.2880, 16.3200, 16.3520, 16.3840, 16.4160, 16.4480, 16.4800, 16.5120, 16.5440, 16.5760, 16.6080, 16.6400, 16.6720, 16.7040, 16.7360, 16.7680, 16.8000, 16.8320, 16.8640, 16.8960, 16.9280, 16.9600, 16.9920, 17.0240, 17.0560, 17.0880, 17.1200, 17.1520, 17.1840, 17.2160, 17.2480, 17.2800, 17.3120, 17.3440, 17.3760, 17.4080, 17.4400, 17.4720, 17.5040, 17.5360, 17.5680, 17.6000, 17.6320, 17.6640, 17.6960, 17.7280, 17.7600])
yData = numpy.array([-0.0404, -0.0405, -0.0350, -0.0406, -0.0412, -0.0407, -0.0378, -0.0405, -0.0337, -0.0417, -0.0413, -0.0387, -0.0352, -0.0373, -0.0369, -0.0388, -0.0384, -0.0351, -0.0401, -0.0314, -0.0375, -0.0390, -0.0330, -0.0343, -0.0341, -0.0369, -0.0424, -0.0369, -0.0309, -0.0387, -0.0346, -0.0433, -0.0410, -0.0355, -0.0343, -0.0396, -0.0369, -0.0400, -0.0377, -0.0330, -0.0416, -0.0348, -0.0380, -0.0338, -0.0349, -0.0359, -0.0418, -0.0336, -0.0375, -0.0309, -0.0362, -0.0422, -0.0437, -0.0352, -0.0303, -0.0335, -0.0358, -0.0467, -0.0341, -0.0306, -0.0322, -0.0338, -0.0418, -0.0417, -0.0299, -0.0264, -0.0308, -0.0352, -0.0330, -0.0261, -0.0088, -0.0071, 0.0013, 0.0012, 0.0151, 0.0352, 0.0475, 0.0764, 0.1423, 0.2617, 0.4057, 0.6241, 0.8076, 0.8872, 0.9248, 0.9340, 0.9395, 0.9514, 0.9650, 0.9708, 0.9875, 0.9852, 0.9955, 0.9971, 0.9966, 0.9981, 0.9983, 0.9932, 1.0013, 1.0011, 0.9961, 1.0044, 0.9994, 1.0028, 1.0028, 0.9996, 1.0009, 1.0024, 1.0027, 1.0075, 1.0017, 1.0001, 1.0033, 1.0062, 1.0071, 1.0032, 1.0026, 1.0027, 1.0062, 1.0063, 0.9981, 1.0025, 0.9994, 1.0075, 1.0026, 1.0035, 1.0018, 0.9999, 1.0045, 1.0067, 0.9980, 1.0044, 0.9976, 0.9976, 1.0087, 1.0026, 1.0010, 0.9997, 1.0025, 0.9943, 1.0098, 0.9964, 0.9994, 0.9973, 0.9997, 1.0084, 1.0035, 0.9974, 0.9967, 0.9967, 1.0013, 1.0060, 1.0026, 0.9960, 0.9970, 0.9987, 1.0054, 1.0048, 0.9952, 0.9937, 0.9972])
def func(x, a, b, Offset): # Sigmoid A with Offset from zunzun.com
return 1.0 / (1.0 + numpy.exp(-1.0 * a * (x - b))) + Offset
# function for genetic algorithm to minimize (sum of squared error)
def sumOfSquaredError(parameterTuple):
warnings.filterwarnings("ignore") # do not print warnings by genetic algorithm
val = func(xData, *parameterTuple)
return numpy.sum((yData - val) ** 2.0)
def generate_Initial_Parameters():
# min and max used for bounds
maxX = max(xData)
minX = min(xData)
maxY = max(yData)
minY = min(yData)
parameterBounds = []
parameterBounds.append([minX, maxX]) # search bounds for a
parameterBounds.append([minX, maxX]) # search bounds for b
parameterBounds.append([minY, maxY]) # search bounds for Offset
# "seed" the numpy random number generator for repeatable results
result = differential_evolution(sumOfSquaredError, parameterBounds, seed=3)
return result.x
# by default, differential_evolution completes by calling curve_fit() using parameter bounds
geneticParameters = generate_Initial_Parameters()
# now call curve_fit without passing bounds from the genetic algorithm,
# just in case the best fit parameters are aoutside those bounds
fittedParameters, pcov = curve_fit(func, xData, yData, geneticParameters)
print('Fitted parameters:', fittedParameters)
print()
modelPredictions = func(xData, *fittedParameters)
absError = modelPredictions - yData
SE = numpy.square(absError) # squared errors
MSE = numpy.mean(SE) # mean squared errors
RMSE = numpy.sqrt(MSE) # Root Mean Squared Error, RMSE
Rsquared = 1.0 - (numpy.var(absError) / numpy.var(yData))
print()
print('RMSE:', RMSE)
print('R-squared:', Rsquared)
print()
##########################################################
# graphics output section
def ModelAndScatterPlot(graphWidth, graphHeight):
f = plt.figure(figsize=(graphWidth/100.0, graphHeight/100.0), dpi=100)
axes = f.add_subplot(111)
# first the raw data as a scatter plot
axes.plot(xData, yData, 'D')
# create data for the fitted equation plot
xModel = numpy.linspace(min(xData), max(xData))
yModel = func(xModel, *fittedParameters)
# now the model as a line plot
axes.plot(xModel, yModel)
axes.set_xlabel('X Data') # X axis data label
axes.set_ylabel('Y Data') # Y axis data label
plt.show()
plt.close('all') # clean up after using pyplot
graphWidth = 800
graphHeight = 600
ModelAndScatterPlot(graphWidth, graphHeight)

Related

3d Graphing Data in matlab and changing the axis values

I am trying to do a 3d graph of a excel file that is 343 by 81 cells, The first column needs to be the X and the first row needs to be the Y and the remaining matrix needs to be the Z. I have the data successfully imported from excel and I create a matrix of the first column called energy (343,1)(x-axis), while creating a row matrix (1, 81) called Time Delay(y-axis) and a (343,81) matrix where the first column and row is zero called Absorbance Change(Z-axis). I've got the proper 3d graph that I need but I need the axes shown in the graph to be that of the Energy and Time Delay instead of the indices of the Absorbance Change matrix. I am putting the relevant portion of the code below as well as a picture of the graph:
EnergyString = dataArray{:, 1};
EnergyString(1,1) = {'0'};
Energy = str2double(EnergyString);
%Energy = [ Energy, zeros(343, 80) ];
TimeDelay = [ z1(1,1), z2(1,1), z3(1,1), z4(1,1), z5(1,1), z6(1,1), z7(1,1), z8(1,1), z9(1,1), z10(1,1), z11(1,1), z12(1,1), z13(1,1), z14(1,1), z15(1,1), z16(1,1), z17(1,1), z18(1,1), z19(1,1), z20(1,1), z21(1,1), z22(1,1), z23(1,1), z24(1,1), z25(1,1), z26(1,1), z27(1,1), z28(1,1), z29(1,1), z30(1,1), z31(1,1), z32(1,1), z33(1,1), z34(1,1), z35(1,1), z36(1,1), z37(1,1), z38(1,1), z39(1,1), z40(1,1), z41(1,1), z42(1,1), z42(1,1), z43(1,1), z44(1,1), z45(1,1), z46(1,1), z47(1,1), z48(1,1), z49(1,1), z50(1,1), z51(1,1), z52(1,1), z53(1,1), z54(1,1), z55(1,1), z56(1,1), z57(1,1), z58(1,1), z59(1,1), z60(1,1), z61(1,1), z62(1,1), z63(1,1), z64(1,1), z65(1,1), z66(1,1), z67(1,1), z68(1,1), z69(1,1), z70(1,1), z71(1,1), z72(1,1), z73(1,1), z74(1,1), z75(1,1), z76(1,1), z77(1,1), z78(1,1), z79(1,1), z80(1,1) ];
%TimeDelay = [ TimeDelay; zeros(342, 81)];
startRow formatSpec filename fileID delimiter ans EnergyString Alpha Beta Gamma Delta Epsilon Zeta Eta Theta Iota Kappa Lambda Mu Nu Xi Omicron Pi Rho Sigma Tau Upsilon Phi Chi Psi Omega AlphaAlpha AlphaBeta AlphaGamma AlphaDelta AlphaEpsilon AlphaZeta AlphaEta AlphaTheta AlphaIota AlphaKappa AlphaLambda AlphaMu AlphaNu AlphaXi AlphaOmicron AlphaPi AlphaRho AlphaSigma AlphaTau AlphaUpsilon AlphaPhi AlphaChi AlphaPsi AlphaOmega BetaAlpha BetaBeta BetaGamma BetaDelta BetaEpsilon BetaZeta BetaEta BetaTheta BetaIota BetaKappa BetaLambda BetaMu BetaNu BetaXi BetaOmicron BetaPi BetaRho BetaSigma BetaTau BetaUpsilon BetaPhi BetaChi BetaPsi BetaOmega GammaAlpha GammaBeta GammaGamma GammaDelta GammaEpsilon GammaZeta GammaEta GammaTheta; %Delete Excess Varaible
AbsorbanceChange = [ zeros(343, 1), z1, z2, z3, z4, z5, z6, z7, z8, z9, z10, z11, z12, z13, z14, z15, z16, z17, z18, z19, z20, z21, z22, z23, z24, z25, z26, z27, z28, z29, z30, z31, z32, z33, z34, z35, z36, z37, z38, z39, z40, z41, z42, z43, z44, z45, z46, z47, z48, z49, z50, z51, z52, z53, z54, z55, z56, z57, z58, z59, z60, z61, z62, z63, z64, z65, z66, z67, z68, z69, z70, z71, z72, z73, z74, z75, z76, z77, z78, z79, z80];
AbsorbanceChange(1,:) = 0;
clear z1 z2 z3 z4 z5 z6 z7 z8 z9 z10 z11 z12 z13 z14 z15 z16 z17 z18 z19 z20 z21 z22 z23 z24 z25 z26 z27 z28 z29 z30 z31 z32 z33 z34 z35 z36 z37 z38 z39 z40 z41 z42 z43 z44 z45 z46 z47 z48 z49 z50 z51 z52 z53 z54 z55 z56 z57 z58 z59 z60 z61 z62 z63 z64 z65 z66 z67 z68 z69 z70 z71 z72 z73 z74 z75 z76 z77 z78 z79 z80;
mesh(AbsorbanceChange)
colorbar
title('WS2-Perovskite-image')
xlabel('Energy') % x-axis label
ylabel('Time-delay') % y-axis label
zlabel('Absorbance Change')
When I type help mesh in MATLAB I see, among other thins, this:
mesh(x,y,Z) and mesh(x,y,Z,C), with two vector arguments replacing
the first two matrix arguments, must have length(x) = n and
length(y) = m where [m,n] = size(Z). In this case, the vertices
of the mesh lines are the triples (x(j), y(i), Z(i,j)).
Note that x corresponds to the columns of Z and y corresponds to
the rows.
Thus, you can do
mesh(Energy, TimeDelay, AbsorbanceChange);
I don't know how you read the data from file, but there is a better way than specifying each cell individually in your code.

saving output of for loop when dimensions of output vary as a function of iterator in matlab

I am attempting to fit some UV/Vis absorbance spectra to published reference standards. In general, the absorbance one obtains from the spectrometer is equal to a linear combination of the concentration of each absorber multiplied by the cross-section of absorption for each molecule at each wavelength (and multiplied by the pathlength of the spectrometer).
That said, not all spectrometers are precise in the x axis (wavelength), so some adjustment may be necessary to fit one's experimental data to the reference standards.
In this script, I am adjusting the index of my wavelength and spectral intensity to see if integer steps of my spectra result in a better fit to the reference standards (each step is 0.08 nm). Of course, I need to save the output of the fit parameters; however, since each fit has a different set of dimensions, I'm having difficulty just throwing them into a structure(k) (commented out in the following code snippet).
If anyone has a tip or hint, I'd be very appreciative. The relevant portion of my sample code follows:
for i = -15:15
lengthy = length(wavelengthy)
if i >= 0
xvalue = (1:lengthy - abs(i))
yvalue = (1+abs(i):lengthy)
else
xvalue = (1+abs(i):lengthy)
yvalue = (1: lengthy - abs(i))
end
Phi = #(k,wavelengthy) ( O3standard(yvalue) .* k(1) + Cl2standard(yvalue) .* k(2) + ClOstandard(yvalue) .* k(3) + OClOstandard(yvalue) .* k(4));
[khat, resnorm, residual, exitflag, output, lambda, jacobian] = lsqcurvefit(Phi,k0,wavelengthy(xvalue),workingspectra(yvalue), lowerbound, upperbound, options);
%parameters.khat(k,:) = khat;
%parameters.jacobian(k,:) = jacobian;
%parameters.exitflag(k,:) = exitflag;
%parameters.output(k,:) = output;
%parameters.residuals(k,:) = residual
%concentrations(:,k) = khat./pathlength
k=k+1
end

Use the osusvm to recognize faces after the C2 layer in HMAX model

I achieved the computation of the original HMAX model, and I get the results at C2 layer. Now I still have the tuned-layer, in other words, to use the osusvm.
In my project, I have two directories. One containing the training images and other containing the test images.
Reference: lennon310's response in Training images and test images
Firstly, I would like to show you my results at C2 layer (surely that the results should be a vectors). Notice that I extracted only 2 prototypes in the S2 layer (in my project I used 256 prototypes, but only in this question, assume that I used only 2 prototypes), and four prototypes sizes:[4 8 12 16]. So for each image, we get 8 C2 units (2 prototypes x 4 patch sizes = 8).
C2res{1}: For the six training images:
0.0088 0.0098 0.0030 0.0067 0.0063 0.0057
0.0300 0.0315 0.0251 0.0211 0.0295 0.0248
0.1042 0.1843 0.1151 0.1166 0.0668 0.1134
0.3380 0.2529 0.3709 0.2886 0.3938 0.3078
0.2535 0.3255 0.3564 0.2196 0.1681 0.2827
3.9902 5.3475 4.5504 4.9500 6.7440 4.4033
0.8520 0.8740 0.7209 0.7705 0.4303 0.7687
6.3131 7.2560 7.9412 7.1929 9.8789 6.6764
C2res{2}: For the two test images:
0.0080 0.0132
0.0240 0.0001
0.1007 0.2214
0.3055 0.0249
0.2989 0.3483
4.6946 4.2762
0.7048 1.2791
6.7595 4.7728
Secondly, I downloaded the osu-svm matlab toolbox and I added its path:
addpath(genpath('./osu-svm/')); %put your own path to osusvm here
useSVM = 1; %if you do not have osusvm installed you can turn this
%to 0, so that the classifier would be a NN classifier
%note: NN is not a great classifier for these features
Then I used the code below:
%Simple classification code
XTrain = [C2res{1}]; %training examples as columns
XTest = [C2res{2}]; %the labels of the training set
ytrain = [ones(size(C2res{1},2),1)];%testing examples as columns
ytest = [ones(size(C2res{2},2),1)]; %the true labels of the test set
if useSVM
Model = CLSosusvm(XTrain,ytrain); %training
[ry,rw] = CLSosusvmC(XTest,Model); %predicting new labels
else %use a Nearest Neighbor classifier
Model = CLSnn(XTrain, ytrain); %training
[ry,rw] = CLSnnC(XTest,Model); %predicting new labels
end
successrate = mean(ytest==ry) %a simple classification score
Does the code just above is true ? Why I always get a successrate=1 ? I think that I am wrong in some places. Please I need help. If it is true, does another way to compute that ? What can I use instead of successrate in order to get more sexy results?
Note:
The function CLSosusvm is :
function Model = CLSosusvm(Xtrain,Ytrain,sPARAMS);
%function Model = CLSosusvm(Xtrain,Ytrain,sPARAMS);
%
%Builds an SVM classifier
%This is only a wrapper function for osu svm
%It requires that osu svm (http://www.ece.osu.edu/~maj/osu_svm/) is installed and included in the path
%X contains the data-points as COLUMNS, i.e., X is nfeatures \times nexamples
%y is a column vector of all the labels. y is nexamples \times 1
%sPARAMS is a structure of parameters:
%sPARAMS.KERNEL specifies the kernel type
%sPARAMS.C specifies the regularization constant
%sPARAMS.GAMMA, sPARAMS.DEGREE are parameters of the kernel function
%Model contains the parameters of the SVM model as returned by osu svm
Ytrain = Ytrain';
if nargin<3
SETPARAMS = 1;
elseif isempty(sPARAMS)
SETPARAMS = 1;
else
SETPARAMS = 0;
end
if SETPARAMS
sPARAMS.KERNEL = 0;
sPARAMS.C = 1;
end
switch sPARAMS.KERNEL,
case 0,
[AlphaY, SVs, Bias, Parameters, nSV, nLabel] = ...
LinearSVC(Xtrain, Ytrain, sPARAMS.C);
case 1,
[AlphaY, SVs, Bias, Parameters, nSV, nLabel] = ...
PolySVC(Xtrain, Ytrain, sPARAMS.DEGREE, sPARAMS.C, 1,0);
case 2,
[AlphaY, SVs, Bias, Parameters, nSV, nLabel] = ...
PolySVC(Xtrain, Ytrain, sPARAMS.DEGREE, sPARAMS.C, 1,sPARAMS.COEF);
case 3,
[AlphaY, SVs, Bias, Parameters, nSV, nLabel] = ...
RbfSVC(Xtrain, Ytrain, sPARAMS.GAMMA, sPARAMS.C);
end
Model.AlphaY = AlphaY;
Model.SVs = SVs;
Model.Bias = Bias;
Model.Parameters = Parameters;
Model.nSV = nSV;
Model.nLabel = nLabel;
Model.sPARAMS = sPARAMS;
The function CLSosusvmC is :
function [Labels, DecisionValue]= CLSosusvmC(Samples, Model);
%function [Labels, DecisionValue]= CLSosusvmC(Samples, Model);
%
%wrapper function for osu svm classification
%Samples contains the data-points to be classified as COLUMNS, i.e., it is nfeatures \times nexamples
%Model is the model returned by CLSosusvm
%Labels are the predicted labels
%DecisionValue are the values assigned by the Model to the points (Labels = sign(DecisionValue))
[Labels, DecisionValue]= SVMClass(Samples, Model.AlphaY, ...
Model.SVs, Model.Bias, ...
Model.Parameters, Model.nSV, Model.nLabel);
Labels = Labels';
DecisionValue = DecisionValue';
Your code looks good to me.
Since you have only 2 test images, the possible successful rate will be limited to 0, 0.5, 1. And it is expected to achieve the 100% accuracy with a 25% probability ([0 1],[1 0],[1 1],[0 0]). You can shuffle the data, and re-select 2 from the 8 as the test for some times, then observe the accuracy.
Also try to add more images to both training and test samples.
Machine learning has little sense on the set of 8 images. Gather at least 10x more data, and then analyze the results. With such small dataset any results are possible (from 0 to 100 percent), and none of them is realiable.
Meanwhile you can try to perform repeated cross validation:
Shuffle your data
Split it to two-element parts ( [1 2][3 4][5 6][7 8] )
For each of such parts:
a) test on it, while training on the rest, so for example:
train on [3 4 5 6 7 8] and test on [1 2]
b) record the mean score
Repeat the whole process and report the means score

How to use Matlab for non linear least squares Michaelis–Menten parameters estimation

I have a set of measurements and I started making a linear approximation (as in this plot). A linear least squares estimation of the parameters V_{max} and K_{m} from this code in Matlab:
data=[2.0000 0.0615
2.0000 0.0527
0.6670 0.0334
0.6670 0.0334
0.4000 0.0138
0.4000 0.0258
0.2860 0.0129
0.2860 0.0183
0.2220 0.0083
0.2200 0.0169
0.2000 0.0129
0.2000 0.0087 ];
x = 1./data(:,1);
y = 1./data(:,2);
J = [x,ones(length(x),1)];
k = J\y;
vmax = 1/k(2);
km = k(1)*vmax;
lse = (vmax.*data(:,1))./(km+data(:,1));
plot(data(:,1),data(:,2),'o','color','red','linewidth',1)
line(data(:,1),lse,'linewidth',2)
This yields a fit that looks alright. Next, I wanted to do the same thing but with non-linear least squares. However, the fit always looks wrong, here is the code for that attempt:
options = optimset('MaxIter',10000,'MaxFunEvals',50000,'FunValCheck',...
'on','Algorithm',{'levenberg-marquardt',.00001});
p=lsqnonlin(#myfun,[0.1424,2.5444]);
lse = (p(1).*data(:,1))./(p(2)+data(:,1));
plot(data(:,1),data(:,2),'o','color','red','linewidth',1)
line(data(:,1),lse,'linewidth',2)
which requires this function in an M-File:
function F = myfun(x)
F = data(:,2)-(x(1).*data(:,1))./x(2)+data(:,1);
If you run the code you will see my problem. But hopefully, unlike me, you see what I'm doing wrong.
I think that you forgot some parentheses (some others are superfluous) in your nonlinear function. Using an anonymous function:
myfun = #(x)data(:,2)-x(1).*data(:,1)./(x(2)+data(:,1)); % Parentheses were missing
options = optimset('MaxIter',10000,'MaxFunEvals',50000,'FunValCheck','on',...
'Algorithm',{'levenberg-marquardt',.00001});
p = lsqnonlin(myfun,[0.1424,2.5444],[],[],options);
lse = p(1).*data(:,1)./(p(2)+data(:,1));
plot(data(:,1),data(:,2),'o','color','red','linewidth',1)
line(data(:,1),lse,'linewidth',2)
You also weren't actually applying any of your options.
You might look into using lsqcurvefit instead as it was designed for data fitting problems:
myfun = #(x,dat)x(1).*dat./(x(2)+dat);
options = optimset('MaxIter',10000,'MaxFunEvals',50000,'FunValCheck','on',...
'Algorithm',{'levenberg-marquardt',.00001});
p = lsqcurvefit(myfun,[0.1424,2.5444],data(:,1),data(:,2),[],[],options);
lse = myfun(p,data(:,1));
plot(data(:,1),data(:,2),'o','color','red','linewidth',1)
line(data(:,1),lse,'linewidth',2)

Unexpected behaviour of function findpeaks in MATLAB's Signal Processing Toolbox

Edit: Actually this is not unexpected behaviour, but I still need a solution.
findpeaks compares each element of data to its neighboring values.
I have data which contains peaks which I detect with the function findpeaks from the Signal Processing Toolbox. Sometimes the function seems not to detect the peaks properly, when I have the same value twice next to each other. This occurs very rarly in my data, but here is a sample to illustrate my problem:
>> values
values =
-0.0324
-0.0371
-0.0393
-0.0387
-0.0331
-0.0280
-0.0216
-0.0134
-0.0011
0.0098
0.0217
0.0352
0.0467
0.0548
0.0639
0.0740
0.0813
0.0858 <-- here should be another peak
0.0858 <--
0.0812
0.0719
0.0600
0.0473
0.0353
0.0239
0.0151
0.0083
0.0034
-0.0001
-0.0025
-0.0043
-0.0057
-0.0048
-0.0038
-0.0026
0.0007
0.0043
0.0062
0.0083
0.0106
0.0111
0.0116
0.0102
0.0089
0.0057
0.0025
-0.0025
-0.0056
Now the findpeaks function only finds one peak:
>> [pks loc] = findpeaks(values)
pks =
0.0116
loc =
42
If I plot the data, it becomes obvious that findpeaks misses one of the peaks at the location 18/19 because they both have the value 0.08579.
What is the best way to find those missing peaks?
If you have the image processing toolbox, you can use IMREGIONALMAX to find the peaks, after which you can use regionprops to find the center of the regions (if that's what you need), i.e.
bw = imregionalmax(signal);
peakLocations = find(bw); %# returns n peaks for an n-tuple of max-values
stats = regionprops(bw,'Centroid');
peakLocations = cat(1,stats.Centroid); %# returns the center of the n-tuple of max-values
This is an old topic, but maybe some are still looking for an easier solution to this (like I did today):
You could also just substract some very small fixed value from all values on a plateau, except from the first value. This causes each first value on a plateau to always be the highest on the respective plateaus, causing them to be included as peaks.
Just make something like this part of your code:
peaks = yourdata;
verysmallvalue = .001;
plateauvalue = peaks(1);
for i = 2:size(peaks,1)
if peaks(i) == plateauvalue
peaks(i) = peaks(i) - verysmallvalue;
else
plateauvalue = peaks(i);
end
end
[PKS,LOCS] = findpeaks(peaks);
plot(yourdata);
hold on;
plot(LOCS, yourdata(LOCS), 'Color', 'Red', 'Line', 'None', 'Marker', 'o');
Hope this helps!
Use the second derivative test instead?
I ended up writing my own simpler version of findpeaks, which seems to work for my purpose.
function [pks,locs] = my_findpeaks(X)
M = numel(X);
pks = [];
locs = [];
if (M < 4)
datamsgid = generatemsgid('emptyDataSet');
error(datamsgid,'Data set must contain at least 4 samples.');
else
for idx=1:M-3
if X(idx)< X(idx+1) && X(idx+1)>=X(idx+2) && X(idx+2)> X(idx+3)
pks = [pks X(idx)];
locs = [locs idx];
end
end
end
end
Edit: To clarify, the problem arose, when I had a peak which was exactly between two sample points and those two sample points had coincidentally the same value. It only happend a couple of times in more than 10.000 cases.
The behavior that you describe is a known bug in versions of MATLAB prior to R2010b. The minimum example is
findpeaks([0 1 1 0])
which returns [], while
findpeaks([0 1 0])
returns the (position of the) peak.
The bug has been fixed in R2010b and later, see the official Bug Report. With that fix, findpeaks returns the rising edge of "peaks with repeated values" (which I would call plateaus).