annotate multithreshold 1.46/bestFitPsychometicFunctions.m @ 38:c2204b18f4a2 tip

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