|
@@ -7,6 +7,7 @@ import (
|
|
|
"eta/eta_api/models/system"
|
|
|
"eta/eta_api/utils"
|
|
|
"fmt"
|
|
|
+ "math"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
@@ -86,7 +87,7 @@ func ResidualAnalysisPreview(req residual_analysis_model.ResidualAnalysisReq) (r
|
|
|
}
|
|
|
|
|
|
// 映射图表信息
|
|
|
- mappingEdbList, err := fillMappingChartInfo(req, edbInfoMappingA, edbInfoMappingB, originalEdbList, indexADataMap)
|
|
|
+ mappingEdbList, a, b, r, err := fillMappingChartInfo(req, edbInfoMappingA, edbInfoMappingB, originalEdbList, indexADataMap)
|
|
|
if err != nil {
|
|
|
return residual_analysis_model.ResidualAnalysisResp{}, err
|
|
|
}
|
|
@@ -98,7 +99,7 @@ func ResidualAnalysisPreview(req residual_analysis_model.ResidualAnalysisReq) (r
|
|
|
}
|
|
|
|
|
|
// 残差图表信息
|
|
|
- ResidualEdbList, err := fillResidualChartInfo(edbInfoMappingA, edbInfoMappingB, mappingEdbList)
|
|
|
+ ResidualEdbList, R2, err := fillResidualChartInfo(edbInfoMappingA, edbInfoMappingB, mappingEdbList)
|
|
|
if err != nil {
|
|
|
return residual_analysis_model.ResidualAnalysisResp{}, err
|
|
|
}
|
|
@@ -108,12 +109,16 @@ func ResidualAnalysisPreview(req residual_analysis_model.ResidualAnalysisReq) (r
|
|
|
resp.MappingChartData = residual_analysis_model.ChartResp{
|
|
|
ChartInfo: baseChartInfo,
|
|
|
EdbInfoList: ResidualEdbList,
|
|
|
+ A: a,
|
|
|
+ B: b,
|
|
|
+ R: r,
|
|
|
+ R2: R2,
|
|
|
}
|
|
|
|
|
|
return resp, nil
|
|
|
}
|
|
|
|
|
|
-func fillResidualChartInfo(edbInfoMappingA *data_manage.ChartEdbInfoMapping, edbInfoMappingB *data_manage.ChartEdbInfoMapping, mappingEdbList []residual_analysis_model.ResidualAnalysisChartEdbInfoMapping) ([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, error) {
|
|
|
+func fillResidualChartInfo(edbInfoMappingA *data_manage.ChartEdbInfoMapping, edbInfoMappingB *data_manage.ChartEdbInfoMapping, mappingEdbList []residual_analysis_model.ResidualAnalysisChartEdbInfoMapping) ([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, float64, error) {
|
|
|
// 计算公式 映射残差 = 因变量指标 - 映射指标
|
|
|
var edbInfoA, edbInfoB residual_analysis_model.ResidualAnalysisChartEdbInfoMapping
|
|
|
if mappingEdbList[0].EdbInfoId == edbInfoMappingA.EdbInfoId {
|
|
@@ -125,26 +130,46 @@ func fillResidualChartInfo(edbInfoMappingA *data_manage.ChartEdbInfoMapping, edb
|
|
|
}
|
|
|
dataAList, ok := edbInfoA.DataList.([]*data_manage.EdbDataList)
|
|
|
if !ok {
|
|
|
- return nil, fmt.Errorf("数据类型转换失败", nil)
|
|
|
+ return nil, 0, fmt.Errorf("数据类型转换失败", nil)
|
|
|
}
|
|
|
edbData := dataAList
|
|
|
dataBList, ok := edbInfoB.DataList.([]*data_manage.EdbDataList)
|
|
|
if !ok {
|
|
|
- return nil, fmt.Errorf("数据类型转换失败", nil)
|
|
|
+ return nil, 0, fmt.Errorf("数据类型转换失败", nil)
|
|
|
}
|
|
|
var indexDataBMap = make(map[string]*data_manage.EdbDataList)
|
|
|
for _, data := range dataBList {
|
|
|
indexDataBMap[data.DataTime] = data
|
|
|
}
|
|
|
- var valueB float64
|
|
|
+ // 求R2
|
|
|
+ var valueB, sumValueA, averageValueA, residualQuadraticSum, totalQuadraticSum, R2 float64
|
|
|
+
|
|
|
+ for _, indexData := range edbData {
|
|
|
+ // 因变量的值总和
|
|
|
+ sumValueA += indexData.Value
|
|
|
+ }
|
|
|
+ // 因变量平均值
|
|
|
+ averageValueA = sumValueA / float64(len(edbData))
|
|
|
+
|
|
|
for _, indexData := range edbData {
|
|
|
if dataB, ok := indexDataBMap[indexData.DataTime]; ok {
|
|
|
valueB = dataB.Value
|
|
|
} else {
|
|
|
valueB = 0
|
|
|
}
|
|
|
+
|
|
|
+ // 总因变量平方和
|
|
|
+ totalQuadraticSum += math.Pow(indexData.Value-averageValueA, 2)
|
|
|
+
|
|
|
+ // 补全残差值
|
|
|
indexData.Value = indexData.Value - valueB
|
|
|
+
|
|
|
+ // 残差平方和
|
|
|
+ residualQuadraticSum += math.Pow(indexData.Value, 2)
|
|
|
}
|
|
|
+ // 计算R2 公式:R2=1-SSE/SST R2越大,越符合线性 R2 = 1 - 残差平方和/总平方和
|
|
|
+ R2 = 1 - residualQuadraticSum/totalQuadraticSum
|
|
|
+
|
|
|
for _, mapping := range mappingEdbList {
|
|
|
if mapping.EdbInfoId != edbInfoMappingA.EdbInfoId {
|
|
|
mapping.DataList = edbData
|
|
@@ -152,32 +177,36 @@ func fillResidualChartInfo(edbInfoMappingA *data_manage.ChartEdbInfoMapping, edb
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return mappingEdbList, nil
|
|
|
+ return mappingEdbList, R2, nil
|
|
|
}
|
|
|
|
|
|
-func fillMappingChartInfo(req residual_analysis_model.ResidualAnalysisReq, edbInfoMappingA *data_manage.ChartEdbInfoMapping, edbInfoMappingB *data_manage.ChartEdbInfoMapping, originalEdbList []residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, indexADataMap map[string]*data_manage.EdbDataList) ([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, error) {
|
|
|
+func fillMappingChartInfo(req residual_analysis_model.ResidualAnalysisReq, edbInfoMappingA *data_manage.ChartEdbInfoMapping, edbInfoMappingB *data_manage.ChartEdbInfoMapping, originalEdbList []residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, indexADataMap map[string]*data_manage.EdbDataList) ([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, float64, float64, float64, error) {
|
|
|
// 计算公式:Y=aX+b,Y为映射后的指标,X为自变量指标
|
|
|
// 正序:a=(L2-L1)/(R2-R1) b=L2-R2*a
|
|
|
// 逆序:a=(L2-L1)/(R1-R2) b=L2-R1*a
|
|
|
// L2:左轴下限 R2:右轴上限 L1:左轴上限 R1:右轴下限
|
|
|
- var a, b float64
|
|
|
- if req.IsOrder {
|
|
|
- a = (req.LeftIndexMax - req.LeftIndexMin) / (req.RightIndexMax - req.RightIndexMin)
|
|
|
- b = req.LeftIndexMax - req.RightIndexMax*a
|
|
|
- } else {
|
|
|
- a = (req.LeftIndexMax - req.LeftIndexMin) / (req.RightIndexMin - req.RightIndexMax)
|
|
|
- b = req.LeftIndexMax - req.RightIndexMin*a
|
|
|
+ var a, b, r float64
|
|
|
+
|
|
|
+ // 映射残差 计算a,b
|
|
|
+ if req.ResidualType == 1 {
|
|
|
+ if req.IsOrder {
|
|
|
+ a = (req.LeftIndexMax - req.LeftIndexMin) / (req.RightIndexMax - req.RightIndexMin)
|
|
|
+ b = req.LeftIndexMax - req.RightIndexMax*a
|
|
|
+ } else {
|
|
|
+ a = (req.LeftIndexMax - req.LeftIndexMin) / (req.RightIndexMin - req.RightIndexMax)
|
|
|
+ b = req.LeftIndexMax - req.RightIndexMin*a
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
dataList, ok := edbInfoMappingB.DataList.([]*data_manage.EdbDataList)
|
|
|
if !ok {
|
|
|
- return nil, fmt.Errorf("数据类型转换失败", nil)
|
|
|
+ return nil, a, b, r, fmt.Errorf("数据类型转换失败", nil)
|
|
|
}
|
|
|
|
|
|
// 领先指标 dataList进行数据处理
|
|
|
if req.IndexType == 2 {
|
|
|
if req.LeadValue < 0 {
|
|
|
- return nil, fmt.Errorf("领先值不能小于0", nil)
|
|
|
+ return nil, a, b, r, fmt.Errorf("领先值不能小于0", nil)
|
|
|
} else if req.LeadValue > 0 {
|
|
|
for _, indexData := range dataList {
|
|
|
switch req.LeadFrequency {
|
|
@@ -196,14 +225,12 @@ func fillMappingChartInfo(req residual_analysis_model.ResidualAnalysisReq, edbIn
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 指标B数据补充
|
|
|
for index := 0; index < len(dataList)-1; index++ {
|
|
|
// 获取当前数据和下一个数据
|
|
|
beforeIndexData := dataList[index]
|
|
|
afterIndexData := dataList[index+1]
|
|
|
|
|
|
- // 计算映射值
|
|
|
- beforeIndexData.Value = a*beforeIndexData.Value + b
|
|
|
-
|
|
|
// 从最早时间开始,补充时间为自然日
|
|
|
if utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
|
|
|
// 创建补充数据
|
|
@@ -217,10 +244,32 @@ func fillMappingChartInfo(req residual_analysis_model.ResidualAnalysisReq, edbIn
|
|
|
dataList = append(dataList, &replenishIndexData)
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ // 拟合残差 计算a,b
|
|
|
+ var coordinateList []utils.Coordinate
|
|
|
+ if req.ResidualType == 2 {
|
|
|
+ for _, indexData := range dataList {
|
|
|
+ if _, ok := indexADataMap[indexData.DataTime]; ok {
|
|
|
+
|
|
|
+ coordinate := utils.Coordinate{
|
|
|
+ X: indexData.Value,
|
|
|
+ Y: indexADataMap[indexData.DataTime].Value,
|
|
|
+ }
|
|
|
+ coordinateList = append(coordinateList, coordinate)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ a, b = utils.GetLinearResult(coordinateList)
|
|
|
+ r = utils.CalculationDecisive(coordinateList)
|
|
|
+ }
|
|
|
+
|
|
|
// 根据指标A的时间key,在B的映射指标中筛选出对应的值
|
|
|
var dataBList []*data_manage.EdbDataList
|
|
|
+
|
|
|
for _, indexData := range dataList {
|
|
|
if _, ok := indexADataMap[indexData.DataTime]; ok {
|
|
|
+ // 计算指标B映射值
|
|
|
+ indexData.Value = a*indexData.Value + b
|
|
|
+
|
|
|
dataBList = append(dataBList, indexData)
|
|
|
}
|
|
|
}
|
|
@@ -231,10 +280,10 @@ func fillMappingChartInfo(req residual_analysis_model.ResidualAnalysisReq, edbIn
|
|
|
mapping.EdbInfoId = 0
|
|
|
mapping.EdbCode = ""
|
|
|
mapping.EdbName = edbInfoMappingB.EdbName + "映射" + edbInfoMappingA.EdbName
|
|
|
- mapping.DataList = dataList
|
|
|
+ mapping.DataList = dataBList
|
|
|
}
|
|
|
}
|
|
|
- return mappingEdbList, nil
|
|
|
+ return mappingEdbList, a, b, r, nil
|
|
|
}
|
|
|
|
|
|
func fillOriginalChart(req residual_analysis_model.ResidualAnalysisReq, mappingList []*data_manage.ChartEdbInfoMapping, startDate string, endDate string, edbInfoMappingA *data_manage.ChartEdbInfoMapping, edbInfoMappingB *data_manage.ChartEdbInfoMapping, OriginalEdbList []residual_analysis_model.ResidualAnalysisChartEdbInfoMapping) ([]residual_analysis_model.ResidualAnalysisChartEdbInfoMapping, error) {
|
|
@@ -322,7 +371,7 @@ func SaveResidualAnalysis(req residual_analysis_model.ResidualAnalysisIndexSaveR
|
|
|
// 更新or新增
|
|
|
if req.EdbCode != "" {
|
|
|
// 查询指标库指标
|
|
|
- edbInfo, err := data_manage.GetEdbInfoByEdbCode(utils.DATA_SOURCE_RESIDUAL_ANALYSIS, req.EdbCode)
|
|
|
+ edbInfo, err := data_manage.GetEdbInfoByEdbCode(req.Source, req.EdbCode)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
@@ -355,7 +404,7 @@ func SaveResidualAnalysis(req residual_analysis_model.ResidualAnalysisIndexSaveR
|
|
|
Unit: req.Unit,
|
|
|
UnitEn: req.UnitEn,
|
|
|
Frequency: req.Frequency,
|
|
|
- Source: utils.DATA_SOURCE_RESIDUAL_ANALYSIS,
|
|
|
+ Source: req.Source,
|
|
|
SourceName: "残差分析",
|
|
|
})
|
|
|
if err != nil {
|