src/math/tools/DataAnalysis/MultipleRegressionAnalysis.js
/**
* The script is part of konpeito.
*
* AUTHOR:
* natade (http://twitter.com/natadea)
*
* LICENSE:
* The MIT license https://opensource.org/licenses/MIT
*/
import Matrix from "../../core/Matrix.js";
/**
* Settings for multiple regression analysis
* @typedef {Object} KMultipleRegressionAnalysisSettings
* @property {import("../../core/Matrix.js").KMatrixInputData} samples explanatory variable. (Each column is a parameters and each row is a samples.)
* @property {import("../../core/Matrix.js").KMatrixInputData} target response variable. / actual values. (column vector)
* @property {boolean} [is_standardised=false] Use standardized partial regression coefficients.
*/
/**
* Vector state
* @typedef {Object} KMultipleRegressionAnalysisVectorState
* @property {number} df degree of freedom
* @property {number} SS sum of squares
* @property {number} MS unbiased_variance
*/
/**
* Analysis of variance. ANOVA.
* @typedef {Object} KMultipleRegressionAnalysisAnova
* @property {KMultipleRegressionAnalysisVectorState} regression regression.
* @property {KMultipleRegressionAnalysisVectorState} residual residual error.
* @property {KMultipleRegressionAnalysisVectorState} total total.
* @property {number} F F value. Dispersion ratio (F0)
* @property {number} significance_F Significance F. Test with F distribution with q, n-q-1 degrees of freedom.(Probability of error.)
*/
/**
* Regression table data.
* @typedef {Object} KMultipleRegressionAnalysisPartialRegressionData
* @property {number} coefficient Coefficient.
* @property {number} standard_error Standard error.
* @property {number} t_stat t-statistic.
* @property {number} p_value P-value. Risk factor.
* @property {number} lower_95 Lower limit of a 95% confidence interval.
* @property {number} upper_95 Upper limit of a 95% confidence interval.
*/
/**
* Regression table.
* @typedef {Object} KMultipleRegressionAnalysisPartialRegression
* @property {KMultipleRegressionAnalysisPartialRegressionData} intercept Intercept.
* @property {KMultipleRegressionAnalysisPartialRegressionData[]} parameters Parameters.
*/
/**
* Output for multiple regression analysis
* @typedef {Object} KMultipleRegressionAnalysisOutput
* @property {number} q number of explanatory variables.
* @property {number} n number of samples.
* @property {number[][]} predicted_values predicted values. (column vector)
* @property {number} sY Variance of predicted values of target variable.
* @property {number} sy Variance of measured values of target variable.
* @property {number} multiple_R Multiple R. Multiple correlation coefficient.
* @property {number} R_square R Square. Coefficient of determination.
* @property {number} adjusted_R_square Adjusted R Square. Adjusted coefficient of determination.
* @property {KMultipleRegressionAnalysisAnova} ANOVA analysis of variance.
* @property {number} Ve Unbiased variance of residuals. (Ve)
* @property {number} standard_error Standard error. (SE)
* @property {number} AIC Akaike's Information Criterion. (AIC)
* @property {KMultipleRegressionAnalysisPartialRegression} regression_table Regression table.
*/
/**
* Multiple regression analysis.
*/
export default class MultipleRegressionAnalysis {
/**
* Multiple regression analysis
* @param {KMultipleRegressionAnalysisSettings} settings - input data
* @returns {KMultipleRegressionAnalysisOutput} analyzed data
*/
static runMultipleRegressionAnalysis(settings) {
// 最小二乗法により重回帰分析する。
// 参考文献
// [1] 図解でわかる多変量解析―データの山から本質を見抜く科学的分析ツール
// 涌井 良幸, 涌井 貞美, 日本実業出版社 (2001/01)
// [2] これならわかる Excelで楽に学ぶ多変量解析
// 長谷川 勝也, 技術評論社 (2002/07)
// [3] ど素人の「Excel 回帰分析」表の見方 (単回帰分析)
// http://atiboh.sub.jp/t07kaikibunseki.html
// [4] 赤池の情報量基準(AIC)の計算方法
// http://software.ssri.co.jp/statweb2/tips/tips_10.html
// samples 説明変量。行がサンプル。列が各値。
// target 目的変量・実測値。縦ベクトル。
// is_standardised trueで標準化偏回帰係数
let samples = Matrix.create(settings.samples);
let target = Matrix.create(settings.target);
const set_unbiased = {correction : 1};
const set_sample = {correction : 0};
// 標準化偏回帰係数を調べるために平均0 分散1に正規化する
if(settings.is_standardised) {
samples = samples.standardization();
target = target.standardization();
}
// 説明変量・説明変数の数 q
const number_of_explanatory_variables = Matrix.create(samples.width);
// 標本数(観測数) n
const number_of_samples = Matrix.create(samples.height);
// 共分散行列
const S = samples.cov(set_unbiased);
const S_rcond = S.rcond();
// どこかの値に相関が非常に高いものがあり計算できない。
if(S_rcond <= 1e-10) {
console.log("Analysis failed due to highly correlated explanatory variables.(rcond : " + S_rcond + ")");
return null;
}
// 目的変量との共分散(縦ベクトル)
const y_array = [];
const max_var = number_of_explanatory_variables.intValue;
for(let i = 0; i < max_var; i++) {
y_array[i] = [ samples.getMatrix(":", i).cov(target, set_unbiased) ];
}
const Y = Matrix.create(y_array);
// 偏回帰係数(縦ベクトル) partial regression coefficient. (column vector)
const partial_regression_coefficient = S.inv().mul(Y);
// バイアス・定数項・切片 bias
const bias = target.mean().sub(samples.mean().mul(partial_regression_coefficient));
// 予測値(縦ベクトル) predicted values. (column vector)
const predicted_values = samples.mul(partial_regression_coefficient).add(bias);
// 目的変量の予測値の分散
const sY = predicted_values.var(set_unbiased);
// 目的変量の実測値の分散
const sy = target.var(set_unbiased);
// 重相関係数
const multiple_R = predicted_values.corrcoef(target, set_unbiased);
// 決定係数・寄与率
const R_square = sY.div(sy);
// 回帰
const regression_df = number_of_explanatory_variables; // 自由度
const regression_SS = predicted_values.sub(target.mean()).dotpow(2).sum(); // 平方和(変動)・MSr
const regression_MS = regression_SS.div(regression_df); // 不偏分散(分散)
// 残差 residual error
const residual_df = number_of_samples.sub(number_of_explanatory_variables).sub(1); // 自由度
const residual_SS = predicted_values.sub(target).dotpow(2).sum(); // 平方和(変動)・MSe
const residual_MS = residual_SS.div(residual_df); // 不偏分散(分散)
// 全体
const total_df = number_of_samples.sub(1); // 自由度
const total_SS = target.sub(target.mean()).dotpow(2).sum(); // 平方和(変動)・MSt・VE
const total_MS = total_SS.div(total_df); // 不偏分散(分散)
// Ve(残差の不偏分散)
const Ve = residual_MS;
// SE(標準誤差, SE, standard error)
const standard_error = Ve.sqrt();
// 回帰の分散比(F値)(観測された分散比)・F0
const regression_F = regression_MS.div(residual_MS);
// 回帰の有意 F significance F
// 自由度 q, n-q-1 のF分布による検定
// 誤りが発生する確率(1 - cdf('F',X,A,B))
// F分布を用いて、誤りが発生する確率を調べる (有意 F)
const regression_significance_F = Matrix.ONE.sub(regression_F.fcdf(regression_df, residual_df));
// 自由度修正済決定係数・補正R2 adjusted R2, 自由度修正済決定係数 / 自由度調整済寄与率
// 1 - (残差による変動 / 残差の自由度) / (全変動 / 全体の自由度)
const adjusted_R_square = Matrix.ONE.sub(residual_MS.div(total_MS));
// 赤池情報量規準(Akaike's Information Criterion, AIC)
// 回帰式に定数項を含む場合の式
// out.n * (log(2 * pi * (table(2, 2)/out.n)) + 1) + 2 * (out.q + 2);
const AIC = number_of_samples.mul(
residual_SS.div(number_of_samples).mul(2.0 * Math.PI).log().add(1)
).add(number_of_explanatory_variables.add(2).mul(2));
// ここからは偏回帰の値を計算していく
// 偏差平方和・積和行列の逆行列を作る
// つまり、共分散行列の各共分散で(サンプル数N)を割らない値を求めればいい。
// 不偏の場合は、 偏差平方和 * ( N * (N-1) ) を求めれば良い。
const IS = S.dotmul(number_of_samples).inv();
// 初期化
const intercept = {
coefficient : Matrix.ZERO,
standard_error : Matrix.ZERO,
t_stat : Matrix.ZERO,
p_value : Matrix.ZERO,
lower_95 : Matrix.ZERO,
upper_95 : Matrix.ZERO
};
const parameters = [];
for(let i = 0; i < max_var; i++) {
parameters[i] = {
coefficient : Matrix.ZERO,
standard_error : Matrix.ZERO,
t_stat : Matrix.ZERO,
p_value : Matrix.ZERO,
lower_95 : Matrix.ZERO,
upper_95 : Matrix.ZERO
};
}
// 係数
{
// 切片の係数
intercept.coefficient = bias;
// 偏回帰の係数
for(let i = 0; i < max_var; i++) {
parameters[i].coefficient = new Matrix(partial_regression_coefficient.getComplex(i));
}
}
// 標準誤差
{
// 切片の標準誤差
const q = number_of_explanatory_variables.intValue;
let s = number_of_samples.inv();
for(let j = 0; j < q; j++) {
for(let k = 0; k < q; k++) {
s = s.add(samples.getMatrix(":", j).mean().mul(samples.getMatrix(":", k).mean()).mul(IS.getMatrix(j, k)));
}
}
intercept.standard_error = s.mul(Ve).sqrt();
// 偏回帰の標準誤差
for(let i = 0; i < max_var; i++) {
parameters[i].standard_error = IS.getMatrix(i, i).mul(Ve).sqrt();
}
}
{
/**
* t*値, P値, 信頼区間
* @param {any} data
* @ignore
*/
const calcTPI = function(data) {
// t*値, 影響度, 統計量t, t-statistic.
// 大きいほど目的変数との関連性が強い
/**
* @type {Matrix}
*/
data.t_stat = data.coefficient.div(data.standard_error);
// P値, 危険率, P-value. Risk factor.
// 切片と偏回帰係数が誤っている確率
// スチューデントの t 分布の確率密度関数を利用
/**
* @type {Matrix}
*/
data.p_value = data.t_stat.tdist(residual_df, 2);
// 信頼区間の計算
// 下限 95%, 上限 95%
const percent = new Matrix(1.0 - 0.95);
data.lower_95 = data.coefficient.sub(percent.tinv2(residual_df).mul(data.standard_error));
data.upper_95 = data.coefficient.add(percent.tinv2(residual_df).mul(data.standard_error));
};
calcTPI(intercept);
for(let i = 0; i < max_var; i++) {
calcTPI(parameters[i]);
}
}
/**
* @type {KMultipleRegressionAnalysisPartialRegression}
*/
let regression_table = null;
{
/**
* @type {KMultipleRegressionAnalysisPartialRegressionData}
*/
const intercept_data = {
coefficient : intercept.coefficient.doubleValue,
standard_error : intercept.standard_error.doubleValue,
t_stat : intercept.t_stat.doubleValue,
p_value : intercept.p_value.doubleValue,
lower_95 : intercept.lower_95.doubleValue,
upper_95 : intercept.upper_95.doubleValue
};
/**
* @type {KMultipleRegressionAnalysisPartialRegressionData[]}
*/
const parameters_data = [];
for(let i = 0; i < max_var; i++) {
parameters_data.push({
coefficient : parameters[i].coefficient.doubleValue,
standard_error : parameters[i].standard_error.doubleValue,
t_stat : parameters[i].t_stat.doubleValue,
p_value : parameters[i].p_value.doubleValue,
lower_95 : parameters[i].lower_95.doubleValue,
upper_95 : parameters[i].upper_95.doubleValue
});
}
regression_table = {
intercept : intercept_data,
parameters : parameters_data
};
}
/**
* @type {KMultipleRegressionAnalysisOutput}
*/
const output = {
q : number_of_explanatory_variables.doubleValue,
n : number_of_samples.doubleValue,
predicted_values : predicted_values.getNumberMatrixArray(),
sY : sY.doubleValue,
sy : sy.doubleValue,
multiple_R : multiple_R.doubleValue,
R_square : R_square.doubleValue,
adjusted_R_square : adjusted_R_square.doubleValue,
ANOVA : {
regression : {
df : regression_df.doubleValue,
SS : regression_SS.doubleValue,
MS : regression_MS.doubleValue
},
residual : {
df : residual_df.doubleValue,
SS : residual_SS.doubleValue,
MS : residual_MS.doubleValue
},
total : {
df : total_df.doubleValue,
SS : total_SS.doubleValue,
MS : total_MS.doubleValue
},
F : regression_F.doubleValue,
significance_F : regression_significance_F.doubleValue,
},
Ve : Ve.doubleValue,
standard_error : standard_error.doubleValue,
AIC : AIC.doubleValue,
regression_table : regression_table
};
return output;
}
}