comparison Copy_of_multithreshold 1.46/bestFitPsychometicFunctions.m @ 28:02aa9826efe0

mainly multiThreshold
author Ray Meddis <rmeddis@essex.ac.uk>
date Fri, 01 Jul 2011 12:59:47 +0100
parents
children
comparison
equal deleted inserted replaced
27:d4a7675b0413 28:02aa9826efe0
1 % ------------------------------------------------------- bestFitPsychometicFunctions
2 function [psy, levelsPhaseTwoBinVector, logistic, rareEvent]= bestFitPsychometicFunctions (levelsPhaseTwo, responsesPhaseTwo)
3 % bestFitPsychometicFunctions computes a psychometric function from a matrix of levels and responses
4 %output
5 % psy is the empirical probabbility of a detection associated with levels in levelsPhaseTwoBinVector
6 % logistic is a structure with results for fitting the logistic function
7 % the logistic fit depends on whether maximum likelihood is used or least squares
8 % rareEvent is a structure with results for fitting the rareEvent function
9 % this is always calculated and lest squares is always used
10
11 global experiment stimulusParameters binFrequencies
12
13 % Generate a psychometic function by binning the levelsPhaseTwo
14 % x is a vector of [levelsPhaseTwo; responsesPhaseTwo]
15 % experiment.psyBinWidth defines the width of the bin
16 [psy, levelsPhaseTwoBinVector, binFrequencies,yesResponses, noResponses]= ...
17 psychometricFunction(levelsPhaseTwo, responsesPhaseTwo, experiment.psyBinWidth);
18 % [levelsPhaseTwoBinVector; binFrequencies; psy];
19
20 % undefined slope - return
21 if isempty(psy)
22 % slope is undefined
23 psy=[]; levelsPhaseTwoBinVector=[];
24 logistic.bestThreshold=NaN; logistic.bestK=NaN; logistic.predictionsLOG=[]; logistic.predictionLevels=[];
25 rareEvent.bestGain=NaN; rareEvent.bestVMin=NaN; rareEvent.predictionsRE=[]; rareEvent.predictionLevels=[];
26 rareEvent.thresholddB= NaN; rareEvent.bestPaMindB=NaN;
27 return
28 end
29
30 % find best fit rare event function
31 switch experiment.paradigm
32 case 'gapDuration'
33 % i.e. don't attempt this but visit the function to set null results
34 rareEvent=fitRareEvent([], responsesPhaseTwo, stimulusParameters.targetDuration);
35 otherwise
36 rareEvent=fitRareEvent(levelsPhaseTwo, responsesPhaseTwo, stimulusParameters.targetDuration);
37 end
38
39 % find best logistic fit
40 logisticFunc=' 1./(1+exp(-a2.*(x-a1)));';
41 switch experiment.functionEstMethod
42 % least squares estimate
43 case {'logisticLS', 'rareEvent','peaksAndTroughs'}
44 [a1, a2, Euclid]=fitFunctionUsingLeastSquares(levelsPhaseTwo, responsesPhaseTwo, ...
45 logisticFunc, experiment.possLogSlopes, experiment.meanSearchStep);
46 % [a1, a2, Euclid]=fitLogisticUsingLS(psy, levelsPhaseTwoBinVector); %! does not give same result
47 logistic.bestThreshold=a1;
48 logistic.bestK=a2;
49 logistic.predictionsLOG= ...
50 1./(1+exp(-logistic.bestK.*(experiment.predictionLevels-logistic.bestThreshold)));
51 logistic.predictionLevels=experiment.predictionLevels;
52 logistic.Euclid = Euclid;
53 % predResponses=1./(1+exp(-logistic.bestK.*(levelsPhaseTwo-logistic.bestThreshold)));
54 % [levelsPhaseTwo' responsesPhaseTwo' predResponses' responsesPhaseTwo'-predResponses' ]
55
56 % maximum likelihood fitting
57 case 'logisticML'
58 [a1, a2, Euclid]=fitFunctionUsingMaxLikelihood (levelsPhaseTwoBinVector,...
59 psy, yesResponses, noResponses, logisticFunc, experiment.possLogSlopes,experiment.meanSearchStep);
60 logistic.bestThreshold=a1;
61 logistic.bestK=a2;
62 logistic.predictionsLOG= ...
63 1./(1+exp(-logistic.bestK.*(experiment.predictionLevels-logistic.bestThreshold)));
64 logistic.predictionLevels=experiment.predictionLevels;
65 logistic.Euclid = Euclid;
66 end
67
68 % disp(num2str([logistic.bestThreshold logistic.bestK logistic.Euclid]))
69 % disp(num2str([ rareEvent.thresholddB rareEvent.bestGain rareEvent.bestVMin rareEvent.Euclid]))
70
71 % --------------------------------------------- fitLogisticUsingLS
72 function [mean, slope, Euclid]=fitLogisticUsingLS(p,L)
73
74 %!! each bin needs to be weighted by its size
75
76 idx1=find(p>0);
77 p=p(idx1);
78 L=L(idx1);
79 idx1=find(p<1);
80 p=p(idx1);
81 L=L(idx1);
82
83 if length(L)<2
84 mean=NaN;
85 slope=NaN;
86 Euclid=NaN;
87 return
88 end
89
90 y=L;
91 x=log(1./p -1);
92 a =polyfit(x, y, 1);
93 mean=a(2);
94 slope=-1/a(1);
95
96 % euclid
97 predy=polyval(a,x);
98 Euclid=sum((predy-y).^2);
99 [mean slope Euclid];
100
101 % --------------------------------------------- fitFunctionUsingLeastSquares
102 function [a1, a2, Euclid]=fitFunctionUsingLeastSquares(levelsPhaseTwo, responsesPhaseTwo, func, possSlopes, meanSearchStep)
103
104 % nResponses= yesResponses+noResponses;
105 % idx=find(not(nResponses==0));
106 % levelsPhaseTwo=levelsPhaseTwo(idx);
107 % yesResponses=yesResponses(idx);
108 % noResponses=noResponses(idx);
109 % nResponses=nResponses(idx);
110
111 % psy=yesResponses./nResponses;
112
113 possThresholds=min(levelsPhaseTwo):meanSearchStep:max(levelsPhaseTwo);
114
115
116 % create vectors representing all combinations of a1 and a2
117 nPossThresh=length(possThresholds);
118 nPossSlopes=length(possSlopes);
119 a1=repmat(possThresholds',nPossSlopes,1);
120 a2=repmat(possSlopes,nPossThresh,1);
121 a2=reshape(a2,nPossThresh*nPossSlopes,1);
122
123 % each response is predicted by every possible threshold and slope
124 LS=ones(size(a1));
125 for i=1:length(levelsPhaseTwo)
126 x=levelsPhaseTwo(i);
127 eval(['y=' func]);
128 actual=responsesPhaseTwo(i);
129 error= (actual - y).^2;
130 LS=LS+error;
131 end
132
133 [Euclid, idx]=min(LS);
134
135 a1=a1(idx);
136 a2=a2(idx);
137
138 % x=levelsPhaseTwo;
139 % eval(['y=' func]);
140 % Euclid= sum((psy-y).^2);
141
142 % plot(levelsPhaseTwo,y,'r')
143 % hold on
144 % plot(levelsPhaseTwo,psy,'o')
145
146 % --------------------------------------------- fitFunctionUsingMaxLikelihood
147 function [a1, a2, Euclid]=fitFunctionUsingMaxLikelihood...
148 (levelsPhaseTwo,psy, yesResponses, noResponses, func, possible_a2, meanSearchStep)
149
150 % fitFunctionUsingMaxLikelihood fits the function in 'func' to binned yes-no data.
151 % levelsPhaseTwo specifies the bin centers
152 % yesResponses and noResponses are the niumber of yes and no responsesPhaseTwo in each bin
153 % 'func' is a function of a1, b1, x; e.g. func='1./(1+exp(-a2.*(x-a1)));';
154 % and a2, a1 are parameters to be discovered.
155 % If bins are empty (i.e. neither yes or no), these are eliminated
156 %
157
158 nResponses=yesResponses+noResponses;
159 possible_a1=min(levelsPhaseTwo):meanSearchStep:max(levelsPhaseTwo);
160
161 % if nargin<6
162 % possible_a2=-2:.05:4;
163 % end
164
165 % create vectors representing all combinations of a1 and a2
166 nPossThresh=length(possible_a1);
167 nPossSlopes=length(possible_a2);
168 a1=repmat(possible_a1',nPossSlopes,1);
169 a2=repmat(possible_a2,nPossThresh,1);
170 a2=reshape(a2,nPossThresh*nPossSlopes,1);
171
172 cumulativeProbability=ones(1, length(a2));
173 cumulativeProbability=cumulativeProbability';
174
175 for i=1:length(levelsPhaseTwo)
176 x=levelsPhaseTwo(i);
177 eval(['y=' func]);
178 funcVal=(1-y).^yesResponses(i);
179 cumulativeProbability= cumulativeProbability.*funcVal;
180 funcVal=y.^noResponses(i);
181 cumulativeProbability= cumulativeProbability.*funcVal;
182 end
183
184 [maxProb idx]=max(cumulativeProbability);
185 a1=a1(idx);
186 a2=a2(idx);
187
188 % x=levelsPhaseTwo;
189 eval(['y=' func]);
190 Euclid= sum(nResponses.*(psy-y).^2);
191
192 % figure(1), clf
193 % plot(levelsPhaseTwo,y)
194 % hold on
195 % plot(levelsPhaseTwo,psy,'o')
196
197
198
199 % --------------------------------------------------- fitRareEvent
200 function rareEvent=fitRareEvent(stimulusLevels, responsesPhaseTwo, duration, gains, Vmins)
201 % least squares estimate of *rare event* function
202 % model is: r = g P – A ... g=events/s/Pa, A= events/s, P= pressure (Pa)
203 % psy=1- exp(d(gP –A )) ... d=duration
204 % p(event)=gain*levelmPa -Vmin
205 % 'responsesPhaseTwo' is a binary vector of subject's decision.
206 % 'stimulusLevels' are the corresponding signal levesl (values)
207 % duration is required to compute the expectation of an event occurring
208 % gains is an optional list of gains to be tried
209 % Vmins is an optional list of Vmins to be tried
210
211 global experiment
212 if nargin<5
213 minVmin=.1; maxVmin=10; nVmins=100;
214 Vmins=[0 logspace(log10(minVmin),log10(maxVmin),nVmins)];
215 % disp(Vmins)
216 gainMin=0.0001; gainMax=1; nGains=100;
217 gains=[1e-5 logspace(log10(gainMin),log10(gainMax),nGains)];
218 end
219
220 rareEvent.bestGain=NaN;
221 rareEvent.bestVMin=NaN;
222 rareEvent.thresholddB=0;
223 rareEvent.bestPaMindB=NaN;
224 rareEvent.predictionLevels=[];
225 rareEvent.predictionsRE=[];
226 rareEvent.Euclid=NaN;
227
228 if isempty(stimulusLevels), return, end
229
230 % expected slope is negative, rareEvent can not be used
231 if experiment.psyFunSlope<0
232 return
233 end
234
235 % NB calculations in microPascals!
236 stimulusLevelsAsPressure=28 * 10.^(stimulusLevels/20);
237
238 % allGains=reshape(repmat(gains,nVmins,1), 1, nVmins*nGains);
239 % allVmins=repmat(Vmins, 1, nGains);
240
241 predictions=NaN*zeros(1,length(stimulusLevels));
242 gainCount=0;
243 Euclid=inf; bestVmin=0; bestGain=0;
244 for gain= gains
245 gainCount=gainCount+1;
246 VminCount=0;
247
248 % 1 – exp(-d (g P – A))
249 for Vmin=Vmins
250 VminCount=VminCount+1;
251 % all levelsPhaseTwo are simultaneously assessed
252
253 % original linear function
254 % gP_Vmin=gain*stimulusLevelsAsPressure-Vmin;
255 % idx=(gP_Vmin>0);
256 % predictions(idx)= 1-exp(-duration*(gP_Vmin(idx)));
257 % new log function log(r) =gP-A
258
259 % log function
260 gP_Vmin=gain*stimulusLevelsAsPressure-Vmin;
261 idx=(gP_Vmin>0);
262 predictions(idx)= 1-exp(-duration*exp(gP_Vmin(idx)));
263 predictions(~idx)=0;
264
265 % % square function
266 % P_Vmin=stimulusLevelsAsPressure-Vmin;
267 % idx=(P_Vmin)>0;
268 % predictions(idx)= 1-exp(-duration*gain*(P_Vmin(idx)).^2);
269 % predictions(~idx)=0;
270
271
272 % NB the error is equally weighted for each stimulus/response pair
273 % this is not the same as equal weighting for each distinct level
274 error=(predictions - responsesPhaseTwo).^2;
275 error=mean(error(~isnan(error)));
276 if error<Euclid
277 Euclid=error;
278 bestVmin=Vmin;
279 bestVminCount=VminCount;
280 % bestGainCount=gainCount;
281 bestGain=gain;
282 end
283 end
284 end
285 % disp(Vmins)
286
287 % if bestGainCount==1 | bestGainCount==nGains
288 % disp(['gain estimate ' num2str(gains(bestGainCount)) ' may be out of range'])
289 % end
290
291 [rareEvent.Euclid idx]=min(Euclid);
292 rareEvent.bestGain=bestGain;
293 rareEvent.bestVMin=bestVmin;
294 rareEvent.thresholdPa=(-log(0.5)/duration + rareEvent.bestVMin)/rareEvent.bestGain;
295 rareEvent.thresholddB=20*log10(rareEvent.thresholdPa/28);
296 rareEvent.bestPaMindB=20*log10((rareEvent.bestVMin/rareEvent.bestGain)/28);
297
298 predictionLevels= -50:1:120;
299 rareEvent.predictionLevels=predictionLevels;
300 stimulusLevelsAsPressure=28*10.^(predictionLevels/20);
301
302 % gP_Vmin=rareEvent.bestGain*stimulusLevelsAsPressure-rareEvent.bestVMin;
303 % rareEvent.predictionsRE= 1-exp(-duration*(gP_Vmin));
304
305 gP_Vmin=rareEvent.bestGain*stimulusLevelsAsPressure-rareEvent.bestVMin;
306 rareEvent.predictionsRE= 1-exp(-duration*exp(gP_Vmin));
307
308 % P_Vmin=stimulusLevelsAsPressure-Vmin;
309 % predictions= 1-exp(-duration*gain*P_Vmin.^2);
310 % predictions(predictions<0)=0;
311 % rareEvent.predictionsRE=predictions;
312
313 % rareEvent
314