package data import ( "github.com/shopspring/decimal" "hongze/hongze_chart_lib/models" "hongze/hongze_chart_lib/utils" "time" ) // GetChartPredictEdbInfoDataListByRule1 根据规则1获取预测数据 func GetChartPredictEdbInfoDataListByRule1(edbInfoId int, dataValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*models.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*models.EdbDataList) { newPredictEdbInfoData = predictEdbInfoData //获取后面的预测数据 dayList := getPredictEdbDayList(startDate, endDate, frequency) predictEdbInfoData = make([]*models.EdbDataList, 0) for k, v := range dayList { newPredictEdbInfoData = append(newPredictEdbInfoData, &models.EdbDataList{ EdbDataId: edbInfoId + 10000000000 + k, EdbInfoId: edbInfoId, DataTime: v.Format(utils.FormatDate), Value: dataValue, DataTimestamp: (v.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适 }) existMap[v.Format(utils.FormatDate)] = dataValue } return } // GetChartPredictEdbInfoDataListByRuleTb 根据同比值规则获取预测数据 // 2.1 同比: 在未来某一个时间段内,给定一个固定的同比增速a,用去年同期值X乘以同比增速(1+a),得到预测值Y=X(1+a) // 例: 今年1-3月值,100,100,120。给定同比增速a=0.1,则明年1-3月预测值为: 100*1.1=110,100*1.1=110,120*1.1=132。 func GetChartPredictEdbInfoDataListByRuleTb(edbInfoId int, tbValue float64, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*models.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*models.EdbDataList, minValue, maxValue float64) { allDataList := make([]*models.EdbDataList, 0) allDataList = append(allDataList, realPredictEdbInfoData...) allDataList = append(allDataList, predictEdbInfoData...) newPredictEdbInfoData = predictEdbInfoData index := len(allDataList) //获取后面的预测数据 dayList := getPredictEdbDayList(startDate, endDate, frequency) predictEdbInfoData = make([]*models.EdbDataList, 0) for k, currentDate := range dayList { tmpData := &models.EdbDataList{ EdbDataId: edbInfoId + 10000000000 + index + k, EdbInfoId: edbInfoId, DataTime: currentDate.Format(utils.FormatDate), //Value: dataValue, DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适 } var val float64 var calculateStatus bool //计算结果 //currentItem := existMap[av] //上一年的日期 preDate := currentDate.AddDate(-1, 0, 0) preDateStr := preDate.Format(utils.FormatDate) if preValue, ok := existMap[preDateStr]; ok { //上一年同期找到 val = TbzDiv(preValue, tbValue) calculateStatus = true } else { switch frequency { case "月度": //向上和向下,各找一个月 nextDateDay := preDate preDateDay := preDate for i := 0; i <= 35; i++ { nextDateDayStr := nextDateDay.Format(utils.FormatDate) if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到 val = TbzDiv(preValue, tbValue) calculateStatus = true break } else { preDateDayStr := preDateDay.Format(utils.FormatDate) if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到 val = TbzDiv(preValue, tbValue) calculateStatus = true break } } nextDateDay = nextDateDay.AddDate(0, 0, 1) preDateDay = preDateDay.AddDate(0, 0, -1) } case "季度", "年度": if preValue, ok := existMap[preDateStr]; ok { //上一年同期->下一个月找到 val = TbzDiv(preValue, tbValue) calculateStatus = true break } default: nextDateDay := preDate preDateDay := preDate for i := 0; i < 35; i++ { nextDateDayStr := nextDateDay.Format(utils.FormatDate) if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到 val = TbzDiv(preValue, tbValue) calculateStatus = true break } else { preDateDayStr := preDateDay.Format(utils.FormatDate) if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到 val = TbzDiv(preValue, tbValue) calculateStatus = true break } else { //fmt.Println("pre not find:", preDateStr, "i:", i) } } nextDateDay = nextDateDay.AddDate(0, 0, 1) preDateDay = preDateDay.AddDate(0, 0, -1) } } } if calculateStatus { tmpData.Value = val newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData) allDataList = append(allDataList, tmpData) existMap[tmpData.DataTime] = val // 最大最小值 if val < minValue { minValue = val } if val > maxValue { maxValue = val } } } return } // TbzDiv 同比值计算 // @params a float64 去年同期值 // @params b float64 固定同比增速 func TbzDiv(a, b float64) (result float64) { if b != 0 { // 去年同期值 af := decimal.NewFromFloat(a) // 同比增速 bf := decimal.NewFromFloat(b) // 默认1 cf := decimal.NewFromFloat(1) // 总增速 val := bf.Add(cf) // 计算 result, _ = val.Mul(af).RoundCeil(4).Float64() } else { result = 0 } return } // GetChartPredictEdbInfoDataListByRuleTc 根据同差值规则获取预测数据 // 2.2 同差: 在未来某一个时间段内,给定一个固定的同比增加值a,用去年同期值X加上同比增加值A,得到预测值Y=X+a // 例: 今年1-3月值,100,100,120。给定同比增加值a=10,则明年1-3月预测值为: 100+10=110,100+10=110,120+10=130 func GetChartPredictEdbInfoDataListByRuleTc(edbInfoId int, tcValue float64, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*models.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*models.EdbDataList, minValue, maxValue float64) { allDataList := make([]*models.EdbDataList, 0) allDataList = append(allDataList, realPredictEdbInfoData...) allDataList = append(allDataList, predictEdbInfoData...) newPredictEdbInfoData = predictEdbInfoData index := len(allDataList) //获取后面的预测数据 dayList := getPredictEdbDayList(startDate, endDate, frequency) predictEdbInfoData = make([]*models.EdbDataList, 0) for k, currentDate := range dayList { tmpData := &models.EdbDataList{ EdbDataId: edbInfoId + 10000000000 + index + k, EdbInfoId: edbInfoId, DataTime: currentDate.Format(utils.FormatDate), //Value: dataValue, DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适 } var val float64 var calculateStatus bool //计算结果 //currentItem := existMap[av] //上一年的日期 preDate := currentDate.AddDate(-1, 0, 0) preDateStr := preDate.Format(utils.FormatDate) if preValue, ok := existMap[preDateStr]; ok { //上一年同期找到 val = TczDiv(preValue, tcValue) calculateStatus = true } else { switch frequency { case "月度": //向上和向下,各找一个月 nextDateDay := preDate preDateDay := preDate for i := 0; i <= 35; i++ { nextDateDayStr := nextDateDay.Format(utils.FormatDate) if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到 val = TczDiv(preValue, tcValue) calculateStatus = true break } else { preDateDayStr := preDateDay.Format(utils.FormatDate) if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到 val = TczDiv(preValue, tcValue) calculateStatus = true break } } nextDateDay = nextDateDay.AddDate(0, 0, 1) preDateDay = preDateDay.AddDate(0, 0, -1) } case "季度", "年度": if preValue, ok := existMap[preDateStr]; ok { //上一年同期->下一个月找到 val = TczDiv(preValue, tcValue) calculateStatus = true break } default: nextDateDay := preDate preDateDay := preDate for i := 0; i < 35; i++ { nextDateDayStr := nextDateDay.Format(utils.FormatDate) if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到 val = TczDiv(preValue, tcValue) calculateStatus = true break } else { preDateDayStr := preDateDay.Format(utils.FormatDate) if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到 val = TczDiv(preValue, tcValue) calculateStatus = true break } else { //fmt.Println("pre not find:", preDateStr, "i:", i) } } nextDateDay = nextDateDay.AddDate(0, 0, 1) preDateDay = preDateDay.AddDate(0, 0, -1) } } } if calculateStatus { tmpData.Value = val newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData) allDataList = append(allDataList, tmpData) existMap[tmpData.DataTime] = val // 最大最小值 if val < minValue { minValue = val } if val > maxValue { maxValue = val } } } return } // TczDiv 环差值计算 // @params a float64 上一期值 // @params b float64 固定的环比增加值 func TczDiv(a, b float64) (result float64) { if b != 0 { // 上一期值 af := decimal.NewFromFloat(a) // 固定的环比增加值 bf := decimal.NewFromFloat(b) // 计算 result, _ = af.Add(bf).RoundCeil(4).Float64() } else { result = 0 } return } // GetChartPredictEdbInfoDataListByRuleHb 根据环比值规则获取预测数据 // 环比:在未来某一个时间段内,给定一个固定的环比增速a,用上一期值X乘以环比增速(1+a),得到预测值Y=X(1+a) // 例: 最近1期值为100,给定环比增速a=0.2,则未来3期预测值为: 100*1.2=120,120*1.2=144,144*1.2=172.8 func GetChartPredictEdbInfoDataListByRuleHb(edbInfoId int, hbValue float64, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*models.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*models.EdbDataList, minValue, maxValue float64) { allDataList := make([]*models.EdbDataList, 0) allDataList = append(allDataList, realPredictEdbInfoData...) allDataList = append(allDataList, predictEdbInfoData...) newPredictEdbInfoData = predictEdbInfoData index := len(allDataList) //获取后面的预测数据 dayList := getPredictEdbDayList(startDate, endDate, frequency) for k, currentDate := range dayList { tmpK := index + k - 1 //上1期的值 // 环比值计算 val := HbzDiv(allDataList[tmpK].Value, hbValue) currentDateStr := currentDate.Format(utils.FormatDate) tmpData := &models.EdbDataList{ EdbDataId: edbInfoId + 10000000000 + index + k, EdbInfoId: edbInfoId, DataTime: currentDateStr, Value: val, DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适 } newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData) allDataList = append(allDataList, tmpData) existMap[currentDateStr] = val // 最大最小值 if val < minValue { minValue = val } if val > maxValue { maxValue = val } } return } // HbzDiv 环比值计算 // @params a float64 上一期值 // @params b float64 固定的环比增速 func HbzDiv(a, b float64) (result float64) { if b != 0 { // 上一期值 af := decimal.NewFromFloat(a) // 固定的环比增速 bf := decimal.NewFromFloat(b) // 默认1 cf := decimal.NewFromFloat(1) // 总增速 val := bf.Add(cf) // 计算 result, _ = val.Mul(af).RoundCeil(4).Float64() } else { result = 0 } return } // GetChartPredictEdbInfoDataListByRuleHc 根据环差值规则获取预测数据 // 2.4 环差:在未来某一个时间段内,给定一个固定的环比增加值a,用上一期值X加上环比增加值a,得到预测值Y=X+a // 例: 最近1期值为100,给定环比增加值a=10,则未来3期预测值为: 100+10=110,110+10=120,120+10=130 func GetChartPredictEdbInfoDataListByRuleHc(edbInfoId int, hcValue float64, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*models.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*models.EdbDataList, minValue, maxValue float64) { allDataList := make([]*models.EdbDataList, 0) allDataList = append(allDataList, realPredictEdbInfoData...) allDataList = append(allDataList, predictEdbInfoData...) newPredictEdbInfoData = predictEdbInfoData index := len(allDataList) //获取后面的预测数据 dayList := getPredictEdbDayList(startDate, endDate, frequency) for k, currentDate := range dayList { tmpK := index + k - 1 //上1期的值 // 环差别值计算 val := HczDiv(allDataList[tmpK].Value, hcValue) currentDateStr := currentDate.Format(utils.FormatDate) tmpData := &models.EdbDataList{ EdbDataId: edbInfoId + 10000000000 + index + k, EdbInfoId: edbInfoId, DataTime: currentDateStr, Value: val, DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适 } newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData) allDataList = append(allDataList, tmpData) existMap[currentDateStr] = val // 最大最小值 if val < minValue { minValue = val } if val > maxValue { maxValue = val } } return } // HczDiv 环差值计算 // @params a float64 上一期值 // @params b float64 固定的环比增加值 func HczDiv(a, b float64) (result float64) { if b != 0 { // 上一期值 af := decimal.NewFromFloat(a) // 固定的环比增加值 bf := decimal.NewFromFloat(b) // 计算 result, _ = af.Add(bf).RoundCeil(4).Float64() } else { result = 0 } return } // GetChartPredictEdbInfoDataListByRuleNMoveMeanValue 根据N期移动均值规则获取预测数据 // 2.5 N期移动均值:在未来某一个时间段内,下一期值等于过去N期值得平均值。 // 例:最近3期值(N=3),为95,98,105则未来第1期值为 1/3*(95+98+105)=99.33, 未来第2期值为 1/3*(98+105+99.33)=100.78依次类推。 func GetChartPredictEdbInfoDataListByRuleNMoveMeanValue(edbInfoId int, nValue int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*models.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*models.EdbDataList, minValue, maxValue float64) { allDataList := make([]*models.EdbDataList, 0) allDataList = append(allDataList, realPredictEdbInfoData...) allDataList = append(allDataList, predictEdbInfoData...) newPredictEdbInfoData = predictEdbInfoData lenAllData := len(allDataList) if lenAllData < nValue || lenAllData <= 0 { return } if nValue <= 0 { return } // 分母 decimalN := decimal.NewFromInt(int64(nValue)) //获取后面的预测数据 dayList := getPredictEdbDayList(startDate, endDate, frequency) for k, currentDate := range dayList { tmpIndex := lenAllData + k - 1 //上1期的值 // 数据集合中的最后一个数据 tmpDecimalVal := decimal.NewFromFloat(allDataList[tmpIndex].Value) for tmpK := 2; tmpK <= nValue; tmpK++ { tmpIndex2 := tmpIndex - tmpK //上N期的值 tmpDecimalVal2 := decimal.NewFromFloat(allDataList[tmpIndex2].Value) tmpDecimalVal = tmpDecimalVal.Add(tmpDecimalVal2) } // N期移动均值计算 val, _ := tmpDecimalVal.Div(decimalN).RoundCeil(4).Float64() currentDateStr := currentDate.Format(utils.FormatDate) tmpData := &models.EdbDataList{ EdbDataId: edbInfoId + 10000000000 + lenAllData + k, EdbInfoId: edbInfoId, DataTime: currentDateStr, Value: val, DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适 } newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData) allDataList = append(allDataList, tmpData) existMap[currentDateStr] = val // 最大最小值 if val < minValue { minValue = val } if val > maxValue { maxValue = val } } return } // GetChartPredictEdbInfoDataListByRuleNLinearRegression 根据N期移动均值规则获取预测数据 // 2.6N期段线性外推值:给出过去N期值所确定的线性回归方程(Y=aX+b)在未来一段时间内的推算值。回归方程虽然比较复杂,但各种编程语言应该都有现成的模块或函数,应该无需自己编写。 // 例1:过去5期值(N=5)分别为:3,5,7,9,11(每两期值之间的时间间隔相等)。那么按照线性回归方程推算,未来三期的预测值是:13,15,17。 // // 例2:过去6期值(N=6)分别为:3,3,5,7,9,11(每两期值之间的时间间隔相等)。那么按照线性回归方程推算,未来三期的预测值是:12.33,14.05,15.76。例1和例2的区别在于,多加了一期数据,导致回归方程发生改变,从而预测值不同。 func GetChartPredictEdbInfoDataListByRuleNLinearRegression(edbInfoId int, nValue int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*models.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*models.EdbDataList, minValue, maxValue float64) { //var errMsg string //defer func() { // if errMsg != `` { // go alarm_msg.SendAlarmMsg("更新上海的token失败;ERR:"+err.Error(), 3) // } //}() allDataList := make([]*models.EdbDataList, 0) allDataList = append(allDataList, realPredictEdbInfoData...) allDataList = append(allDataList, predictEdbInfoData...) newPredictEdbInfoData = predictEdbInfoData lenAllData := len(allDataList) if lenAllData < nValue || lenAllData <= 0 { return } if nValue <= 1 { return } //获取后面的预测数据 // 获取线性方程公式的a、b的值 coordinateData := make([]Coordinate, 0) for tmpK := nValue; tmpK > 0; tmpK-- { tmpIndex2 := lenAllData - tmpK //上N期的值 tmpCoordinate := Coordinate{ X: float64(nValue - tmpK + 1), Y: allDataList[tmpIndex2].Value, } coordinateData = append(coordinateData, tmpCoordinate) } a, b := getLinearResult(coordinateData) //fmt.Println("a:", a, ";======b:", b) dayList := getPredictEdbDayList(startDate, endDate, frequency) for k, currentDate := range dayList { tmpK := nValue + k + 1 aDecimal := decimal.NewFromFloat(a) xDecimal := decimal.NewFromInt(int64(tmpK)) bDecimal := decimal.NewFromFloat(b) val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).RoundCeil(4).Float64() currentDateStr := currentDate.Format(utils.FormatDate) tmpData := &models.EdbDataList{ EdbDataId: edbInfoId + 10000000000 + lenAllData + k, EdbInfoId: edbInfoId, DataTime: currentDateStr, Value: val, DataTimestamp: (currentDate.UnixNano() / 1e6) + 1000, //前端需要让加1s,说是2022-09-01 00:00:00 这样的整点不合适 } newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData) allDataList = append(allDataList, tmpData) existMap[currentDateStr] = val // 最大最小值 if val < minValue { minValue = val } if val > maxValue { maxValue = val } } return } // Series is a container for a series of data type Series []Coordinate // Coordinate holds the data in a series type Coordinate struct { X, Y float64 } func getLinearResult(s []Coordinate) (gradient, intercept float64) { if len(s) <= 1 { return } // Placeholder for the math to be done var sum [5]float64 // Loop over data keeping index in place i := 0 for ; i < len(s); i++ { sum[0] += s[i].X sum[1] += s[i].Y sum[2] += s[i].X * s[i].X sum[3] += s[i].X * s[i].Y sum[4] += s[i].Y * s[i].Y } // Find gradient and intercept f := float64(i) gradient = (f*sum[3] - sum[0]*sum[1]) / (f*sum[2] - sum[0]*sum[0]) intercept = (sum[1] / f) - (gradient * sum[0] / f) //fmt.Println("gradient:", gradient, ";intercept:", intercept) // Create the new regression series //for j := 0; j < len(s); j++ { // regressions = append(regressions, Coordinate{ // X: s[j].X, // Y: s[j].X*gradient + intercept, // }) //} return }