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

History | View | Annotate | Download (10.8 KB)

1 0:f233164f4c86 rmeddis
% ------------------------------------------------------- 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