📄 felogit.m
字号:
function results = felogit(y,x,ivec,tvec,beta0,c0,maxit,tol);
% PURPOSE: computes binomial logistic regression with a one-dimensional fixed effect:
% logit(y_it) = x_it'beta + c_i
% where: i = 1,...,nobs indexes observational units
% t = 1,...,T_i indexes repeated observations on i
% c_i is a fixed effect associated with i
% NOTE: felogit() returns MLEs of beta AND c_i
%----------------------------------------------------------------%
% USAGE: results = felogit(y,x,i,t,maxit,tol)
% where: y = dependent variable vector (nobs x 1)
% x = matrix of covariates (nobs x nvar)
% Note: the model does NOT include a common
% intercept, instead it includes an effect for
% each i.
% ivec = (nobs x 1) vector which identifies the unit
% (i) associated with each row of y,x (i.e., the
% unit for which we have repeated observations).
% Note: must be numeric
% tvec = (nobs x 1) vector which identifies the replication
% number (t) of each row of y,x (i.e., index for
% repeated observations). Note: must be numeric.
% beta0 = optional starting values for beta (default is ols)
% c0 = optional starting values for c (default is zero)
% maxit = optional maximum number of iterations (default=100)
% tol = optional convergence criterion (default=1e-8)
% nobs = sum(T_i) ( = N*T if T_i=T for all i)
%----------------------------------------------------------------%
% RETURNS: a structure
% results.meth = 'felogit'
% results.beta = MLE of beta vector
% results.covb = covariance matrix of results.beta (negative
% inverse of appropriate block of Hessian, H11)
% results.stdb = standard errors of results.beta
% results.tstatb = t-stats for results.beta
% results.c = MLE of fixed effects vector c
% results.yfit = fitted y-values, i.e., Pr(y_it=1)
% results.lik = value of log-likelihood
% results.cnvg = convergence criterion
% results.iter = number of iterations
% results.nobs = total number of observations
% results.nvar = number of covariates
% results.y = y vector
% results.N = number of units (i)
% results.T = maximum number of observations on a single
% unit (i)
% results.n0 = number of units (i) dropped because y_it=0
% for all t
% results.n1 = number of units (i) dropped because y_it=1
% for all t
% results.d = sparse design matrix of estimated fixed effects
% results.rsqr = McFadden pseudo-R-squared
% results.lratio1 = LR test-statistic: no slopes, common intercept
% results.lratio2 = LR test-statistic: no heterogeneity (just
% slopes and common intercept)
% results.covc = covariance matrix of the estimated fixed effects
% (negative inverse of appropriate block of Hessian, H22)
% results.stdc = standard errors of estimated fixed effects
% results.tstatc = t-stats for estimated fixed effects
% results.covbc = cov(results.beta,results.c), i.e., negative
% inverse appropriate block of Hessian, H12
%
%----------------------------------------------------------------%
% Notes:
% 1. Data should be sorted by t within i. A future version of
% will check that the data are sorted and sort if required.
% 2. felogit uses the functions ols() and logit() for computing
% starting values and testing, respectively. These functions are
% part of the Econometrics Toolbox by James P. LeSage, and
% are available at www.spatial-econometrics.com
% 3. felogit() uses an algorithm to compute the MLE of the fixed
% effects logit model which uses an analytic simplification of the
% Newton-Raphson update. Thanks to George Jakubson for showing me
% this computational trick.
% 4. Generally speaking, the felogit has been optimized for solving
% 'moderately large' problems (i.e., nobs = 500,000 and N = 50,000
% or so). The multiple clear and pack statements can be removed to
% speed up operation in smaller jobs.
% 5. Estimation can be sped up by commenting out code that computes
% the covariance of the estimated fixed effects and t-stats for the
% estimated fixed effects.
%----------------------------------------------------------------%
% See also: prt_felogit, felogit_lik, ols, logit, mm_felogit
%----------------------------------------------------------------%
% References: A note on maximum likelihood fixed effects
% estimation by George Jakubson, available from sdw9@cornell.edu
%----------------------------------------------------------------%
% written by:
% Simon D. Woodcock
% CISER / Economics
% Cornell University
% Ithaca, NY
% sdw9@cornell.edu
%---------------------------------------------------------%
% ERROR CHECKING AND PRELIMINARY CALCULATIONS %
%---------------------------------------------------------%
if (nargin < 4); error('Too few arguments to felogit'); end;
if (nargin > 8); error('Too many arguments to felogit'); end;
[nobs junk] = size(y);
[nx nvar] = size(x);
if nobs ~= nx; error('y and x must have the same row dimension'); end;
% create i,t indices
ii = zeros(nobs,1); ii(1,1) = 1; it(1,1) = 1;
for i = 2:nobs;
it(i,1) = i; % this is original row position index, needed for reassembly in felogitmask
if ivec(i,1) == ivec(i-1,1)
ii(i,1) = ii(i-1,1);
else
ii(i,1) = ii(i-1,1) + 1;
end;
end;
N = max(ii);
if nargin == 4; maxit = 100; tol = 1e-8;
res = ols(y,x); beta0 = res.beta;
c0 = zeros(N,1);
end;
if nargin == 5; maxit = 100; tol = 1e-8; c0 = zeros(N,1); end;
if nargin > 5 % check if beta0 or c0 are empty
[tmp junk] = size(beta0);
if tmp == 0; res = ols(y,x); beta0 = res.beta; end;
[tmp junk] = size(c0);
if tmp == 0; c0 = zeros(N,1); end;
end;
if nargin == 6; maxit = 100; tol = 1e-8; end;
if nargin == 7; tol = 1e-8; end;
if ~isnumeric(ivec); error('Argument ivec must be numeric'); end;
if ~isnumeric(tvec); error('Argument tvec must be numeric'); end;
% check for all 1's or all 0's
tmp = find(y==1); chk = length(tmp); [nobs junk] = size(y);
if chk == nobs; error('logit: y-vector contains all ones');
elseif chk == 0; error('logit: y-vector contains no ones'); end;
% create sparse design matrix
d = sparse(1:nobs,ii,1);
% check for all 1's or all 0's within i -- these observations must be dropped
% or else the algorithm below tries to set their c_i to +/- infinity
T_i = sum(d); % (1 x N) vector of # observations on each i
tmp = d'*y; % (N x 1) vector of sum of y_it for each i
[i0,junk] = find(tmp==0); % row indices are {i : y_it=0 , all t}
[i1,junk] = find(tmp==T_i'); % row indices are {i : y_it=1 , all t}
n0 = length(i0); n1 = length(i1);
tmp = ~ismember(ii,i0) & ~ismember(ii,i1);
tmp2 = ~ismember(1:N,i0) & ~ismember(1:N,i1);
save tmp y x; % save a copy of the original data for testing
% delete observations with all 1's or all 0's
y = y(logical(tmp),:);
x = x(logical(tmp),:);
c0 = c0(logical(tmp2),:);
d = d(logical(tmp),logical(tmp2));
disp(['Observations on ' num2str(n0) ' units were dropped because y_it=0 for all t.']);
disp('The corresponding values of i (from ivec) have been written to the file i0.mat');
disp(' ');
savei0 = unique(ivec(logical(ismember(ii,i0)),:)); save i0.mat savei0; clear savei0;
disp(['Observations on ' num2str(n1) ' units were dropped because y_it=1 for all t.']);
disp('The corresponding values of i (from ivec) have been written to the file i1.mat');
disp(' ');
savei1 = unique(ivec(logical(ismember(ii,i1)),:)); save i1.mat savei1; clear savei1;
ivec = ivec(logical(tmp),:);
tvec = tvec(logical(tmp),:);
% redefine necessary variables
[nobs junk] = size(y);
% recreate i,t indices
clear ii;
ii = zeros(nobs,1); ii(1,1) = 1;
tt = zeros(nobs,1); tt(1,1) = 1;
for it = 2:nobs;
if ivec(it,1) == ivec(it-1,1)
ii(it,1) = ii(it-1,1);
tt(it,1) = tt(it-1,1) + 1;
else
ii(it,1) = ii(it-1,1) + 1;
tt(it,1) = 1;
end;
end;
N = max(ii);
T = max(tt);
% standardize data
xstd = [1 std(x(:,2:nvar))];
x = x ./ ( ones(nobs,1)*xstd );
beta0 = beta0 .* xstd';
% clean up memory before the update
clear ii ivec tt tvec tmp tmp2 junk hunk i0 i1 T_i chk nx res;
pack;
%---------------------------------------------------------%
% MAXIMUM LIKELIHOOD ESTIMATION OF FIXED EFFECTS LOGIT %
%---------------------------------------------------------%
b = beta0;
c = c0; clear beta0 c0;
iter = 0;
% likelihood and derivatives at starting values
[P,lnL] = felogit_lik(y,x,b,c,d);
[sb sc H11 H12 H22 ] = felogit_deriv(y,x,d,P,nvar,nobs,N);
H22inv = H22 .^ (-1); % H22 is sparse diagonal so invert element by element
H_11 = (H11 - H12*H22inv*H12')\eye(nvar);
% Newton - Raphson update
while (abs(sb'*H_11*sb/length(sb)) > tol) & (iter < maxit)
iter = iter + 1;
bold = b;
cold = c;
db = - H_11*(sb - H12*H22inv*sc);
b = bold + db; % update b
dc = - H22inv*(sc + H12'*db);
c = cold + dc; % update c
[P,lnL] = felogit_lik(y,x,b,c,d); % update P, lnL
[sb sc H11 H12 H22 ] = felogit_deriv(y,x,d,P,nvar,nobs,N); % update gradients and Hessian
H22inv = H22 .^ (-1); % update inv(H22)
H_11 = (H11 - H12*H22inv*H12')\eye(nvar); % update H_11
disp(['iteration: ' num2str(iter)]);
disp(['log-likelihood: ' num2str(lnL)]);
end;
clear bold cold db dc i it maxit sb sc;
pack;
%---------------------------------------------------------%
% GENERATE RESULTS STRUCTURE %
%---------------------------------------------------------%
results.meth = 'felogit';
results.beta = b ./ xstd'; % restore original scale
results.covb = -H_11 ./ (xstd'*xstd); % restore original scale
results.stdb = sqrt(diag(results.covb));
results.tstatb = results.beta ./ results.stdb;
results.c = c;
results.yfit = P;
results.lik = lnL;
results.cnvg = tol;
results.iter = iter;
results.nobs = nobs;
results.nvar = nvar;
results.y = y;
results.N = N;
results.T = T;
results.n0 = n0;
results.n1 = n1;
% basic specification testing;
p = sum(y) / nobs;
lnLr = nobs*(p*log(p) + (1-p)*log(1-p)); % restricted log-likelihood: common intercept ONLY (no slopes)
results.lratio1 = -2*(lnLr - results.lik); % LR test based on lnLr
results.rsqr = 1 - (results.lik / lnLr); % McFadden pseudo-R^2
load tmp y x; delete tmp.mat; % reload complete data
[nobs junk] = size(x);
x = [ones(nobs,1) x];
restricted = logit(y,x); % estimate restricted model: no heterogeneity (common intercept)
results.lratio2 = -2*(restricted.lik - results.lik); % LR test based on restricted.lik
% The following computes the covariance matrix for the fixed
% effects. It is computationally demanding (and probably not
% useful in most applications) so you may wish to comment it out
save all.mat; clear; load all.mat H22 H12 H11 N;
res.covc = -(H22-H12'*(H11\H12))\eye(N);
res.stdc = sqrt(diag( res.covc ));
load all.mat results;
results.tstatc = results.c ./ res.stdc;
results.covc = res.covc; results.stdc = res.stdc;
clear res H22 H11; pack;
load all.mat H_11 H22inv xstd; delete all.mat;
results.covbc = H_11*H12*H22inv ./ (xstd'*ones(1,N)); % restore original scale
% End optional code
%---------------------------------------------------------%
% SUPPLEMENTARY FUNCTION FOR COMPUTING DERIVATIVES %
%---------------------------------------------------------%
function [sb,sc,H11,H12,H22] = felogit_deriv(y,x,d,P,nvar,nobs,N);
% PURPOSE: Computes gradient and elements of Hessian for the
% fixed effects logit model;
% ---------------------------------------------------------
% written by:
% Simon D. Woodcock
% CISER / Economics
% Cornell University
% Ithaca, NY
% sdw9@cornell.edu
% the step variable is introduced to tradeoff memory usage
% against compute time in large problems
% the size of crit should be reduced on systems with very little RAM
crit=12000000;
if nobs*N < crit
step = N;
else
step = max( round(crit/nobs) , 1);
end;
% gradient vectors
sb = x'*(y-P);
sc = d'*(y-P);
% H11 is top-left nvar x nvar block of Hessian
tmp1 = ( (ones(nobs,1) - P)*ones(1,nvar) ) .* x;
tmp2 = ( P*ones(1,nvar) ) .* x;
H11 = -(tmp1'*tmp2);
clear tmp1;
% H12 is top-right nvar x N block of Hessian
% H22 is bottom-right N x N block of Hessian
% use a for loop to avoid creating (nobs x N) non-sparse matrices
H22 = sparse(N,N);
H12 = zeros(nvar,N);
pack; % defragment memory
for i = 1:N/step; % do this in (nobs x step) blocks to speed things up
f = (i-1)*step + 1;
l = i*step;
tmp3 = ( (ones(nobs,1) - P)*ones(1,step) ) .* d(:,f:l);
tmp1 = ( P*ones(1,step) ) .* d(:,f:l);
H12(:,f:l) = - (tmp2'*tmp3);
H22(f:l,f:l) = - (tmp1'*tmp3);
end;
tmp3 = ( (ones(nobs,1) - P)*ones(1,N-(i*step)) ) .* d(:,(i*step)+1:N); % the remainder
tmp1 = ( P*ones(1,N-(i*step)) ) .* d(:,(i*step)+1:N);
H12(:,(i*step)+1:N) = - (tmp2'*tmp3);
H22((i*step)+1:N,(i*step)+1:N) = - (tmp1'*tmp3);
clear tmp1 tmp2 tmp3 step;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -