annotate Copy_of_multithreshold 1.46/bestFitPsychometicFunctions.m @ 30:1a502830d462

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