package line_feature import ( "errors" "eta/eta_chart_lib/models" "eta/eta_chart_lib/models/data_manage/line_feature/response" "eta/eta_chart_lib/services/data" "eta/eta_chart_lib/utils" "github.com/shopspring/decimal" "time" ) // GetStandardDeviationData 获取标准差图表的指标数据 func GetStandardDeviationData(chartInfoId int, startDate, endDate string, mappingInfo *models.ChartEdbInfoMapping, calculateValue int) (edbList []*models.ChartEdbInfoMapping, dataResp response.LineFeatureDataResp, err error, errMsg string) { edbList = make([]*models.ChartEdbInfoMapping, 0) // 指标对应的所有数据 _, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, startDate, endDate, []*models.ChartEdbInfoMapping{mappingInfo}, "") if err != nil { return } if len(edbList) != 1 { errMsg = `指标异常` err = errors.New(errMsg) return } edb := edbList[0] dataList := edb.DataList.([]*models.EdbDataList) newDataList := make([]models.EdbDataList, 0) lenData := len(dataList) var minVal, maxVal float64 if lenData >= calculateValue { tmpDataList := make([]float64, 0) for _, tmpData := range dataList { tmpDataList = append(tmpDataList, tmpData.Value) } for i := calculateValue; i <= lenData; i++ { tmpV := utils.CalculateStandardDeviation(tmpDataList[i-calculateValue : i]) tmpV, _ = decimal.NewFromFloat(tmpV).Round(4).Float64() newDataList = append(newDataList, models.EdbDataList{ EdbDataId: i, EdbInfoId: edb.EdbInfoId, DataTime: dataList[i-1].DataTime, DataTimestamp: dataList[i-1].DataTimestamp, Value: tmpV, }) if tmpV > maxVal { maxVal = tmpV } if tmpV < minVal { minVal = tmpV } } } dataResp = response.LineFeatureDataResp{ MaxData: maxVal, MinData: minVal, LatestDate: edb.LatestDate, EdbInfoCategoryType: edb.EdbInfoCategoryType, ChartColor: `#00F`, ChartStyle: `spline`, PredictChartColor: `#00F`, ChartType: 0, ChartWidth: 3, EdbName: "标准差", EdbNameEn: "standard deviation", Unit: edb.Unit, UnitEn: edb.UnitEn, IsAxis: 1, DataList: newDataList, } return } // GetPercentileData 获取百分位图表的指标数据 func GetPercentileData(chartInfoId int, startDate, endDate string, mappingInfo *models.ChartEdbInfoMapping, calculateValue int, calculateUnit string, percentType int) (edbList []*models.ChartEdbInfoMapping, dataResp response.LineFeatureDataResp, err error, errMsg string) { edbList = make([]*models.ChartEdbInfoMapping, 0) moveUnitDays, ok := utils.FrequencyDaysMap[calculateUnit] if !ok { errMsg = `错误的周期` err = errors.New(errMsg) return } calculateDay := calculateValue * moveUnitDays // 指标对应的所有数据 _, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, startDate, endDate, []*models.ChartEdbInfoMapping{mappingInfo}, "") if err != nil { return } if len(edbList) != 1 { errMsg = `指标异常` err = errors.New(errMsg) return } edb := edbList[0] dataList := edb.DataList.([]*models.EdbDataList) newDataList := make([]models.EdbDataList, 0) var edbMinVal, edbMaxVal float64 dataMap := make(map[time.Time]float64, 0) for _, tmpData := range dataList { currDateTime, _ := time.ParseInLocation(utils.FormatDate, tmpData.DataTime, time.Local) dataMap[currDateTime] = tmpData.Value } //百分位:对所选指标滚动地取对应时间长度的数据值,取最大值Max,最小值Min,计算Max-Min,百分位=(现值-Min)/(Max-Min),Max=Min时不予计算。 if percentType == utils.PercentCalculateTypeRange { for i, tmpData := range dataList { currDateTime, _ := time.ParseInLocation(utils.FormatDate, tmpData.DataTime, time.Local) maxVal := tmpData.Value minVal := tmpData.Value for k := 0; k < calculateDay; k++ { preVal, ok2 := dataMap[currDateTime.AddDate(0, 0, -k)] if ok2 { if preVal > maxVal { maxVal = preVal } if preVal < minVal { minVal = preVal } } } if maxVal == minVal { continue } //百分位=(现值-Min)/(Max-Min) tmpV := (tmpData.Value - minVal) / (maxVal - minVal) * 100 tmpV, _ = decimal.NewFromFloat(tmpV).Round(4).Float64() newDataList = append(newDataList, models.EdbDataList{ EdbDataId: i, EdbInfoId: edb.EdbInfoId, DataTime: dataList[i].DataTime, DataTimestamp: dataList[i].DataTimestamp, Value: tmpV, }) if tmpV < edbMinVal { edbMinVal = tmpV } if tmpV > edbMaxVal { edbMaxVal = tmpV } } } // 百分位数据个数算法 // 数据区间第一个和最后一个数据点的时间和数据分别为(T1,S1)(T2,S2); N=T1到T2指标数据个数, n=小于等于S2的数据个数 // 个数百分位=(n-1)/(N-1) if percentType == utils.PercentCalculateTypeNum { for i, d := range dataList { // T2为当前日期 s2 := decimal.NewFromFloat(d.Value) t2, _ := time.ParseInLocation(utils.FormatDate, d.DataTime, time.Local) // 计算N和n var bigN, tinyN int for k := 0; k < calculateDay; k++ { preVal, preOk := dataMap[t2.AddDate(0, 0, -k)] if !preOk { continue } bigN += 1 if decimal.NewFromFloat(preVal).LessThanOrEqual(s2) { tinyN += 1 } } // N=1时说明计算无效 if bigN == 1 { continue } numerator := decimal.NewFromInt(int64(tinyN - 1)) denominator := decimal.NewFromInt(int64(bigN - 1)) percentVal, _ := numerator.Div(denominator).Round(4).Float64() // 写进数组并判断指标最大最小值 newDataList = append(newDataList, models.EdbDataList{ EdbDataId: i, EdbInfoId: edb.EdbInfoId, DataTime: dataList[i].DataTime, DataTimestamp: dataList[i].DataTimestamp, Value: percentVal, }) if percentVal < edbMinVal { edbMinVal = percentVal } if percentVal > edbMaxVal { edbMaxVal = percentVal } } } dataResp = response.LineFeatureDataResp{ MaxData: edbMaxVal, MinData: edbMinVal, LatestDate: edb.LatestDate, EdbInfoCategoryType: edb.EdbInfoCategoryType, ChartColor: `#00F`, ChartStyle: `spline`, PredictChartColor: `#00F`, ChartType: 0, ChartWidth: 3, EdbName: "百分位", EdbNameEn: "percentile", Unit: "%", UnitEn: "%", IsAxis: 1, DataList: newDataList, } return } // GetFrequencyDistributionData 获取频率分布的图表数据 func GetFrequencyDistributionData(chartInfoId int, mappingInfo *models.ChartEdbInfoMapping, dateType, stepVal int, startDate, endDate string) (edbList []*models.ChartEdbInfoMapping, dataResp response.FrequencyDistributionResp, err error, errMsg string) { XDataList := make([]float64, 0) // 频度 Y1DataList := make([]response.FrequencyDistributionYData, 0) // 累计频率 Y2DataList := make([]response.FrequencyDistributionYData, 0) edbList = make([]*models.ChartEdbInfoMapping, 0) // 指标对应的所有数据 _, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, startDate, endDate, []*models.ChartEdbInfoMapping{mappingInfo}, "") if err != nil { return } if len(edbList) != 1 { err = errors.New("指标异常") return } startDateTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local) var endDateTime time.Time if endDate != `` { endDateTime, _ = time.ParseInLocation(utils.FormatDate, endDate, time.Local) } edb := edbList[0] dataList := edb.DataList.([]*models.EdbDataList) if len(dataList) <= 0 { return } // 非自定义 if dateType != 8 { endDate = dataList[len(dataList)-1].DataTime endDateTime, err = time.ParseInLocation(utils.FormatDate, endDate, time.Local) if err != nil { return } //日期类型:1:最近3月;2:最近6月;3:最近1年;4:最近2年;5:最近3年;6:最近5年;7:最近10年,8:自定义时间 startDateTime = utils.GetDateByDateType2(dateType, endDateTime) startDate = startDateTime.Format(utils.FormatDate) newDataList := make([]*models.EdbDataList, 0) for _, v := range dataList { tmpDataTime, tmpErr := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local) if tmpErr != nil { err = tmpErr return } if tmpDataTime.Equal(startDateTime) || tmpDataTime.After(startDateTime) { newDataList = append(newDataList, v) } } dataList = newDataList } maxVal := dataList[0].Value minVal := dataList[0].Value dataValMap := make(map[float64]int) total := 0 // 数据总量 for _, tmpData := range dataList { currDateTime, _ := time.ParseInLocation(utils.FormatDate, tmpData.DataTime, time.Local) if (currDateTime.Equal(startDateTime) || currDateTime.After(startDateTime)) && (endDateTime.IsZero() || currDateTime.Before(endDateTime)) { if maxVal < tmpData.Value { maxVal = tmpData.Value } if minVal > tmpData.Value { minVal = tmpData.Value } num, ok := dataValMap[tmpData.Value] if ok { dataValMap[tmpData.Value] = num + 1 } else { dataValMap[tmpData.Value] = 1 } total++ } } if total <= 0 { errMsg = `没有数据` err = errors.New(errMsg) return } // 最大最小值 向上/下取整 minVal = utils.GetFloorNewNum(minVal, 2) maxVal = utils.GetCeilNewNum(maxVal, 2) //间距 spacing, _ := (decimal.NewFromFloat(maxVal).Sub(decimal.NewFromFloat(minVal))).Div(decimal.NewFromInt(int64(stepVal))).Float64() distributionDataNumMap := make(map[float64]int) for i := 1; i <= stepVal; i++ { tmpMinVal, _ := decimal.NewFromFloat(minVal).Add((decimal.NewFromFloat(spacing)).Mul(decimal.NewFromInt(int64(i - 1)))).Float64() tmpMaxVal, _ := decimal.NewFromFloat(minVal).Add((decimal.NewFromFloat(spacing)).Mul(decimal.NewFromInt(int64(i)))).Float64() XDataList = append(XDataList, tmpMinVal) distributionDataNumMap[tmpMinVal] = 0 for tmpVal, num := range dataValMap { if tmpMinVal <= tmpVal { // 最后一期数据是要小于等于 if i == stepVal { if tmpVal <= tmpMaxVal { distributionDataNumMap[tmpMinVal] += num } } else { if tmpVal < tmpMaxVal { distributionDataNumMap[tmpMinVal] += num } } } } } var minFrequency, maxFrequency float64 tmpNum := 0 for k, tmpMinVal := range XDataList { // 数量 frequencyYNum := distributionDataNumMap[tmpMinVal] // 频率 tmpFrequency, _ := decimal.NewFromInt(int64(frequencyYNum)).Div(decimal.NewFromInt(int64(total))).Mul(decimal.NewFromInt(100)).Round(4).Float64() Y1DataList = append(Y1DataList, response.FrequencyDistributionYData{ X: tmpMinVal, Y: tmpFrequency, }) if k == 0 { minFrequency = tmpFrequency maxFrequency = tmpFrequency } else { if tmpFrequency < minFrequency { minFrequency = tmpFrequency } if tmpFrequency > maxFrequency { maxFrequency = tmpFrequency } } // 累计数 tmpNum += frequencyYNum // 累计频率 tmpTotalFrequency, _ := decimal.NewFromInt(int64(tmpNum)).Div(decimal.NewFromInt(int64(total))).Mul(decimal.NewFromInt(100)).Round(4).Float64() Y2DataList = append(Y2DataList, response.FrequencyDistributionYData{ X: tmpMinVal, Y: tmpTotalFrequency, }) } newDataList := []response.FrequencyDistributionData{ { Name: "频率", NameEn: "Frequency", Unit: "%", UnitEn: "%", Value: Y1DataList, Color: "#00F", IsAxis: 1, }, { Name: "累计频率", NameEn: "Total Frequency", Unit: "%", UnitEn: "%", Value: Y2DataList, Color: "#F00", IsAxis: 0, }, } edbList[0].DataList = nil dataResp = response.FrequencyDistributionResp{ LeftMinValue: minFrequency, LeftMaxValue: maxFrequency, RightMinValue: 0, RightMaxValue: 100, DataList: newDataList, } return }