Skip to content

Commit 0c15ea5

Browse files
committed
Add the ability for callers to adjust explanatory variables to use when it cannot inverse a matrix
1 parent b1d1f37 commit 0c15ea5

File tree

2 files changed

+93
-4
lines changed

2 files changed

+93
-4
lines changed

v2/error.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package regression
2+
3+
import (
4+
"errors"
5+
"math"
6+
7+
"gonum.org/v1/gonum/mat"
8+
)
9+
10+
var (
11+
ErrNearSingular = &ConditionError{isExactlySingular: false}
12+
ErrExactlySingular = &ConditionError{isExactlySingular: true}
13+
14+
matConditionError = mat.Condition(0) // matrix singular or near-singular
15+
matConditionErrorInf = mat.Condition(math.Inf(1)) // matrix exactly singular
16+
)
17+
18+
type ConditionError struct {
19+
err error
20+
isExactlySingular bool
21+
Hint *ConditionErrorHint
22+
}
23+
24+
func (e ConditionError) Error() string {
25+
if e.err == nil {
26+
return ""
27+
}
28+
return e.err.Error()
29+
}
30+
31+
func (e ConditionError) Is(err error) bool {
32+
if condErr, ok := err.(*ConditionError); ok {
33+
return e.isExactlySingular == condErr.isExactlySingular
34+
}
35+
return false
36+
}
37+
38+
func (e ConditionError) Unwrap() error {
39+
return e.err
40+
}
41+
42+
func wrapAsConditionError(err error, hint *ConditionErrorHint) *ConditionError {
43+
return &ConditionError{
44+
err: err,
45+
isExactlySingular: errors.Is(err, matConditionErrorInf),
46+
Hint: hint,
47+
}
48+
}
49+
50+
type ConditionErrorHint struct {
51+
ExplanatoryVars []ExplanatoryVarHint
52+
}
53+
54+
type ExplanatoryVarHint struct {
55+
OriginalIndex int // 元々のインデックス
56+
Label string // 名称
57+
Coeff float64 // 偏回帰係数 B
58+
VIF float64 // 共線性の統計量 VIF
59+
}
60+
61+
func newExplanatoryVarHints(basicRawModel *basicRawModel, vifs []float64) []ExplanatoryVarHint {
62+
hints := make([]ExplanatoryVarHint, basicRawModel.numOfExplanatoryVars)
63+
64+
for i := 0; i < basicRawModel.numOfExplanatoryVars; i++ {
65+
hints[i] = ExplanatoryVarHint{
66+
OriginalIndex: basicRawModel.indexesTable[i],
67+
Label: basicRawModel.explanatoryVarsLabels[i],
68+
Coeff: basicRawModel.coeffs[i],
69+
VIF: vifs[i],
70+
}
71+
}
72+
73+
return hints
74+
}

v2/regression.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package regression
22

33
import (
44
"errors"
5+
"fmt"
56
"math"
67
"strconv"
78

@@ -363,8 +364,15 @@ func (r *Regression) Run() (*Model, error) {
363364

364365
sRInv := new(mat.Dense)
365366
if err := sRInv.Inverse(sR); err != nil {
366-
logger.Err.Printf("Cannot inverse a matrix: %v", err)
367-
return nil, err
367+
e := fmt.Errorf("cannot inverse a matrix(sR): %w", err)
368+
369+
logger.Err.Println(e)
370+
371+
if errors.As(e, &matConditionError) {
372+
return nil, wrapAsConditionError(e, &ConditionErrorHint{ExplanatoryVars: newExplanatoryVarHints(bm, coeffsVIFs)})
373+
}
374+
375+
return nil, e
368376
}
369377

370378
sInv := new(mat.Dense)
@@ -433,8 +441,15 @@ func (r *Regression) Run() (*Model, error) {
433441
// 相関行列から偏相関行列を求める
434442
corrDenseInv := new(mat.Dense)
435443
if err := corrDenseInv.Inverse(corrDense); err != nil {
436-
logger.Err.Printf("Cannot inverse a matrix: %v", err)
437-
return nil, err
444+
e := fmt.Errorf("cannot inverse a matrix(corrDense): %w", err)
445+
446+
logger.Err.Println(e)
447+
448+
if errors.As(e, &matConditionError) {
449+
return nil, wrapAsConditionError(e, &ConditionErrorHint{ExplanatoryVars: newExplanatoryVarHints(bm, coeffsVIFs)})
450+
}
451+
452+
return nil, e
438453
}
439454
coeffsPartialCorrelations := make([]float64, bm.numOfExplanatoryVars)
440455
for i := range coeffsPartialCorrelations {

0 commit comments

Comments
 (0)