package models import ( "errors" "eta/eta_index_lib/utils" "fmt" "github.com/shopspring/decimal" "sort" "strconv" "strings" "time" ) // EdbInfoData // @Description: 指标数据 type EdbInfoData struct { EdbDataId int `description:"数据ID"` DataTime time.Time `description:"数据日期"` Value float64 `description:"数据"` } // BaseCalculateBySearchData // @Description: 基础的计算公式 type BaseCalculateBySearchData struct { DataList []*EdbInfoSearchData Frequency string `description:"需要转换的频度"` Formula interface{} Calendar string `description:"公历/农历"` MoveType int `description:"移动方式:1:领先(默认),2:滞后"` MoveFrequency string `description:"移动频度"` FromFrequency string `description:"来源的频度"` Source int `description:"1:累计值转月;2:累计值转季;3:同比值;4:同差值;5:N数值移动平均数计算;6:环比值;7:环差值;8:升频;9:降频;10:时间移位;11:超季节性;12:年化;13:累计值;14:累计值年初至今;15:指数修匀;16:日均值"` } // BaseCalculate // @Description: 基础的计算公式 type BaseCalculate struct { DataList []*EdbInfoData Frequency string `description:"需要转换的频度"` Formula interface{} Calendar string `description:"公历/农历"` MoveType int `description:"移动方式:1:领先(默认),2:滞后"` MoveFrequency string `description:"移动频度"` FromFrequency string `description:"来源的频度"` Source int `description:"1:累计值转月;2:累计值转季;3:同比值;4:同差值;5:N数值移动平均数计算;6:环比值;7:环差值;8:升频;9:降频;10:时间移位;11:超季节性;12:年化;13:累计值;14:累计值年初至今;15:指数修匀;16:日均值"` } type BaseCalculateResp struct { DataMap map[string]float64 DateList []string } // Ljzzy // @Description: 累计值转月 // @author: Roc // @receiver obj // @datetime2023-11-02 18:05:19 // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) Ljzzy() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) dataList := obj.DataList // 升序 // 数据处理 yearMap := make(map[int]map[int]*EdbInfoData) dataLen := len(dataList) for i := 0; i < dataLen; i++ { item := dataList[i] //日其中获取年 itemDate := item.DataTime year := itemDate.Year() month := int(itemDate.Month()) if monthMap, yok := yearMap[year]; yok { monthMap[month] = item yearMap[year] = monthMap } else { monthMap = make(map[int]*EdbInfoData) monthMap[month] = item yearMap[year] = monthMap } } // 开始计算 for yk, yv := range yearMap { _, oneMonthOk := yv[1] _, twoMonthOk := yv[2] if !oneMonthOk && !twoMonthOk { continue } for i := 1; i <= 12; i++ { //fmt.Println(yk, i, yv[i]) dataCurrentItem := yv[i] if dataCurrentItem == nil { continue } var date time.Time var val float64 // 一二月单独处理 if i == 1 || i == 2 { if _, mok := yv[1]; mok { //1月有值 if i == 1 { date = dataCurrentItem.DataTime val, _ = decimal.NewFromFloat(dataCurrentItem.Value).Float64() //a.Div(b).Float64() } if i == 2 { dataOneItem := yv[1] date = dataCurrentItem.DataTime twoMonth := decimal.NewFromFloat(dataCurrentItem.Value) oneMonth := decimal.NewFromFloat(dataOneItem.Value) val, _ = twoMonth.Sub(oneMonth).Float64() } } else { //1月无值 dataTwoItem := yv[2] if i == 1 { dateStr := strconv.Itoa(yk) + "-01-31" a := decimal.NewFromFloat(dataTwoItem.Value) b := decimal.NewFromFloat(2.0) val, _ = a.Div(b).Float64() date, err = time.ParseInLocation(utils.FormatDate, dateStr, time.Local) if err != nil { return } } if i == 2 { //1月无值:1月=2月/2 (不管怎样,都要给1月赋值) { dateStr := strconv.Itoa(yk) + "-01-31" a := decimal.NewFromFloat(dataTwoItem.Value) b := decimal.NewFromFloat(2.0) val, _ = a.Div(b).Float64() date, err = time.ParseInLocation(utils.FormatDate, dateStr, time.Local) if err != nil { return } dateDataMap[date] = val } //end 1月无值 // 这是正常二月份的值 date = dataCurrentItem.DataTime a := decimal.NewFromFloat(dataTwoItem.Value) b := decimal.NewFromFloat(2.0) val, _ = a.Div(b).Float64() } } } else { dataPreItem := yv[i-1] if dataCurrentItem != nil && dataPreItem != nil { date = dataCurrentItem.DataTime //val = dataCurrentItem.Value - dataPreItem.Value a := decimal.NewFromFloat(dataCurrentItem.Value) b := decimal.NewFromFloat(dataPreItem.Value) val, _ = a.Sub(b).Float64() } } // 如果没有日期,那么就退出当前循环,进入下一个循环 if date.IsZero() { continue } dateDataMap[date] = val } } return } // Ljzzj // @Description: 累计值转季度 // @author: Roc // @receiver obj // @datetime2023-11-02 18:05:05 // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) Ljzzj() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) dataList := obj.DataList // 升序 // 数据处理 yearMap := make(map[int]map[int]*EdbInfoData) dataLen := len(dataList) for i := 0; i < dataLen; i++ { item := dataList[i] //日其中获取年 itemDate := item.DataTime year := itemDate.Year() quarter := utils.MonthQuarterMap[int(itemDate.Month())] if quarterMap, yok := yearMap[year]; yok { quarterMap[quarter] = item yearMap[year] = quarterMap } else { quarterMap = make(map[int]*EdbInfoData) quarterMap[quarter] = item yearMap[year] = quarterMap } } // 开始计算 for yk, yv := range yearMap { _, oneQuarterOk := yv[1] _, twoQuarterOk := yv[2] if !oneQuarterOk && !twoQuarterOk { continue } for i := 1; i <= 4; i++ { //fmt.Println(yk, i, yv[i]) dataCurrentItem := yv[i] if dataCurrentItem == nil { continue } var date time.Time var val float64 // 一二季度单独处理 if i == 1 || i == 2 { if _, mok := yv[1]; mok { //第1个季度有值 if i == 1 { date = dataCurrentItem.DataTime val, _ = decimal.NewFromFloat(dataCurrentItem.Value).Float64() //a.Div(b).Float64() } if i == 2 { dataOneItem := yv[1] date = dataCurrentItem.DataTime twoQuarter := decimal.NewFromFloat(dataCurrentItem.Value) oneQuarter := decimal.NewFromFloat(dataOneItem.Value) val, _ = twoQuarter.Sub(oneQuarter).Float64() } } else { //第1个季度无值 dataTwoItem := yv[2] if i == 1 { dateStr := strconv.Itoa(yk) + "-03-31" a := decimal.NewFromFloat(dataTwoItem.Value) b := decimal.NewFromFloat(2.0) val, _ = a.Div(b).Float64() date, err = time.ParseInLocation(utils.FormatDate, dateStr, time.Local) if err != nil { return } } if i == 2 { //第1个季度无值:第1个季度=第2个季度/2 (不管怎样,都要给1季度赋值) { dateStr := strconv.Itoa(yk) + "-03-31" a := decimal.NewFromFloat(dataTwoItem.Value) b := decimal.NewFromFloat(2.0) val, _ = a.Div(b).Float64() date, err = time.ParseInLocation(utils.FormatDate, dateStr, time.Local) if err != nil { return } dateDataMap[date] = val } //end 第1个季度无值 date = dataCurrentItem.DataTime a := decimal.NewFromFloat(dataTwoItem.Value) b := decimal.NewFromFloat(2.0) val, _ = a.Div(b).Float64() } } } else { dataPreItem := yv[i-1] if dataCurrentItem != nil && dataPreItem != nil { date = dataCurrentItem.DataTime //val = dataCurrentItem.Value - dataPreItem.Value a := decimal.NewFromFloat(dataCurrentItem.Value) b := decimal.NewFromFloat(dataPreItem.Value) val, _ = a.Sub(b).Float64() } } // 如果没有日期,那么就退出当前循环,进入下一个循环 if date.IsZero() { continue } dateDataMap[date] = val } } return } // Tbz // @Description: 同比值计算 // @author: Roc // @receiver obj // @datetime2023-11-02 18:04:59 // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) Tbz() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) dataList := obj.DataList frequency := obj.FromFrequency //数据处理 // 数据降序 dataList = reverseSliceByDesc(dataList) var dateArr []time.Time dataMap := make(map[string]*EdbInfoData) // 避免因为时间戳导致的日期不对,还是用string来表示比较合适 for _, v := range dataList { v.DataTime = time.Date(v.DataTime.Year(), v.DataTime.Month(), v.DataTime.Day(), 0, 0, 0, 0, v.DataTime.Location()) dateArr = append(dateArr, v.DataTime) dataMap[v.DataTime.Format(utils.FormatDate)] = v } // 开始计算 for _, currentDate := range dateArr { //当前日期 currentDateStr := currentDate.Format(utils.FormatDate) currentItem, ok := dataMap[currentDateStr] if !ok { continue } // 找到的数据 var findItem *EdbInfoData //上一年的日期 preDate := currentDate.AddDate(-1, 0, 0) preDateStr := preDate.Format(utils.FormatDate) if findItem, ok = dataMap[preDateStr]; !ok { //上一年同期没找到 if frequency == "月度" { //向上和向下,各找一个月 for i := 0; i <= 35; i++ { nextDateDay := preDate.AddDate(0, 0, i) nextDateDayStr := nextDateDay.Format(utils.FormatDate) if findItem, ok = dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到 break } else { preDateDay := preDate.AddDate(0, 0, -i) preDateDayStr := preDateDay.Format(utils.FormatDate) if findItem, ok = dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到 break } } } } else if frequency == "季度" || frequency == "年度" { if findItem, ok = dataMap[preDateStr]; ok { //上一年同期->下一个月找到 break } } else { nextDateDay := preDate.AddDate(0, 0, 1) nextDateDayStr := nextDateDay.Format(utils.FormatDate) preDateDay := preDate.AddDate(0, 0, -1) preDateDayStr := preDateDay.Format(utils.FormatDate) for i := 0; i < 35; i++ { if i >= 1 { nextDateDay = nextDateDay.AddDate(0, 0, i) nextDateDayStr = nextDateDay.Format(utils.FormatDate) } if findItem, ok = dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到 break } else { if i >= 1 { preDateDay = preDateDay.AddDate(0, 0, -i) preDateDayStr = preDateDay.Format(utils.FormatDate) } if findItem, ok = dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到 break } } } } } // 如果没找到数据 if findItem == nil { continue } dateDataMap[currentDate] = tbzDiv(currentItem.Value, findItem.Value) } return } // tbzDiv // @Description: 同比值计算 // @author: Roc // @datetime2023-11-02 16:29:14 // @param a float64 // @param b float64 // @return float64 func tbzDiv(a, b float64) float64 { var val float64 if b != 0 { af := decimal.NewFromFloat(a) bf := decimal.NewFromFloat(b) val, _ = af.Div(bf).Sub(decimal.NewFromFloat(1)).RoundCeil(4).Float64() } return val } // Tcz // @Description: 计算同差值 // @author: Roc // @receiver obj // @datetime2023-11-02 18:04:51 // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) Tcz() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) dataList := obj.DataList // 降序 frequency := obj.FromFrequency // 数据处理 // 数据降序 dataList = reverseSliceByDesc(dataList) var dateArr []time.Time dataMap := make(map[string]*EdbInfoData) for _, v := range dataList { v.DataTime = time.Date(v.DataTime.Year(), v.DataTime.Month(), v.DataTime.Day(), 0, 0, 0, 0, v.DataTime.Location()) dateArr = append(dateArr, v.DataTime) dataMap[v.DataTime.Format(utils.FormatDate)] = v } // 开始计算 for _, currentDate := range dateArr { //当前日期 currentItem, ok := dataMap[currentDate.Format(utils.FormatDate)] if !ok { continue } // 找到的数据 var findItem *EdbInfoData //上一年的日期 preDate := currentDate.AddDate(-1, 0, 0) preDateStr := preDate.Format(utils.FormatDate) if findItem, ok = dataMap[preDateStr]; !ok { //上一年同期没找到 if frequency == "月度" { //向上和向下,各找一个月 for i := 0; i <= 35; i++ { nextDateDay := preDate.AddDate(0, 0, i) nextDateDayStr := nextDateDay.Format(utils.FormatDate) if findItem, ok = dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到 break } else { preDateDay := preDate.AddDate(0, 0, -i) preDateDayStr := preDateDay.Format(utils.FormatDate) if findItem, ok = dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到 break } } } } else if frequency == "季度" || frequency == "年度" { if findItem, ok = dataMap[preDateStr]; ok { //上一年同期->下一个月找到 break } } else { for i := 0; i < 35; i++ { nextDateDay := preDate.AddDate(0, 0, i) nextDateDayStr := nextDateDay.Format(utils.FormatDate) if findItem, ok = dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到 break } else { preDateDay := preDate.AddDate(0, 0, -i) preDateDayStr := preDateDay.Format(utils.FormatDate) if findItem, ok = dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到 break } } } } } // 如果没找到数据 if findItem == nil { continue } dateDataMap[currentDate] = tczSub(currentItem.Value, findItem.Value) } return } // tczSub // @Description: 计算同差值 // @author: Roc // @datetime2023-11-02 18:01:46 // @param a float64 // @param b float64 // @return float64 func tczSub(a, b float64) float64 { af := decimal.NewFromFloat(a) bf := decimal.NewFromFloat(b) val, _ := af.Sub(bf).RoundCeil(4).Float64() return val } // Nszydpjjs // @Description: N数值移动平均数计算 // @author: Roc // @receiver obj // @datetime2023-11-02 18:17:38 // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) Nszydpjjs() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) fromDataList := obj.DataList nVal := fmt.Sprint(obj.Formula) formulaInt, err := strconv.Atoi(nVal) if err != nil { return } // 数据处理 // 数据降序 fromDataList = reverseSliceByDesc(fromDataList) var fromDateArr []time.Time fromDataMap := make(map[time.Time]*EdbInfoData) for _, v := range fromDataList { fromDateArr = append(fromDateArr, v.DataTime) fromDataMap[v.DataTime] = v } arrLen := len(fromDateArr) for ak, currentDate := range fromDateArr { //处理第一个值 var valArr []float64 if findItem, ok := fromDataMap[currentDate]; ok { valArr = append(valArr, findItem.Value) } else { continue } if ak+1 != arrLen { //处理除第一个值之外的N-1个值 for i := 1; i < formulaInt; i++ { arrIndex := ak + i if arrIndex >= arrLen { break } arrVal := fromDateArr[arrIndex] if findItem, ok := fromDataMap[arrVal]; ok { valArr = append(valArr, findItem.Value) } else { continue } } } valArrLen := len(valArr) //var totalVal float64 totalVal := decimal.NewFromFloat(0.00) for _, v := range valArr { newDecimal := decimal.NewFromFloat(v) totalVal = totalVal.Add(newDecimal) } af := totalVal //decimal.NewFromFloat(totalVal) bf := decimal.NewFromFloat(float64(valArrLen)) val, _ := af.Div(bf).RoundCeil(4).Float64() dateDataMap[currentDate] = val } return } // Hbz // @Description: 环比值计算 // @author: Roc // @receiver obj // @datetime2023-11-02 18:28:24 // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) Hbz() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) dataList := obj.DataList nVal := fmt.Sprint(obj.Formula) formulaInt, err := strconv.Atoi(nVal) if err != nil { return } // 数据处理 // 数据降序 dataList = reverseSliceByDesc(dataList) var dateArr []time.Time for _, v := range dataList { v.DataTime = time.Date(v.DataTime.Year(), v.DataTime.Month(), v.DataTime.Day(), 0, 0, 0, 0, v.DataTime.Location()) dateArr = append(dateArr, v.DataTime) } // 数据计算 dataLen := len(dataList) //fmt.Println("dataLen:", dataLen) for i := 0; i < dataLen; i++ { j := i + formulaInt if j < dataLen { //当期 currentItem := dataList[i] preItem := dataList[j] // 当期和上期没有数据 if currentItem == nil || preItem == nil { continue } // 开始计算 val, ok := hbzDiv(currentItem.Value, preItem.Value) // 计算失败 if !ok { continue } dateDataMap[currentItem.DataTime] = val } } return } // hbzDiv // @Description: 环比值计算,current:当期,pre:上期 公式: (当期-上期)/上期 // @author: Roc // @datetime2023-11-02 18:20:11 // @param current float64 // @param pre float64 // @return val float64 // @return ok bool func hbzDiv(current, pre float64) (val float64, ok bool) { if pre == 0 { return } currentVal := decimal.NewFromFloat(current) preVal := decimal.NewFromFloat(pre) val, _ = currentVal.Sub(preVal).Div(preVal).RoundCeil(4).Float64() //valStr := decimal.NewFromFloat(val).RoundCeil(4).String() //utils.SubFloatToString(val, 4) ok = true return } // Hcz // @Description: 计算环差值数据 // @author: Roc // @receiver obj // @datetime2023-11-02 18:33:20 // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) Hcz() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) dataList := obj.DataList nVal := fmt.Sprint(obj.Formula) formulaInt, err := strconv.Atoi(nVal) if err != nil { return } // 数据处理 // 数据降序 dataList = reverseSliceByDesc(dataList) var dateArr []time.Time for _, v := range dataList { v.DataTime = time.Date(v.DataTime.Year(), v.DataTime.Month(), v.DataTime.Day(), 0, 0, 0, 0, v.DataTime.Location()) dateArr = append(dateArr, v.DataTime) } dataLen := len(dataList) //fmt.Println("dataLen:", dataLen) for i := 0; i < dataLen; i++ { j := i + formulaInt if j < dataLen { //当期 currentItem := dataList[i] preItem := dataList[j] // 当期和上期没有数据 if currentItem == nil || preItem == nil { continue } // 开始计算 val := hczDiv(currentItem.Value, preItem.Value) dateDataMap[currentItem.DataTime] = val } } return } // hczDiv // @Description: 环差值计算,current:当期,pre:上期 公式:当期-上期 // @author: Roc // @datetime2023-11-02 18:33:07 // @param current float64 // @param pre float64 // @return float64 func hczDiv(current, pre float64) float64 { currentVal := decimal.NewFromFloat(current) preVal := decimal.NewFromFloat(pre) val, _ := currentVal.Sub(preVal).RoundCeil(4).Float64() //valStr := decimal.NewFromFloat(val).RoundCeil(4).String() //utils.SubFloatToString(val, 4) return val } // UpFrequency // @Description: 升频计算 // @author: Roc // @receiver obj // @datetime2023-11-02 18:43:03 // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) UpFrequency() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) dataList := obj.DataList // 数据处理 var dateArr []time.Time dataMap := make(map[time.Time]*EdbInfoData) fromDataMap := make(map[time.Time]float64) //来源指指标数据 for _, v := range dataList { v.DataTime = time.Date(v.DataTime.Year(), v.DataTime.Month(), v.DataTime.Day(), 0, 0, 0, 0, v.DataTime.Location()) dateArr = append(dateArr, v.DataTime) dataMap[v.DataTime] = v fromDataMap[v.DataTime] = v.Value } if obj.FromFrequency == `日度` { errMsg = `不能选择日度指标` err = errors.New(errMsg) return } nowDay := time.Now() nowDay = time.Date(nowDay.Year(), nowDay.Month(), nowDay.Day(), 0, 0, 0, 0, nowDay.Location()) // 数据计算 dataLen := len(dataList) lastI := dataLen - 1 for i := 0; i < dataLen; i++ { //当期 currentItem := dataList[i] currentDate := currentItem.DataTime var day int var preItem *EdbInfoData var preDate time.Time // 如果是第一期的数据,那么直接赋值 if i == 0 { dateDataMap[currentItem.DataTime] = currentItem.Value continue } // 上一期 j := i - 1 preItem = dataList[j] preDate = preItem.DataTime day = int(currentDate.Sub(preItem.DataTime).Hours() / float64(24)) //utils.FileLog.Info("preItem.DataTime:" + preItem.DataTime.Format(utils.FormatDate) + ";currentItem.DataTime" + currentItem.DataTime.Format(utils.FormatDate)) //fmt.Println("preItem.DataTime:" + preItem.DataTime.Format(utils.FormatDate) + ";currentItem.DataTime:" + currentItem.DataTime.Format(utils.FormatDate)) for k := 1; k < day; k++ { needDay := preDate.AddDate(0, 0, k) dateDataMap[needDay] = preItem.Value } dateDataMap[currentItem.DataTime] = currentItem.Value // 如果是最后一期的数据 if i == lastI { day = int(nowDay.Sub(currentDate).Hours() / float64(24)) //utils.FileLog.Info("preItem.DataTime:" + currentDate.Format(utils.FormatDate) + ";currentItem.DataTime" + nowDay.Format(utils.FormatDate)) for k := 1; k <= day; k++ { needDay := currentDate.AddDate(0, 0, k) dateDataMap[needDay] = currentItem.Value } } } return } // DownFrequency // @Description: 降频计算 // @author: Roc // @receiver obj // @datetime2023-11-02 18:51:26 // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) DownFrequency() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) dataList := obj.DataList edbFrequency := obj.Frequency formula := obj.Formula.(string) if !CheckFrequency(obj.FromFrequency, obj.Frequency) { errMsg = "频度异常,不允许低频降频到高频" err = errors.New(errMsg) return } // 数据处理 var dateArr []time.Time dataMap := make(map[time.Time]*EdbInfoData) fromDataMap := make(map[time.Time]float64) //来源指指标数据 for _, v := range dataList { v.DataTime = time.Date(v.DataTime.Year(), v.DataTime.Month(), v.DataTime.Day(), 0, 0, 0, 0, v.DataTime.Location()) dateArr = append(dateArr, v.DataTime) dataMap[v.DataTime] = v fromDataMap[v.DataTime] = v.Value } // 数据计算 dataLen := len(dataList) if dataLen <= 0 { return } startDataTime := dataList[0].DataTime endDataTime := dataList[dataLen-1].DataTime var lastValue float64 // 最近的值 var nextEndDate time.Time // 下一个节点的日期 weekDayDataList := make([]float64, 0) for tmpStartDataTime := startDataTime; !tmpStartDataTime.After(endDataTime); tmpStartDataTime = tmpStartDataTime.AddDate(0, 0, 1) { // 将当前数据加入到 weekDayDataList if tmpData, ok := dataMap[tmpStartDataTime]; ok { tmpValue := decimal.NewFromFloat(tmpData.Value) tmpValueFloat, _ := tmpValue.Round(4).Float64() weekDayDataList = append(weekDayDataList, tmpValueFloat) } // 如果下个节点的日期不存在,那么就先给赋值(兼容时间区间内只有一组数据的情况) if nextEndDate.IsZero() { nextEndDate = utils.GetFrequencyEndDay(tmpStartDataTime, edbFrequency) } // 日期处理过滤 switch edbFrequency { case "周度": if tmpStartDataTime.Weekday() != 5 { //不是周五,代表需要进入下一个循环获取数据并计算 continue } else { //记录下一个结束节点的日期 nextEndDate = tmpStartDataTime.AddDate(0, 0, 7) } case "旬度": nextDay := tmpStartDataTime.AddDate(0, 0, 1) if nextDay.Day() != 1 && nextDay.Day() != 11 && nextDay.Day() != 21 { //不是每月10、20、最后一天,代表需要进入下一个循环获取数据并计算 continue } else { //记录下一个结束节点的日期 if nextDay.Day() == 1 || nextDay.Day() == 11 { //月初或者月末的时候,加10天就好了 nextEndDate = nextDay.AddDate(0, 0, 9) } else { tmpNextMonth := nextDay.AddDate(0, 1, 0) nextEndDate = time.Date(tmpNextMonth.Year(), tmpNextMonth.Month(), 1, 0, 0, 0, 0, time.Local).AddDate(0, 0, -1) } } case "月度": nextDay := tmpStartDataTime.AddDate(0, 0, 1) if nextDay.Day() != 1 { //不是每月最后一天,代表需要进入下一个循环获取数据并计算 continue } else { //记录下一个结束节点的日期 nextEndDate = nextDay.AddDate(0, 1, -1) } case "季度": nextDay := tmpStartDataTime.AddDate(0, 0, 1) if (nextDay.Month() == 1 || nextDay.Month() == 4 || nextDay.Month() == 7 || nextDay.Month() == 10) && nextDay.Day() == 1 { //记录下一个结束节点的日期 nextEndDate = nextDay.AddDate(0, 3, -1) } else { //不是3,6,9,12 月份的最后一天,代表需要进入下一个循环获取数据并计算 continue } case "年度": if tmpStartDataTime.Month() == 12 && tmpStartDataTime.Day() == 31 { //记录下一个结束节点的日期 nextEndDate = tmpStartDataTime.AddDate(1, 0, 0) } else { //不是每年的12-31日,代表需要进入下一个循环获取数据并计算 continue } default: err = errors.New("错误的频度:" + edbFrequency) return } // 当前时间段内的数据计算,得出实际值 var currVal float64 lenWeekDayDataList := len(weekDayDataList) // 如果这个时间区间内没有数据,那么就采用上一个时间区间的值 if len(weekDayDataList) <= 0 { currVal = lastValue } else { if formula == "期末值" { currVal = weekDayDataList[lenWeekDayDataList-1] } else { // 平均值 sumValDeci := decimal.NewFromFloat(0) for _, v := range weekDayDataList { tmpValDeci := decimal.NewFromFloat(v) sumValDeci = sumValDeci.Add(tmpValDeci) } lenDeci := decimal.NewFromInt(int64(lenWeekDayDataList)) currVal, _ = sumValDeci.Div(lenDeci).Round(4).Float64() } } // 赋值 dateDataMap[tmpStartDataTime] = currVal // 一轮结束后,数据清空 weekDayDataList = make([]float64, 0) } // 最后已有的日期处理完成后,需要对剩余不在时间段内的数据做处理 if len(weekDayDataList) > 0 { // 当前时间段内的数据计算,得出实际值 var currVal float64 lenWeekDayDataList := len(weekDayDataList) // 如果这个时间区间内没有数据,那么就采用上一个时间区间的值 if len(weekDayDataList) < 0 { currVal = lastValue } else { if formula == "期末值" { currVal = weekDayDataList[lenWeekDayDataList-1] } else { // 平均值 sumValDeci := decimal.NewFromFloat(0) for _, v := range weekDayDataList { tmpValDeci := decimal.NewFromFloat(v) sumValDeci = sumValDeci.Add(tmpValDeci) } lenDeci := decimal.NewFromInt(int64(lenWeekDayDataList)) currVal, _ = sumValDeci.Div(lenDeci).Round(4).Float64() } } // 赋值 dateDataMap[nextEndDate] = currVal } return } // TimeShift // @Description: 时间移位计算 // @author: Roc // @receiver obj // @datetime2023-11-03 13:19:07 // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) TimeShift() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) dataList := obj.DataList nVal := fmt.Sprint(obj.Formula) formulaInt, err := strconv.Atoi(nVal) if err != nil { return } moveType := obj.MoveType moveFrequency := obj.MoveFrequency // 数据处理 var dateArr []time.Time dataMap := make(map[time.Time]*EdbInfoData) for _, v := range dataList { v.DataTime = time.Date(v.DataTime.Year(), v.DataTime.Month(), v.DataTime.Day(), 0, 0, 0, 0, v.DataTime.Location()) dateArr = append(dateArr, v.DataTime) dataMap[v.DataTime] = v } var shiftDay int switch moveFrequency { case "天": shiftDay = formulaInt case "周": shiftDay = formulaInt * 7 case "月": shiftDay = formulaInt * 30 case "季": shiftDay = formulaInt * 90 case "年": shiftDay = formulaInt * 365 default: shiftDay = formulaInt } if moveType == 2 { shiftDay = -shiftDay } dataLen := len(dataList) for i := 0; i < dataLen; i++ { //当期 currentItem := dataList[i] currentDate := currentItem.DataTime newDate := currentDate.AddDate(0, 0, shiftDay) val, _ := decimal.NewFromFloat(currentItem.Value).RoundCeil(4).Float64() dateDataMap[newDate] = val } return } // Cjjx // @Description: 超季节性计算 // @author: Roc // @receiver obj // @datetime2023-11-03 13:28:23 // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) Cjjx() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) dataList := obj.DataList nVal := fmt.Sprint(obj.Formula) formulaInt, err := strconv.Atoi(nVal) if err != nil { return } calendar := obj.Calendar // 数据处理 var dateArr []time.Time dataMap := make(map[time.Time]*EdbInfoData) for _, v := range dataList { v.DataTime = time.Date(v.DataTime.Year(), v.DataTime.Month(), v.DataTime.Day(), 0, 0, 0, 0, v.DataTime.Location()) dateArr = append(dateArr, v.DataTime) dataMap[v.DataTime] = v } // 通过插值法补全所有数据(包含周末) handleDataMap := make(map[time.Time]float64) _, err = HandleDataByLinearRegressionByTime(dataList, handleDataMap) if err != nil { return } // 每个年份的日期数据需要平移的天数 moveDayMap := make(map[int]int, 0) // 每个年份的春节公历 var lastDataDay time.Time if len(dataList) > 0 { lastDataDay = dataList[0].DataTime } // 数据计算 for _, currentDate := range dateArr { // 如果遇到闰二月,如2.29,去掉该天数据 if strings.Contains(currentDate.Format(utils.FormatDate), "02-29") { continue } //农历的超季节性运算,只计算11月--次年5月,分段计算,与数据区间和N数值有关 if calendar == "农历" && currentDate.Month() > 5 && currentDate.Month() < 11 { continue } currentItem, ok := dataMap[currentDate] // 找不到数据就退出当前循环,进入下一循环 if !ok { continue } pastValueList := make([]float64, 0) // 过去几期的数据 pastValueList = append(pastValueList, currentItem.Value) //当前日期 for i := 1; i < formulaInt; i++ { //前几年当天公历的日期 historyPreDate := currentDate.AddDate(-i, 0, 0) moveDay := 0 if calendar == "农历" { if tmpMoveDay, ok := moveDayMap[historyPreDate.Year()]; !ok { moveDay, err = getMoveDay(lastDataDay, historyPreDate) if err != nil { return } } else { moveDay = tmpMoveDay } // 移动天数到对应农历 的 公历 日期 historyPreDate = historyPreDate.AddDate(0, 0, moveDay) } if tmpValue, ok := handleDataMap[historyPreDate]; ok { //上一年同期找到 pastValueList = append(pastValueList, tmpValue) } } if len(pastValueList) == formulaInt { val, ok := cjjxSub(currentItem.Value, pastValueList) if !ok { continue } dateDataMap[currentDate] = val } } return } // cjjxSub // @Description: 超季节性计算 // @author: Roc // @datetime2023-11-03 13:25:49 // @param currValue float64 // @param pastValue []float64 // @return value float64 // @return ok bool func cjjxSub(currValue float64, pastValue []float64) (value float64, ok bool) { num := len(pastValue) if num == 0 { return } numDecimal := decimal.NewFromInt(int64(num)) af := decimal.NewFromFloat(currValue) bf := decimal.NewFromFloat(pastValue[0]) for k := 1; k < num; k++ { tmpVal := decimal.NewFromFloat(pastValue[k]) bf = bf.Add(tmpVal) } value, _ = af.Sub(bf.Div(numDecimal)).RoundCeil(4).Float64() ok = true return } // Annualized // @Description: 年化计算 // @author: Roc // @receiver obj // @datetime2023-11-03 13:34:32 // @param to orm.TxOrmer // @param edbInfoId int // @param source int // @param fromEdbInfo *EdbInfo // @param edbCode string // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) Annualized() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) fromDataList := obj.DataList lenFromDataList := len(fromDataList) // 如果来源指标没有数据,那么就直接返回得了 if lenFromDataList <= 0 { return } // 数据处理 // 插值法数据处理 handleDataMap := make(map[time.Time]float64) _, err = HandleDataByLinearRegressionByTime(fromDataList, handleDataMap) if err != nil { return } // 每年的最后一天的数据值 yearLastValMap := make(map[int]float64) startDataTime := fromDataList[0].DataTime endDataTime := fromDataList[lenFromDataList-1].DataTime for i := startDataTime.Year(); i <= endDataTime.Year(); i++ { tmpDate := time.Date(i, 12, 31, startDataTime.Hour(), startDataTime.Minute(), startDataTime.Second(), startDataTime.Nanosecond(), time.Local) if tmpVal, ok := handleDataMap[tmpDate]; ok { yearLastValMap[i] = tmpVal } } // 数据计算 // 遍历来源指标数据 for _, v := range fromDataList { currDate := v.DataTime perValMap := make(map[time.Time]float64) //前3年当日的数据 for i := 1; i <= 3; i++ { tmpDateTime := currDate.AddDate(-i, 0, 0) if tmpVal, ok := handleDataMap[tmpDateTime]; ok { perValMap[tmpDateTime] = tmpVal } } lenPerValMap := len(perValMap) // 如果数据少于2年,那么就不参与计算,结束当前循环,进入下一个循环 if lenPerValMap < 2 { continue } // N年 当前值占全年比重 的值列表 divValList := make([]decimal.Decimal, 0) for tmpDateTime, tmpVal := range perValMap { yearLastVal, ok2 := yearLastValMap[tmpDateTime.Year()] // 如果当年最后一天没有数据 if !ok2 { continue } // 当前值占全年比重 tmpYearLastVal := decimal.NewFromFloat(yearLastVal) if tmpYearLastVal.IsZero() { //如果是0,那么就退出当前循环,进入下一个循环 continue } divVal := decimal.NewFromFloat(tmpVal).Div(tmpYearLastVal) divValList = append(divValList, divVal) } lenDivValList := len(divValList) // 如果 N年 当前值占全年比重 的值 小于 2个,那么就不参与计算,结束当前循环,进入下一个循环 if lenDivValList < 2 { continue } divValSum := decimal.NewFromFloat(0) for _, divVal := range divValList { divValSum = divValSum.Add(divVal) } // 当前计算出来的结果 tmpDivVal := divValSum.Div(decimal.NewFromInt(int64(lenDivValList))) if tmpDivVal.IsZero() { //如果是0,那么就退出当前循环,进入下一个循环 continue } currVal, _ := decimal.NewFromFloat(v.Value).Div(tmpDivVal).Round(4).Float64() dateDataMap[currDate] = currVal } return } // Ljz // @Description: 累计值 // @author: Roc // @receiver obj // @datetime2023-11-03 13:49:17 // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) Ljz() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) dataList := obj.DataList frequency := obj.Frequency //需要变更的频度 fromFrequency := obj.FromFrequency //来源的频度 // 数据处理 var isWeekData bool // 是否周度数据,如果是周度数据的话,是需要变频的,最后结果还需要除以7 // 周度数据需要先变成日度的 if fromFrequency == `周度` { isWeekData = true } fromEdbDataMap := make(map[time.Time]float64) if isWeekData { dataList, err = HandleDataByLinearRegressionByTime(dataList, fromEdbDataMap) if err != nil { return } } //日度转周度:日期选周五,计算上周六到本周五的日度值的加总,最新日期为最新值对应的周五。 //日度转月度:日期选每个月最后一天,计算当月所有日度值的加总,最新日期为最新值对应当月最后一天。 //日度转季度、年度:方法类似转月度。 //周度转月度/季度/年度:将周度值转成日度,空值用插值法插值,计算当月/当季/当年所有值的加总,然后除以7。 //月度转季度/年度: 当季/当年月度值相加。 dateList := make([]time.Time, 0) valueMap := make(map[time.Time]float64) switch frequency { case "年度": yearMap := make(map[int]float64) yearList := make([]int, 0) for _, item := range dataList { item.DataTime = time.Date(item.DataTime.Year(), item.DataTime.Month(), item.DataTime.Day(), 0, 0, 0, 0, item.DataTime.Location()) year := item.DataTime.Year() yearVal, ok := yearMap[year] if ok { yearMap[year] = item.Value + yearVal } else { yearList = append(yearList, year) yearMap[year] = item.Value } } for _, v := range yearList { currTime := time.Date(v, 12, 31, 0, 0, 0, 0, time.Local) dateList = append(dateList, currTime) valueMap[currTime] = yearMap[v] } case "半年度": yearMonthMap := make(map[string]float64) yearMonthList := make([]string, 0) for _, item := range dataList { item.DataTime = time.Date(item.DataTime.Year(), item.DataTime.Month(), item.DataTime.Day(), 0, 0, 0, 0, item.DataTime.Location()) itemDate := item.DataTime year := itemDate.Year() var tmpK string if itemDate.Month() <= 6 { tmpK = fmt.Sprint(year, "06") } else { tmpK = fmt.Sprint(year, "12") } yearVal, ok := yearMonthMap[tmpK] if ok { yearMonthMap[tmpK] = item.Value + yearVal } else { yearMonthList = append(yearMonthList, tmpK) yearMonthMap[tmpK] = item.Value } } for _, v := range yearMonthList { currTime, tmpErr := time.ParseInLocation(utils.FormatYearMonthUnSpace, v, time.Local) if tmpErr != nil { err = tmpErr return } currTime = currTime.AddDate(0, 1, -1) dateList = append(dateList, currTime) valueMap[currTime] = yearMonthMap[v] } case "季度": yearMonthMap := make(map[string]float64) yearMonthList := make([]string, 0) for _, item := range dataList { item.DataTime = time.Date(item.DataTime.Year(), item.DataTime.Month(), item.DataTime.Day(), 0, 0, 0, 0, item.DataTime.Location()) itemDate := item.DataTime year := itemDate.Year() var tmpK string if itemDate.Month() <= 3 { tmpK = fmt.Sprint(year, "03") } else if itemDate.Month() <= 6 { tmpK = fmt.Sprint(year, "06") } else if itemDate.Month() <= 9 { tmpK = fmt.Sprint(year, "09") } else { tmpK = fmt.Sprint(year, "12") } yearVal, ok := yearMonthMap[tmpK] if ok { yearMonthMap[tmpK] = item.Value + yearVal } else { yearMonthList = append(yearMonthList, tmpK) yearMonthMap[tmpK] = item.Value } } for _, v := range yearMonthList { currTime, tmpErr := time.ParseInLocation(utils.FormatYearMonthUnSpace, v, time.Local) if tmpErr != nil { err = tmpErr return } currTime = currTime.AddDate(0, 1, -1) dateList = append(dateList, currTime) valueMap[currTime] = yearMonthMap[v] } case "月度": yearMonthMap := make(map[string]float64) yearMonthList := make([]string, 0) for _, item := range dataList { item.DataTime = time.Date(item.DataTime.Year(), item.DataTime.Month(), item.DataTime.Day(), 0, 0, 0, 0, item.DataTime.Location()) itemDate := item.DataTime year := itemDate.Year() var tmpK string tmpK = fmt.Sprint(year*100 + int(itemDate.Month())) yearVal, ok := yearMonthMap[tmpK] if ok { yearMonthMap[tmpK] = item.Value + yearVal } else { yearMonthList = append(yearMonthList, tmpK) yearMonthMap[tmpK] = item.Value } } for _, v := range yearMonthList { currTime, tmpErr := time.ParseInLocation(utils.FormatYearMonthUnSpace, v, time.Local) if tmpErr != nil { err = tmpErr return } currTime = currTime.AddDate(0, 1, -1) dateList = append(dateList, currTime) valueMap[currTime] = yearMonthMap[v] } case "旬度": tmpDateDataMap := make(map[time.Time]float64) tmpDateList := make([]time.Time, 0) for _, item := range dataList { item.DataTime = time.Date(item.DataTime.Year(), item.DataTime.Month(), item.DataTime.Day(), 0, 0, 0, 0, item.DataTime.Location()) itemDate := item.DataTime dayInt := itemDate.Year()*100 + int(itemDate.Month()) var currTime time.Time if itemDate.Day() <= 10 { //本月上旬 tmpK := fmt.Sprint(dayInt, "10") currTime, err = time.ParseInLocation(utils.FormatDateUnSpace, tmpK, time.Local) if err != nil { return } } else if itemDate.Day() <= 20 { // 本月中旬 tmpK := fmt.Sprint(dayInt, "20") currTime, err = time.ParseInLocation(utils.FormatDateUnSpace, tmpK, time.Local) if err != nil { return } } else { // 本月下旬 currTime, err = time.ParseInLocation(utils.FormatYearMonthUnSpace, fmt.Sprint(dayInt), time.Local) if err != nil { return } currTime = currTime.AddDate(0, 1, -1) } yearVal, ok := tmpDateDataMap[currTime] if ok { tmpDateDataMap[currTime] = item.Value + yearVal } else { tmpDateList = append(tmpDateList, currTime) tmpDateDataMap[currTime] = item.Value } } for _, currTime := range tmpDateList { dateList = append(dateList, currTime) valueMap[currTime] = tmpDateDataMap[currTime] } case "周度": tmpDateDataMap := make(map[time.Time]float64) tmpDateList := make([]time.Time, 0) for _, item := range dataList { item.DataTime = time.Date(item.DataTime.Year(), item.DataTime.Month(), item.DataTime.Day(), 0, 0, 0, 0, item.DataTime.Location()) itemDate := item.DataTime var currTime time.Time // 周六周日,这是下一个周五的数据 if itemDate.Weekday() == 0 { currTime = itemDate.AddDate(0, 0, 5) } else if itemDate.Weekday() == 6 { currTime = itemDate.AddDate(0, 0, 6) } else { currTime = itemDate.AddDate(0, 0, 5-int(itemDate.Weekday())) } yearVal, ok := tmpDateDataMap[currTime] if ok { tmpDateDataMap[currTime] = item.Value + yearVal } else { tmpDateList = append(tmpDateList, currTime) tmpDateDataMap[currTime] = item.Value } } for _, currTime := range tmpDateList { dateList = append(dateList, currTime) valueMap[currTime] = tmpDateDataMap[currTime] } default: err = errors.New("错误的频度") return } // 数据计算 for _, currTime := range dateList { tmpVal, ok2 := valueMap[currTime] if !ok2 { err = errors.New("数据异常,date:" + currTime.Format(utils.FormatDate)) return } var saveValue float64 if isWeekData { //周度指标转的话,最后结果要除以7 saveValue, _ = decimal.NewFromFloat(tmpVal).Div(decimal.NewFromInt(7)).Round(4).Float64() } else { saveValue, _ = decimal.NewFromFloat(tmpVal).Round(4).Float64() } dateDataMap[currTime] = saveValue } return } // LjzNczj // @Description:年初至今累计值计算 // @author: Roc // @receiver obj // @datetime2023-11-03 13:55:44 // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) LjzNczj() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) dataList := obj.DataList frequency := obj.Frequency //需要变更的频度 fromFrequency := obj.FromFrequency //来源的频度 // 数据处理 var isWeekData bool // 是否周度数据,如果是周度数据的话,是需要变频的,最后结果还需要除以7 // 周度数据需要先变成日度的 if fromFrequency == `周度` { isWeekData = true } fromEdbDataMap := make(map[time.Time]float64) if isWeekData { dataList, err = HandleDataByLinearRegressionByTime(dataList, fromEdbDataMap) if err != nil { return } } //日度数据年初至今:日期同原日度数据。将每年1月1日(含)到日度数据所在日期含间的日度值,进行加总。 //周度数据年初至今:日期同原周度数据。将周度值转成日度频率,空值用插值法插值,然后算法同日度年度至今,再除以7 //月度/季度数据年初至今:日期同原月度/季度数据,将每年1月1日(含)到月度数据所在日期(含)之间的月度/季度值,进行加总 //以此类推 dateList := make([]time.Time, 0) valueMap := make(map[time.Time]float64) yearMap := make(map[int]float64) switch frequency { case "周度": tmpDateDataMap := make(map[time.Time]float64) tmpDateList := make([]time.Time, 0) for _, item := range dataList { item.DataTime = time.Date(item.DataTime.Year(), item.DataTime.Month(), item.DataTime.Day(), 0, 0, 0, 0, item.DataTime.Location()) itemDate := item.DataTime var currTime time.Time // 周六周日,这是下一个周五的数据 if itemDate.Weekday() == 0 { currTime = itemDate.AddDate(0, 0, 5) } else if itemDate.Weekday() == 6 { currTime = itemDate.AddDate(0, 0, 6) } else { currTime = itemDate.AddDate(0, 0, 5-int(itemDate.Weekday())) } year := itemDate.Year() yearVal, ok := yearMap[year] if ok { yearMap[year] = item.Value + yearVal } else { yearMap[year] = item.Value } if itemDate.Equal(currTime) { tmpDateDataMap[itemDate] = yearMap[year] tmpDateList = append(tmpDateList, itemDate) } } for _, currTime := range tmpDateList { dateList = append(dateList, currTime) valueMap[currTime] = tmpDateDataMap[currTime] } default: for _, item := range dataList { item.DataTime = time.Date(item.DataTime.Year(), item.DataTime.Month(), item.DataTime.Day(), 0, 0, 0, 0, item.DataTime.Location()) itemDate := item.DataTime year := itemDate.Year() yearVal, ok := yearMap[year] if ok { yearMap[year] = item.Value + yearVal } else { yearMap[year] = item.Value } valueMap[itemDate] = yearMap[year] dateList = append(dateList, itemDate) } } // 数据计算 for _, currTime := range dateList { tmpVal, ok2 := valueMap[currTime] if !ok2 { err = errors.New("数据异常,date:" + currTime.Format(utils.FormatDate)) return } var saveValue float64 if isWeekData { //周度指标转的话,最后结果要除以7 saveValue, _ = decimal.NewFromFloat(tmpVal).Div(decimal.NewFromInt(7)).Round(4).Float64() } else { saveValue, _ = decimal.NewFromFloat(tmpVal).Round(4).Float64() } dateDataMap[currTime] = saveValue } return } // ExponentialSmoothing // @Description: 指数修匀计算 // @author: Roc // @receiver obj // @datetime2023-11-03 14:07:47 // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) ExponentialSmoothing() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) dataList := obj.DataList formula := obj.Formula.(string) // alpha值 alpha, _ := strconv.ParseFloat(formula, 64) if alpha <= 0 || alpha >= 1 { err = fmt.Errorf("alpha值有误: %v", alpha) return } // 获取标准差图表的指标数据 fromDataList, err := calculateExponentialSmoothingData(dataList, alpha) if err != nil { return } // 数据计算 for _, tmpData := range fromDataList { if tmpData.DataTime.IsZero() { continue } // 当前的实际值 saveValue, _ := decimal.NewFromFloat(tmpData.Value).Round(4).Float64() dateDataMap[tmpData.DataTime] = saveValue } return } // calculateExponentialSmoothingData // @Description: 计算指数修匀 // @author: Roc // @datetime2023-11-03 14:05:41 // @param dataList []*EdbInfoData 时间基准指标在时间区间内的值 // @param strAlpha float64 // @return newDataList []EdbInfoData // @return err error func calculateExponentialSmoothingData(dataList []*EdbInfoData, alpha float64) (newDataList []EdbInfoData, err error) { if alpha <= 0 || alpha >= 1 { err = fmt.Errorf("alpha值有误: %v", alpha) return } var preVal float64 alphaDecimal := decimal.NewFromFloat(alpha) subAlpha := decimal.NewFromFloat(1).Sub(alphaDecimal) for k, d := range dataList { d.DataTime = time.Date(d.DataTime.Year(), d.DataTime.Month(), d.DataTime.Day(), 0, 0, 0, 0, d.DataTime.Location()) // 首期的值以原始值作为指数修匀的计算值 if k == 0 { newDataList = append(newDataList, EdbInfoData{ EdbDataId: k, DataTime: dataList[k].DataTime, Value: d.Value, }) preVal = d.Value continue } // 上一期的值参与计算 preDecimal := decimal.NewFromFloat(preVal) valDecimal := decimal.NewFromFloat(d.Value) partA := alphaDecimal.Mul(valDecimal) partB := subAlpha.Mul(preDecimal) res, _ := (partA.Add(partB)).Float64() preVal = res newDataList = append(newDataList, EdbInfoData{ EdbDataId: k, DataTime: dataList[k].DataTime, Value: res, }) } return } // Rjz // @Description: 日均值计算 // @author: Roc // @receiver obj // @datetime2023-11-03 14:47:47 // @return dateDataMap map[time.Time]float64 // @return err error func (obj BaseCalculate) Rjz() (dateDataMap map[time.Time]float64, err error, errMsg string) { dateDataMap = make(map[time.Time]float64) dataList := obj.DataList fromFrequency := obj.FromFrequency if fromFrequency == `` { errMsg = "原指标的频度不能为空" err = errors.New(errMsg) return } if obj.FromFrequency == "日度" { errMsg = "日度指标无需进行日均值计算" err = errors.New(errMsg) return } // 数据处理 // 数据降序 dataList = reverseSliceByDesc(dataList) var dateArr []time.Time dataMap := make(map[time.Time]*EdbInfoData) for _, v := range dataList { v.DataTime = time.Date(v.DataTime.Year(), v.DataTime.Month(), v.DataTime.Day(), 0, 0, 0, 0, v.DataTime.Location()) dateArr = append(dateArr, v.DataTime) dataMap[v.DataTime] = v } // 数据计算 for _, currentDate := range dateArr { //当前日期 currentItem, ok := dataMap[currentDate] if !ok { continue } //根据频度计算需要均分的天数 days := GetRjzFrequencyDays(currentDate, fromFrequency) val, ok := rjzDivV2(currentItem.Value, days) if !ok { continue } dateDataMap[currentDate] = val } return } // rjzDivV2 // @Description: 日均值计算 // @author: Roc // @datetime2023-11-03 14:47:36 // @param a float64 // @param b int // @return val float64 // @return ok bool func rjzDivV2(a float64, b int) (val float64, ok bool) { if b == 0 { return } af := decimal.NewFromFloat(a) bf := decimal.NewFromFloat(float64(b)) val, _ = af.Div(bf).Round(4).Float64() ok = true return } // reverseSliceByDesc // @Description: 倒转切片 // @author: Roc // @datetime2023-11-06 14:08:25 // @param slice []*EdbInfoData // @return []*EdbInfoData func reverseSliceByDesc(slice []*EdbInfoData) (newSlice []*EdbInfoData) { // 只有一个数据的话,那么就直接返回 if len(slice) <= 1 { newSlice = slice return } if !slice[1].DataTime.After(slice[0].DataTime) { newSlice = slice return } newSlice = make([]*EdbInfoData, len(slice)) for i, j := 0, len(slice)-1; i < len(slice); i, j = i+1, j-1 { newSlice[i] = slice[j] } return } // EdbInfoSearchDataToData // @Description: 将EdbInfoSearchData转成EdbInfoData // @author: Roc // @datetime2023-11-09 11:11:31 // @param dataList []*EdbInfoSearchData // @return newDateDataList []*EdbInfoData // @return err error func EdbInfoSearchDataToData(dataList []*EdbInfoSearchData) (newDateDataList []*EdbInfoData, err error) { newDateDataList = make([]*EdbInfoData, 0) for _, data := range dataList { dateTime, tmpErr := time.ParseInLocation(utils.FormatDate, data.DataTime, time.Local) if tmpErr != nil { err = tmpErr return } newDateDataList = append(newDateDataList, &EdbInfoData{ EdbDataId: data.EdbDataId, DataTime: dateTime, Value: data.Value, }) } return } // GetDateDataAndDateList // @Description: 通过时间数据map 获取 时间字符串数据map和日期字符串切片 // @author: Roc // @datetime2023-11-09 13:10:38 // @param dateDataMap map[time.Time]float64 // @return dateStrDataMap map[string]float64 // @return dateStrList []string func GetDateDataAndDateList(dateDataMap map[time.Time]float64) (dateStrDataMap map[string]float64, dateStrList []string) { dateStrDataMap = make(map[string]float64) dateStrList = make([]string, 0) dateList := make([]time.Time, 0) for date, val := range dateDataMap { dateStr := date.Format(utils.FormatDate) dateStrDataMap[dateStr] = val dateList = append(dateList, date) } // 使用 sort.Slice 对日期切片进行排序 sort.Slice(dateList, func(i, j int) bool { return dateList[i].Before(dateList[j]) }) // 将日期排序后返回日期字符串切片 for _, v := range dateList { dateStrList = append(dateStrList, v.Format(utils.FormatDate)) } return } // StepCalculateBySearchData // @Description: 分步骤计算 type StepCalculateBySearchData struct { DataList []*EdbInfoSearchData `description:"基础数据"` Calculates []CalculatesReq } type CalculatesReq struct { Formula interface{} Calendar string `description:"公历/农历"` Frequency string `description:"需要转换的频度"` MoveType int `description:"移动方式:1:领先(默认),2:滞后"` MoveFrequency string `description:"移动频度"` FromFrequency string `description:"来源的频度"` Source int `description:"1:累计值转月;2:累计值转季;3:同比值;4:同差值;5:N数值移动平均数计算;6:环比值;7:环差值;8:升频;9:降频;10:时间移位;11:超季节性;12:年化;13:累计值;14:累计值年初至今;15:指数修匀;16:日均值"` Sort int `description:"计算顺序"` } func TransDateData2EdbData(dateData map[time.Time]float64) (edbData []*EdbInfoData) { edbData = make([]*EdbInfoData, 0) for d, v := range dateData { edbData = append(edbData, &EdbInfoData{ DataTime: d, Value: v, }) } sort.Slice(edbData, func(i, j int) bool { return edbData[i].DataTime.Before(edbData[j].DataTime) }) return }