To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

The primary repository for this project is hosted at git://github.com/rmeddis/MAP.git .
This repository is a read-only copy which is updated automatically every hour.

Statistics Download as Zip
| Branch: | Revision:

root / multithreshold 1.46 / bestFitPsychometicFunctions.m @ 0:f233164f4c86

History | View | Annotate | Download (10.8 KB)

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