package line_equation import ( "errors" "eta/eta_chart_lib/models" "eta/eta_chart_lib/models/data_manage/line_equation/request" "eta/eta_chart_lib/services/data" "eta/eta_chart_lib/utils" "github.com/shopspring/decimal" "math" "time" ) type LineEquationResp struct { AData LineEquationDataResp BData LineEquationDataResp R2Data LineEquationDataResp } type LineEquationDataResp struct { MaxData float64 MinData float64 LatestDate string `description:"真实数据的最后日期"` EdbInfoCategoryType int ChartColor string ChartStyle string PredictChartColor string ChartType int ChartWidth int EdbName string EdbNameEn string Unit string UnitEn string IsAxis int DataList []models.EdbDataList } // GetChartEdbData 获取图表数据 func GetChartEdbData(chartInfoId int, lineChartInfoConfig request.LineChartInfoReq, getAData, getBData, getR2Data bool) (edbList []*models.ChartEdbInfoMapping, dataResp LineEquationResp, err error, errMsg string) { // 获取基本信息 mappingList, startDate, endDate, err, errMsg := getConfigData(lineChartInfoConfig) if err != nil { return } xEdbInfoIdList := lineChartInfoConfig.XEdbInfoIdList yEdbInfoIdList := lineChartInfoConfig.YEdbInfoIdList aLineEquationDataResp := LineEquationDataResp{ DataList: make([]models.EdbDataList, 0), MaxData: 0, MinData: 0, ChartColor: "#00f", ChartStyle: `spline`, PredictChartColor: `#00f`, ChartType: 0, ChartWidth: 3, EdbName: `弹性系数`, EdbNameEn: `elastic coefficient`, IsAxis: 1, } bLineEquationDataResp := LineEquationDataResp{ DataList: make([]models.EdbDataList, 0), MaxData: 0, MinData: 0, ChartColor: "#00f", ChartStyle: `spline`, PredictChartColor: `#00f`, ChartType: 0, ChartWidth: 3, EdbName: `截距`, EdbNameEn: `intercept`, IsAxis: 1, } r2LineEquationDataResp := LineEquationDataResp{ DataList: make([]models.EdbDataList, 0), MaxData: 0, MinData: 0, ChartColor: "#00f", ChartStyle: `spline`, PredictChartColor: `#00f`, ChartType: 0, ChartWidth: 3, EdbName: `相关系数`, EdbNameEn: `coefficient of association`, IsAxis: 1, } edbList = make([]*models.ChartEdbInfoMapping, 0) dataResp = LineEquationResp{ AData: aLineEquationDataResp, BData: bLineEquationDataResp, R2Data: r2LineEquationDataResp, } var baseEdbInfo *models.ChartEdbInfoMapping // 获取确定以哪个指标的日期作为基准日期 { var xEdbInfo, yEdbInfo *models.ChartEdbInfoMapping for _, v := range mappingList { if v.EdbInfoId == xEdbInfoIdList[0] { xEdbInfo = v } else if v.EdbInfoId == yEdbInfoIdList[0] { yEdbInfo = v } } if xEdbInfo == nil { errMsg = `X轴第一个指标异常` err = errors.New(errMsg) return } if yEdbInfo == nil { errMsg = `Y轴第一个指标异常` err = errors.New(errMsg) return } // 时间截面规则:按照什么频率来取不同的截面的问题。原则是:按照X轴和Y轴所选择的第一个指标(X和Y分别有一个第一个指标),两个指标中,以低频的那个作为基准。如果X轴和Y轴是同频的,那以Y轴第一个指标的为准。 frequencyIntMap := map[string]int{ "日度": 1, "周度": 2, "旬度": 3, "月度": 4, "季度": 5, "年度": 6, } // 如果x是高频 或者 x与y是同频的,那么就是Y轴的第一个指标为主 if frequencyIntMap[xEdbInfo.Frequency] <= frequencyIntMap[yEdbInfo.Frequency] { baseEdbInfo = yEdbInfo } else { // 否则是X轴的第一个指标是低频 baseEdbInfo = xEdbInfo } } // 指标对应的所有数据 chartType := 1 //1:普通图,2:季节性图 calendar := "公历" edbDataListMap, edbList, err := data.GetEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList, "") if err != nil { return } // 获取所有的日期 dateList := make([]string, 0) for _, v := range edbDataListMap[baseEdbInfo.EdbInfoId] { dateList = append(dateList, v.DataTime) } // 数据整理 // [日期][A指标id:值,B指标id:值] dateEdbMap, err := handleData(baseEdbInfo.EdbInfoId, edbDataListMap) if err != nil { return } lenX := len(xEdbInfoIdList) var isNotAFirst, isNotBFirst, isNotR2First bool for i, date := range dateList { coordinateData := make([]utils.Coordinate, 0) for k := 0; k < lenX; k++ { xVal, ok1 := dateEdbMap[date][xEdbInfoIdList[k]] yVal, ok2 := dateEdbMap[date][yEdbInfoIdList[k]] if !ok1 || !ok2 { continue } tmpCoordinate1 := utils.Coordinate{ X: xVal, Y: yVal, } coordinateData = append(coordinateData, tmpCoordinate1) } dataTime, _ := time.ParseInLocation(utils.FormatDate, date, time.Local) timestamp := dataTime.UnixNano() / 1e6 // 只有存在两个坐标点的时候,才能去计算线性方程和R平方 if len(coordinateData) >= 2 { a, b := utils.GetLinearResult(coordinateData) if !math.IsNaN(a) && !math.IsNaN(b) && !math.IsInf(a, 0) && !math.IsInf(b, 0) { if getAData { a, _ = decimal.NewFromFloat(a).Round(4).Float64() dataResp.AData.DataList = append(dataResp.AData.DataList, models.EdbDataList{ EdbDataId: i, EdbInfoId: 0, DataTime: date, DataTimestamp: timestamp, Value: a, }) if !isNotAFirst { dataResp.AData.MinData = a dataResp.AData.MaxData = a isNotAFirst = true } if dataResp.AData.MinData > a { dataResp.AData.MinData = a } if dataResp.AData.MaxData < a { dataResp.AData.MaxData = a } } if getBData { b, _ = decimal.NewFromFloat(b).Round(4).Float64() dataResp.BData.DataList = append(dataResp.BData.DataList, models.EdbDataList{ EdbDataId: i, EdbInfoId: 0, DataTime: date, DataTimestamp: timestamp, Value: b, }) if !isNotBFirst { dataResp.BData.MinData = b dataResp.BData.MaxData = b isNotBFirst = true } if dataResp.BData.MinData > b { dataResp.BData.MinData = b } if dataResp.BData.MaxData < b { dataResp.BData.MaxData = b } } } // 计算R平方 if getR2Data { tmpVal := utils.CalculationDecisive(coordinateData) if math.IsNaN(tmpVal) || math.IsInf(tmpVal, 0) { continue } tmpVal, _ = decimal.NewFromFloat(tmpVal).Round(4).Float64() dataResp.R2Data.DataList = append(dataResp.R2Data.DataList, models.EdbDataList{ EdbDataId: i, EdbInfoId: 0, DataTime: date, DataTimestamp: timestamp, Value: tmpVal, }) if !isNotR2First { dataResp.R2Data.MinData = tmpVal dataResp.R2Data.MaxData = tmpVal isNotR2First = true } if dataResp.R2Data.MinData > tmpVal { dataResp.R2Data.MinData = tmpVal } if dataResp.R2Data.MaxData < tmpVal { dataResp.R2Data.MaxData = tmpVal } } } } dataResp.AData.LatestDate = baseEdbInfo.LatestDate dataResp.AData.EdbInfoCategoryType = baseEdbInfo.EdbInfoCategoryType dataResp.BData.LatestDate = baseEdbInfo.LatestDate dataResp.BData.EdbInfoCategoryType = baseEdbInfo.EdbInfoCategoryType dataResp.R2Data.LatestDate = baseEdbInfo.LatestDate dataResp.R2Data.EdbInfoCategoryType = baseEdbInfo.EdbInfoCategoryType return } // getConfigData 获取配置数据 func getConfigData(lineChartInfoConfig request.LineChartInfoReq) (mappingList []*models.ChartEdbInfoMapping, startDate, endDate string, err error, errMsg string) { dateType := lineChartInfoConfig.DateType switch dateType { case 1: startDate = "2000-01-01" case 2: startDate = "2010-01-01" case 3: startDate = "2015-01-01" case 4: //startDate = strconv.Itoa(time.Now().Year()) + "-01-01" startDate = "2021-01-01" case 5: startDate = lineChartInfoConfig.StartDate + "-01" endDate = lineChartInfoConfig.EndDate + "-01" case 6: startDate = lineChartInfoConfig.StartDate + "-01" case 7: startDate = "2018-01-01" case 8: startDate = "2019-01-01" case 9: startDate = "2020-01-01" case 11: startDate = "2022-01-01" } //指标数据 edbInfoIdList := make([]int, 0) { edbInfoIdMap := make(map[int]int, 0) for _, edbInfoId := range lineChartInfoConfig.XEdbInfoIdList { edbInfoIdMap[edbInfoId] = edbInfoId } for _, edbInfoId := range lineChartInfoConfig.YEdbInfoIdList { edbInfoIdMap[edbInfoId] = edbInfoId } for _, edbInfoId := range edbInfoIdMap { edbInfoIdList = append(edbInfoIdList, edbInfoId) } } mappingList, err = models.GetChartEdbMappingListByEdbInfoIdList(edbInfoIdList) if err != nil { errMsg = `获取失败` err = errors.New("获取图表,指标信息失败,Err:" + err.Error()) return } return } // handleData 数据处理 func handleData(baseEdbInfoId int, edbDataListMap map[int][]*models.EdbDataList) (dateEdbMap map[string]map[int]float64, err error) { dateEdbMap = make(map[string]map[int]float64) // [日期][A指标id:值,B指标id:值] for edbInfoId, edbDataList := range edbDataListMap { if edbInfoId != baseEdbInfoId { // 用上期的数据补充当期的数据处理 handleDataMap := make(map[string]float64) err = models.HandleDataByPreviousData(edbDataList, handleDataMap) if err != nil { return } for date, val := range handleDataMap { item, ok := dateEdbMap[date] if ok { item[edbInfoId] = val } else { item = map[int]float64{ edbInfoId: val, } } dateEdbMap[date] = item } } else { for _, edbData := range edbDataList { item, ok := dateEdbMap[edbData.DataTime] if ok { item[edbInfoId] = edbData.Value } else { item = map[int]float64{ edbInfoId: edbData.Value, } } dateEdbMap[edbData.DataTime] = item } } } return }