|
@@ -1525,21 +1525,24 @@ func GetChartPredictEdbInfoDataListByRuleNAnnualAverage(edbInfoId int, configVal
|
|
|
|
|
|
// AnnualValueInversionConf 年度值倒推规则
|
|
|
type AnnualValueInversionConf struct {
|
|
|
- Value float64 `description:"年度值"`
|
|
|
- Type int `description:"分配方式,1:均值法;2:同比法"`
|
|
|
- Year int `description:"同比年份"`
|
|
|
+ Value float64 `description:"年度值"`
|
|
|
+ Type int `description:"分配方式,1:均值法;2:同比法"`
|
|
|
+ Year int `description:"同比年份"`
|
|
|
+ YearList []int `description:"指定年份列表"`
|
|
|
}
|
|
|
|
|
|
// GetChartPredictEdbInfoDataListByRuleAnnualValueInversion 根据 年度值倒推 规则获取预测数据
|
|
|
-// ETA预测规则:年度值倒推:设定年度值,余额=年度值-年初至今累计值(算法参考累计值),进行余额分配,均值法分配时保证每期数值相等(日度/周度:剩余期数=剩余自然日历天数/今年指标最新日期自然日历天数*今年至今指标数据期数;旬度/月度/季度/半年度:剩余期数=全年期数(36\12\4\2)-今年至今自然日历期数),同比法保证每期同比相等(同比增速=余额/同比年份相应日期的余额,预测值等于同比年份同期值*同比增速)
|
|
|
-// 举例:
|
|
|
-// 指标A 日度 最新日期 2023-05-19 年初至今累计值100
|
|
|
-// 设置年度值1000
|
|
|
-// 则余额=1000-100=900
|
|
|
-// 均值法分配:剩余期数=226/139*120=195.11
|
|
|
-// 今年之后的每一期预测值=900/195.11=4.6128
|
|
|
-// 同比法分配:同比增速=900/同比年份5.19的余额
|
|
|
-// 预测值=同比年份5-20的值*(1+同比增速)
|
|
|
+// 预测指标-年度值倒推
|
|
|
+// 1、年度值倒推,选择同比法,支持选择多个年份(当前只可选择一个年份)。选择多个年份时,计算多个年份的余额平均,和同期平均。
|
|
|
+// 2、年度值倒推,同比法的算法优化:旬度,月度,季度,半年度的算法,同原先算法。
|
|
|
+// 日度、周度值算法更新(假设指标实际值最新日期月2024/3/1):
|
|
|
+// 1、设定年度值
|
|
|
+// 2、计算余额:年度值-年初至今累计值
|
|
|
+// 3、年初至今累计值计算方法:用后置填充变频成连续自然日日度数据。计算1/1至指标最新日期(2024/3/3/1)的累计值。
|
|
|
+// 4、计算同比年份全年累计值,年初至指标最新值同期(2023/3/1)累计值,两者相减得到同比年份同期余额,再取平均值,作为最终的同期余额
|
|
|
+// 5、用今年余额/去年同期余额得到同比增速。
|
|
|
+// 6、每一期预测值,为同比年份的同期值,乘以(1+同比)。去年同期,用变频后的序列对应。
|
|
|
+// 7、如果选择的同比年份是多个。则计算多个年份的平均余额。今年余额/平均余额=同比增速。同比基数为多个年份的同期平均值
|
|
|
func GetChartPredictEdbInfoDataListByRuleAnnualValueInversion(edbInfoId int, configValue string, dayList []time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*data_manage.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*data_manage.EdbDataList, minValue, maxValue float64, err error) {
|
|
|
if frequency == "年度" {
|
|
|
err = errors.New("当前指标频度是年度,不允许配置年度值倒推")
|
|
@@ -1560,6 +1563,11 @@ func GetChartPredictEdbInfoDataListByRuleAnnualValueInversion(edbInfoId int, con
|
|
|
newPredictEdbInfoData = predictEdbInfoData
|
|
|
index := len(allDataList)
|
|
|
|
|
|
+ // 没有数据,直接返回
|
|
|
+ if index <= 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
// 配置的年度值
|
|
|
yearValueConfig := annualValueInversionConf.Value
|
|
|
|
|
@@ -1708,112 +1716,166 @@ func GetChartPredictEdbInfoDataListByRuleAnnualValueInversion(edbInfoId int, con
|
|
|
// 同比法分配
|
|
|
// 同比法保证每期同比相等(同比增速=余额/同比年份相应日期的余额,预测值等于同比年份同期值*同比增速);
|
|
|
// 同比法分配:同比增速=900/同比年份5.19的余额
|
|
|
-
|
|
|
+ yearList := annualValueInversionConf.YearList
|
|
|
+ if len(yearList) == 0 {
|
|
|
+ //兼容历史数据
|
|
|
+ yearList = append(yearList, annualValueInversionConf.Year)
|
|
|
+ }
|
|
|
// 每年截止到当前日期的累计值
|
|
|
dateTotalMap := make(map[time.Time]float64)
|
|
|
+
|
|
|
+ //把每一期的期数和日期绑定
|
|
|
+ dateIndexMap := make(map[time.Time]int)
|
|
|
+ indexDateMap := make(map[int]time.Time)
|
|
|
// 每年的累计值(计算使用)
|
|
|
yearTotalMap := make(map[int]float64)
|
|
|
- for _, v := range allDataList {
|
|
|
- currTime, tmpErr := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
|
|
|
- if tmpErr != nil {
|
|
|
- err = tmpErr
|
|
|
- return
|
|
|
+ //数据按找后值填充的方式处理成连续自然日日度数据
|
|
|
+ allDataListMap := make(map[string]float64)
|
|
|
+ // todo 如果是日度和周度,用后置填充变频成连续自然日日度数据。计算1/1至指标最新日期(2024/3/3/1)的累计值
|
|
|
+ switch frequency {
|
|
|
+ case "日度", "周度":
|
|
|
+ for _, v := range allDataList {
|
|
|
+ allDataListMap[v.DataTime] = v.Value
|
|
|
+ }
|
|
|
+ //找到最早日期的的年份的1月1日,转成time格式
|
|
|
+ earliestYear := allDataList[0].DataTime[:4]
|
|
|
+ earliestYearFirstDay, _ := time.ParseInLocation(utils.FormatDate, earliestYear+"-01-01", time.Local)
|
|
|
+ days := int(currDayTime.Sub(earliestYearFirstDay).Hours() / float64(24))
|
|
|
+ //循环累加日期,直到循环到最新日期
|
|
|
+ for i := 0; i <= days; i++ {
|
|
|
+ currentDate := earliestYearFirstDay.AddDate(0, 0, i)
|
|
|
+ currentDateStr := currentDate.Format(utils.FormatDate)
|
|
|
+ val, ok := allDataListMap[currentDateStr]
|
|
|
+ if !ok { //如果不存在,则填充后值
|
|
|
+ //循环向后查找数据,直到找到
|
|
|
+ for j := i + 1; j <= days; j++ {
|
|
|
+ //循环往后取值
|
|
|
+ currentDateTmp := earliestYearFirstDay.AddDate(0, 0, j)
|
|
|
+ currentDateTmpStr := currentDateTmp.Format(utils.FormatDate)
|
|
|
+ if tmpVal, ok1 := allDataListMap[currentDateTmpStr]; ok1 {
|
|
|
+ allDataListMap[currentDateStr] = tmpVal
|
|
|
+ val = tmpVal
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //计算每一天的年初至今累计值
|
|
|
+ yearVal := yearTotalMap[currentDate.Year()]
|
|
|
+ if frequency == "周度" {
|
|
|
+ // 每日累计值需要当前值除7
|
|
|
+ yearVal = yearVal + val/7
|
|
|
+ } else {
|
|
|
+ yearVal = yearVal + val
|
|
|
+ }
|
|
|
+ yearTotalMap[currentDate.Year()] = yearVal
|
|
|
+ dateTotalMap[currentDate] = yearVal
|
|
|
+ dateIndexMap[currentDate] = i
|
|
|
+ indexDateMap[i] = currentDate
|
|
|
}
|
|
|
- yearVal := yearTotalMap[currTime.Year()]
|
|
|
- yearVal = yearVal + v.Value
|
|
|
- yearTotalMap[currTime.Year()] = yearVal
|
|
|
- dateTotalMap[currTime] = yearVal
|
|
|
- }
|
|
|
-
|
|
|
- //(同比增速=余额/同比年份相应日期的余额,预测值等于同比年份同期值*同比增速);
|
|
|
- for k, currentDate := range dayList {
|
|
|
- currYearBalance := yearValueConfig - yearTotalMap[currentDate.Year()] // 当年的余额
|
|
|
-
|
|
|
- // 上一期的日期
|
|
|
- prevDateStr := allDataList[len(allDataList)-1].DataTime
|
|
|
- prevDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, prevDateStr, time.Local)
|
|
|
- if tmpErr != nil {
|
|
|
- err = tmpErr
|
|
|
- return
|
|
|
+ default:
|
|
|
+ for k, v := range allDataList {
|
|
|
+ currTime, tmpErr := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
|
|
|
+ if tmpErr != nil {
|
|
|
+ err = tmpErr
|
|
|
+ return
|
|
|
+ }
|
|
|
+ allDataListMap[v.DataTime] = v.Value
|
|
|
+ yearVal := yearTotalMap[currTime.Year()]
|
|
|
+ yearVal = yearVal + v.Value
|
|
|
+ yearTotalMap[currTime.Year()] = yearVal
|
|
|
+ dateTotalMap[currTime] = yearVal
|
|
|
+ dateIndexMap[currTime] = k
|
|
|
+ indexDateMap[k] = currTime
|
|
|
}
|
|
|
-
|
|
|
- //同比年份相应日期
|
|
|
- lastYear := annualValueInversionConf.Year + (currentDate.Year() - currDayTime.Year())
|
|
|
-
|
|
|
- // 前N年的上一期时间;前N年的当期时间;
|
|
|
- var lastPrevDateTime, lastDateTime time.Time
|
|
|
-
|
|
|
- switch frequency {
|
|
|
- case "半年度", "季度":
|
|
|
- lastDateTime = time.Date(lastYear, currentDate.Month(), currentDate.Day(), 0, 0, 0, 0, currentDate.Location())
|
|
|
- lastPrevDateTime = time.Date(lastYear, prevDateTime.Month(), prevDateTime.Day(), 0, 0, 0, 0, prevDateTime.Location())
|
|
|
- case "月度":
|
|
|
- lastDateTime = time.Date(lastYear, currentDate.Month()+1, 1, 0, 0, 0, 0, currentDate.Location()).AddDate(0, 0, -1)
|
|
|
- lastPrevDateTime = time.Date(lastYear, prevDateTime.Month()+1, 1, 0, 0, 0, 0, prevDateTime.Location()).AddDate(0, 0, -1)
|
|
|
- case "旬度":
|
|
|
- if prevDateTime.Day() == 10 || prevDateTime.Day() == 20 {
|
|
|
- lastDateTime = time.Date(lastYear, currentDate.Month(), currentDate.Day(), 0, 0, 0, 0, currentDate.Location())
|
|
|
- lastPrevDateTime = time.Date(lastYear, prevDateTime.Month(), prevDateTime.Day(), 0, 0, 0, 0, prevDateTime.Location())
|
|
|
- } else {
|
|
|
- lastDateTime = time.Date(lastYear, currentDate.Month()+1, 1, 0, 0, 0, 0, currentDate.Location()).AddDate(0, 0, -1)
|
|
|
- lastPrevDateTime = time.Date(lastYear, prevDateTime.Month()+1, 1, 0, 0, 0, 0, prevDateTime.Location()).AddDate(0, 0, -1)
|
|
|
+ }
|
|
|
+ // 当年的余额
|
|
|
+ currYearBalance := yearValueConfig - yearTotalMap[currDayTime.Year()]
|
|
|
+ //fmt.Printf("当年的余额%.4f=给定额度%.4f-当年累计值%.4f\n", currYearBalance, yearValueConfig, yearTotalMap[currDayTime.Year()])
|
|
|
+ // 循环统计同比年份同期余额
|
|
|
+ var sum, avg float64
|
|
|
+ for _, year := range yearList {
|
|
|
+ yearTotal := yearTotalMap[year]
|
|
|
+ //fmt.Printf("同比年份的累计值%.4f\n", yearTotal)
|
|
|
+ tmpDate := time.Date(year, currDayTime.Month(), currDayTime.Day(), 0, 0, 0, 0, currDayTime.Location())
|
|
|
+ //fmt.Printf("同比年份的同期%s\n", tmpDate)
|
|
|
+ dateTotal, ok := dateTotalMap[tmpDate]
|
|
|
+ //fmt.Printf("同比年份的同期累计值%.4f\n", dateTotal)
|
|
|
+ if ok {
|
|
|
+ sum = sum + (yearTotal - dateTotal)
|
|
|
+ } else {
|
|
|
+ // 查找下一期的余额
|
|
|
+ tmpIndex, ok1 := dateIndexMap[tmpDate]
|
|
|
+ if ok1 {
|
|
|
+ for tmpDateTime := indexDateMap[tmpIndex+1]; tmpDateTime.Year() == year; tmpDateTime = indexDateMap[tmpIndex+1] {
|
|
|
+ dateTotal, ok = dateTotalMap[tmpDateTime]
|
|
|
+ if ok {
|
|
|
+ //fmt.Printf("同比年份的同期累计值%.4f\n", dateTotal)
|
|
|
+ sum = sum + (yearTotal - dateTotal)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ tmpIndex += 1
|
|
|
+ }
|
|
|
}
|
|
|
- case "周度", "日度":
|
|
|
- lastDateTime = time.Date(lastYear, currentDate.Month(), currentDate.Day(), 0, 0, 0, 0, currentDate.Location())
|
|
|
- lastPrevDateTime = time.Date(lastYear, prevDateTime.Month(), prevDateTime.Day(), 0, 0, 0, 0, prevDateTime.Location())
|
|
|
}
|
|
|
-
|
|
|
- // 同比年份相应日期的累计值
|
|
|
- var dateTotal float64
|
|
|
-
|
|
|
- dateTotal, ok := dateTotalMap[lastPrevDateTime]
|
|
|
- if !ok { //如果没有找到这个日期,那么就往前面找,一直到找到这个累计值,或者找完这一年
|
|
|
- yearFirstDayTime := time.Date(lastPrevDateTime.Year(), 1, 1, 0, 0, 0, 0, lastDateTime.Location())
|
|
|
- for tmpDateTime := lastPrevDateTime.AddDate(0, 0, -1); tmpDateTime.After(yearFirstDayTime) || tmpDateTime.Equal(yearFirstDayTime); tmpDateTime = tmpDateTime.AddDate(0, 0, -1) {
|
|
|
- dateTotal, ok = dateTotalMap[tmpDateTime]
|
|
|
- if ok {
|
|
|
- break
|
|
|
+ }
|
|
|
+ //fmt.Printf("同比年份的余额%.4f\n", sum)
|
|
|
+ avg = sum / float64(len(yearList))
|
|
|
+ //fmt.Printf("同比年份的余额%.4f\n", avg)
|
|
|
+ // 同比增速=当年余额/同比年份上一期日期的余额
|
|
|
+ tbVal := decimal.NewFromFloat(currYearBalance).Div(decimal.NewFromFloat(avg))
|
|
|
+ /*tbVal11, _ := tbVal.Round(4).Float64()
|
|
|
+ fmt.Printf("同比增速%.4f\n", tbVal11)*/
|
|
|
+ //(同比增速=余额/同比年份相应日期的余额的平均值,预测值等于同比年份同期值*同比增速);
|
|
|
+ for k, currentDate := range dayList {
|
|
|
+ // 循环遍历多个同比年份
|
|
|
+ var valSum float64
|
|
|
+ for _, year := range yearList {
|
|
|
+ //多个同比年份的同期值的平均值
|
|
|
+ tmpCurrentDate := time.Date(year, currentDate.Month(), currentDate.Day(), 0, 0, 0, 0, currentDate.Location())
|
|
|
+ if tmpVal, ok := allDataListMap[tmpCurrentDate.Format(utils.FormatDate)]; ok {
|
|
|
+ valSum += tmpVal
|
|
|
+ } else {
|
|
|
+ // 查找下一期的余额
|
|
|
+ tmpIndex, ok1 := dateIndexMap[tmpCurrentDate]
|
|
|
+ if ok1 {
|
|
|
+ for tmpDateTime := indexDateMap[tmpIndex+1]; tmpDateTime.Year() == year; tmpDateTime = indexDateMap[tmpIndex+1] {
|
|
|
+ tmpVal, ok = allDataListMap[tmpDateTime.Format(utils.FormatDate)]
|
|
|
+ if ok {
|
|
|
+ valSum += tmpVal
|
|
|
+ break
|
|
|
+ }
|
|
|
+ tmpIndex += 1
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ lastDateVal := valSum / float64(len(yearList))
|
|
|
|
|
|
- //同比年份相应的上一期日期的余额
|
|
|
- lastYearDateBalance := yearTotalMap[lastPrevDateTime.Year()] - dateTotal
|
|
|
- if lastYearDateBalance == 0 {
|
|
|
- continue
|
|
|
+ //预测值 = 同比年份同期值*同比增速
|
|
|
+ tmpVal, _ := decimal.NewFromFloat(lastDateVal).Mul(tbVal).Round(4).Float64()
|
|
|
+ currentDateStr := currentDate.Format(utils.FormatDate)
|
|
|
+ tmpData := &data_manage.EdbDataList{
|
|
|
+ EdbDataId: edbInfoId + 100000 + index + k,
|
|
|
+ EdbInfoId: edbInfoId,
|
|
|
+ DataTime: currentDateStr,
|
|
|
+ Value: tmpVal,
|
|
|
+ DataTimestamp: currentDate.UnixNano() / 1e6,
|
|
|
}
|
|
|
+ newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
|
|
|
+ allDataList = append(allDataList, tmpData)
|
|
|
+ existMap[currentDateStr] = tmpVal
|
|
|
|
|
|
- // 同比增速=当年余额/同比年份上一期日期的余额
|
|
|
- tbVal := decimal.NewFromFloat(currYearBalance).Div(decimal.NewFromFloat(lastYearDateBalance))
|
|
|
-
|
|
|
- // 获取同比年份同期值,获取失败的话,就不处理
|
|
|
- if lastDateVal, ok := existMap[lastDateTime.Format(utils.FormatDate)]; ok {
|
|
|
- //预测值 = 同比年份同期值*同比增速
|
|
|
- tmpVal, _ := decimal.NewFromFloat(lastDateVal).Mul(tbVal).Round(4).Float64()
|
|
|
- currentDateStr := currentDate.Format(utils.FormatDate)
|
|
|
- tmpData := &data_manage.EdbDataList{
|
|
|
- EdbDataId: edbInfoId + 100000 + index + k,
|
|
|
- EdbInfoId: edbInfoId,
|
|
|
- DataTime: currentDateStr,
|
|
|
- Value: tmpVal,
|
|
|
- DataTimestamp: currentDate.UnixNano() / 1e6,
|
|
|
- }
|
|
|
- newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
|
|
|
- allDataList = append(allDataList, tmpData)
|
|
|
- existMap[currentDateStr] = tmpVal
|
|
|
-
|
|
|
- yearVal := yearTotalMap[currentDate.Year()]
|
|
|
- yearVal = yearVal + tmpVal
|
|
|
- yearTotalMap[currentDate.Year()] = yearVal
|
|
|
- dateTotalMap[currentDate] = yearVal
|
|
|
+ yearVal := yearTotalMap[currentDate.Year()]
|
|
|
+ yearVal = yearVal + tmpVal
|
|
|
+ yearTotalMap[currentDate.Year()] = yearVal
|
|
|
+ dateTotalMap[currentDate] = yearVal
|
|
|
|
|
|
- // 最大最小值
|
|
|
- if tmpVal < minValue {
|
|
|
- minValue = tmpVal
|
|
|
- }
|
|
|
- if tmpVal > maxValue {
|
|
|
- maxValue = tmpVal
|
|
|
- }
|
|
|
+ // 最大最小值
|
|
|
+ if tmpVal < minValue {
|
|
|
+ minValue = tmpVal
|
|
|
+ }
|
|
|
+ if tmpVal > maxValue {
|
|
|
+ maxValue = tmpVal
|
|
|
}
|
|
|
}
|
|
|
|