module Statsample # Module for generic MLE calculations. # Use subclass of BaseMLE for specific MLE model estimation. # You should visit Statsample::Regression for method to perform fast # regression analysis. # == Usage: # # mle=Statsample::MLE::Probit.new # mle.newton_raphson(x,y) # beta=mle.parameters # likehood=mle.likehood(x,y,beta) # iterations=mle.iterations # module MLE class BaseMLE attr_accessor :verbose attr_accessor :output # Could be :parameters or :mle attr_accessor :stop_criteria # Variance - Covariance matrix attr_reader :var_cov_matrix # Iterations attr_reader :iterations # Parameters (beta coefficients) attr_reader :parameters ITERATIONS=100 MIN_DIFF=1e-5 MIN_DIFF_PARAMETERS=1e-2 # Model should be a MLE subclass def initialize() @verbose = false @output = STDOUT @stop_criteria = :parameters @var_cov_matrix = nil @iterations = nil @parameters = nil end # Calculate likehood for matrices x and y, given b parameters def likehood(x,y,b) prod=1 x.row_size.times{|i| xi=Matrix.rows([x.row(i).to_a.collect{|v| v.to_f}]) y_val=y[i,0].to_f #fbx=f(b,x) prod=prod*likehood_i(xi, y_val ,b) } prod end # Calculate log likehood for matrices x and y, given b parameters def log_likehood(x,y,b) sum=0 x.row_size.times{|i| xi=Matrix.rows([x.row(i).to_a.collect{|v| v.to_f}]) y_val=y[i,0].to_f sum+=log_likehood_i(xi,y_val,b) } sum end # Creates a zero matrix Mx1, with M=x.M def set_default_parameters(x) fd=[0.0]*x.column_size fd.push(0.1) if self.is_a? Statsample::MLE::Normal Matrix.columns([fd]) end # Newton Raphson with automatic stopping criteria. # Based on: Von Tessin, P. (2005). Maximum Likelihood Estimation With Java and Ruby # # x:: matrix of dependent variables. Should have nxk dimensions # y:: matrix of independent values. Should have nx1 dimensions # @m:: class for @ming. Could be Normal or Logit # start_values:: matrix of coefficients. Should have 1xk dimensions def newton_raphson(x,y, start_values=nil) # deep copy? if start_values.nil? parameters=set_default_parameters(x) else parameters = start_values.dup end k=parameters.row_size #cv=Matrix.rows([([1.0]*k)]) #last_diff=nil raise "n on y != n on x" if x.row_size!=y.row_size h=nil fd=nil if @stop_criteria==:mle old_likehood=log_likehood(x, y, parameters) else old_parameters=parameters end ITERATIONS.times do |i| @iterations=i+1 puts "Set #{i}" if @verbose h = second_derivative(x,y,parameters) if h.singular? raise "Hessian is singular!" end fd = first_derivative(x,y,parameters) parameters = parameters-(h.inverse*(fd)) if @stop_criteria==:parameters flag=true k.times do |j| diff= ( parameters[j,0] - old_parameters[j,0] ) / parameters[j,0] flag=false if diff.abs >= MIN_DIFF_PARAMETERS @output.puts "Parameters #{j}: #{diff}" if @verbose end if flag @var_cov_matrix = h.inverse*-1.0 return parameters end old_parameters=parameters else begin new_likehood = log_likehood(x,y,parameters) @output.puts "[#{i}]Log-MLE:#{new_likehood} (Diff:#{(new_likehood-old_likehood) / new_likehood})" if @verbose if(new_likehood < old_likehood) or ((new_likehood - old_likehood) / new_likehood).abs < MIN_DIFF @var_cov_matrix = h.inverse*-1.0 #@output.puts "Ok" break; end old_likehood=new_likehood rescue =>e puts "#{e}" #puts "dup" end end end @parameters=parameters parameters end end end end require 'statsample/mle/normal' require 'statsample/mle/logit' require 'statsample/mle/probit'