require 'pavement_condition_index/lookups/observed_deduct_values' require 'pavement_condition_index/lookups/observed_corrected_deduct_values' require 'pp' require 'matrix'
namespace :regression do
task :generate_deduct_coefficients do output = PavementConditionIndex::Lookups::ObservedDeductValues::OBSERVED_VALUES.map do |pavement_type_key,pavement_type_values| remapped_pavement_type_values = pavement_type_values.map do |distress_type_key,distress_type_values| coefficients = {low: nil, medium: nil, high: nil}.map do |severity_key,coefficient| polynomial_degree = 0 @candidate_coefficients = [] @x_values = distress_type_values[:chart_type] == :log ? distress_type_values[:x_values].map {|x| Math.log10(x)} : distress_type_values[:x_values] loop do polynomial_degree += 1 @candidate_coefficients = regress_polynomial(@x_values,distress_type_values[:expected_y_values][severity_key],polynomial_degree) candidate_y_values = @x_values.map {|x| evaluate_polynomial(*@candidate_coefficients).call(x) } sum_of_squares = 0.0 distress_type_values[:x_values].each_with_index do |x, index| sum_of_squares += (candidate_y_values[index] - distress_type_values[:expected_y_values][severity_key][index])**2 end break if sum_of_squares < 10.0 end [severity_key, @candidate_coefficients] end.to_h remapped_distress_type_values = { valid_min: distress_type_values[:valid_min], valid_max: distress_type_values[:valid_max], chart_type: distress_type_values[:chart_type], coefficients: coefficients } [distress_type_key,remapped_distress_type_values] end.to_h [pavement_type_key,remapped_pavement_type_values] end.to_h pp output end task :generate_corrected_deduct_coefficients do output = PavementConditionIndex::Lookups::ObservedCorrectedDeductValues::OBSERVED_VALUES.map do |pavement_type_key,pavement_type_values| coefficients = pavement_type_values[:expected_y_values].map do |q_value_key,observed_values| polynomial_degree = 0 @candidate_coefficients = [] @x_values = pavement_type_values[:x_values] loop do polynomial_degree += 1 @candidate_coefficients = regress_polynomial(@x_values,observed_values,polynomial_degree) candidate_y_values = @x_values.map {|x| evaluate_polynomial(*@candidate_coefficients).call(x) } sum_of_squares = 0.0 pavement_type_values[:x_values].each_with_index do |x, index| sum_of_squares += (candidate_y_values[index] - observed_values[index])**2 end break if sum_of_squares < 10.0 end [q_value_key, @candidate_coefficients] end.to_h remapped_pavement_type_values = { coefficients: coefficients } [pavement_type_key,remapped_pavement_type_values] end.to_h pp output end task :generate_calculated_deduct_coefficients_file do header = "# generate this whole file by running the following command:\n"+ "# rake regression:generate_calculated_deduct_coefficients_file > lib/pavement_condition_index/lookups/calculated_deduct_coefficients.rb\n"+ "\n"+ "module PavementConditionIndex\n"+ " module Lookups\n"+ " class CalculatedDeductCoefficients\n"+ "\n"+ "# Output of `rake regression:generate_deduct_coefficients`\n"+ "COEFFICIENTS = " puts header Rake::Task["regression:generate_deduct_coefficients"].invoke footer = " end\n"+ " end\n"+ "end\n" puts footer end task :generate_calculated_corrected_deduct_coefficients_file do header = "# generate this whole file by running the following command:\n"+ "# rake regression:generate_calculated_corrected_deduct_coefficients_file > lib/pavement_condition_index/lookups/calculated_corrected_deduct_coefficients.rb\n"+ "\n"+ "module PavementConditionIndex\n"+ " module Lookups\n"+ " class CalculatedCorrectedDeductCoefficients\n"+ "\n"+ "# Output of `rake regression:generate_corrected_deduct_coefficients`\n"+ "COEFFICIENTS = " puts header Rake::Task["regression:generate_corrected_deduct_coefficients"].invoke footer = " end\n"+ " end\n"+ "end\n" puts footer end
end
def regress_polynomial x, y, degree
rows = x.map do |i| (0..degree).map { |power| (i ** power).to_f } end mx, my = Matrix.rows(rows), Matrix.columns([y]) ((mx.transpose * mx).inv * mx.transpose * my).transpose.row(0).to_a
end
def evaluate_polynomial(*coefficients)
Proc.new {|x| coefficients.map.with_index{|c,i| c*(x**i)}.reduce(:+)}
end