Mercurial > hg > map
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 |