edb_data_residual_analysis.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. package models
  2. import (
  3. "encoding/json"
  4. "eta/eta_index_lib/utils"
  5. "fmt"
  6. "github.com/beego/beego/v2/client/orm"
  7. "math"
  8. "sort"
  9. "strconv"
  10. )
  11. // RefreshAllCalculateResidualAnalysis 刷新残差分析
  12. func RefreshAllCalculateResidualAnalysis(edbInfoId, source, subSource, formulaInt, moveType int, fromEdbInfo *EdbInfo, edbCode, startDate, endDate, moveFrequency string) (err error) {
  13. o := orm.NewOrm()
  14. to, err := o.Begin()
  15. if err != nil {
  16. return
  17. }
  18. defer func() {
  19. if err != nil {
  20. fmt.Println("RefreshAllCalculateResidualAnalysis,Err:" + err.Error())
  21. _ = to.Rollback()
  22. } else {
  23. _ = to.Commit()
  24. }
  25. }()
  26. //清空原有数据
  27. sql := ` DELETE FROM edb_data_residual_analysis WHERE edb_info_id = ? `
  28. _, err = to.Raw(sql, edbInfoId).Exec()
  29. if err != nil {
  30. return
  31. }
  32. //计算数据
  33. err = refreshAllCalculateResidualAnalysis(to, edbInfoId, source, subSource, formulaInt, moveType, fromEdbInfo, edbCode, startDate, endDate, moveFrequency)
  34. return
  35. }
  36. // refreshAllCalculateResidualAnalysis 刷新所有残差分析
  37. func refreshAllCalculateResidualAnalysis(to orm.TxOrmer, edbInfoId, source, subSource, formulaInt, moveType int, fromEdbInfo *EdbInfo, edbCode, startDate, endDate, moveFrequency string) (err error) {
  38. fmt.Println("refreshAllCalculateResidualAnalysis startDate:", startDate)
  39. //计算数据
  40. /*dataList, err := GetEdbDataListAllByTo(to, fromEdbInfo.Source, fromEdbInfo.SubSource, FindEdbDataListAllCond{
  41. EdbInfoId: fromEdbInfo.EdbInfoId,
  42. StartDataTime: startDate,
  43. StartDataTimeCond: ">=",
  44. }, 0)
  45. if err != nil {
  46. return err
  47. }*/
  48. calculateMappingList, err := GetCalculateMappingListByEdbInfoId(edbInfoId)
  49. if err != nil {
  50. return err
  51. }
  52. edbInfoIdA := calculateMappingList[0].FromEdbInfoId
  53. edbInfoIdB := calculateMappingList[1].FromEdbInfoId
  54. mappingList, err := GetEdbInfoListByIds([]int{edbInfoIdA, edbInfoIdB})
  55. if err != nil {
  56. return err
  57. }
  58. var edbInfoMappingA, edbInfoMappingB *EdbInfoList
  59. for _, v := range mappingList {
  60. if v.Unit == "无" {
  61. v.Unit = ""
  62. }
  63. if v.EdbInfoId == edbInfoIdA {
  64. edbInfoMappingA = v
  65. }
  66. if v.EdbInfoId == edbInfoIdB {
  67. edbInfoMappingB = v
  68. }
  69. }
  70. if edbInfoMappingA == nil {
  71. return fmt.Errorf("指标A不存在")
  72. }
  73. if edbInfoMappingB == nil {
  74. return fmt.Errorf("指标B不存在")
  75. }
  76. // 从配置中取出时间范围
  77. analysisConfig, err := GetResidualAnalysisConfigById(edbInfoId)
  78. if err != nil {
  79. return err
  80. }
  81. configString := analysisConfig.Config
  82. var config ResidualAnalysisConfig
  83. err = json.Unmarshal([]byte(configString), &config)
  84. // 时间处理
  85. switch config.DateType {
  86. case 0:
  87. startDate = config.StartDate
  88. endDate = config.EndDate
  89. case 1:
  90. startDate = config.StartDate
  91. endDate = ""
  92. default:
  93. startDate = utils.GetPreYearTime(config.DateType)
  94. endDate = ""
  95. }
  96. // 原始图表信息
  97. originalEdbList := make([]EdbInfoList, 0)
  98. originalEdbList, fullADataList, fullBDataList, err := fillOriginalChart(config, fromEdbInfo, mappingList, startDate, endDate, edbInfoMappingA, edbInfoMappingB, originalEdbList)
  99. if err != nil {
  100. return err
  101. }
  102. indexADataMap := map[string]*EdbData{}
  103. for _, indexData := range edbInfoMappingA.DataList {
  104. indexADataMap[indexData.DataTime] = indexData
  105. }
  106. // 映射图表信息
  107. mappingEdbList, _, _, _, err := fillMappingChartInfo(config, fromEdbInfo, edbInfoMappingA, edbInfoMappingB, originalEdbList, indexADataMap, startDate, endDate, fullADataList, fullBDataList)
  108. if err != nil {
  109. return err
  110. }
  111. mappingChartInfo := createChartInfoResp(req, startDate, endDate, edbInfoMappingA.EdbName+"与"+edbInfoMappingB.EdbName+"映射"+edbInfoMappingA.EdbName)
  112. resp.MappingChartData = residual_analysis_model.ChartResp{
  113. ChartInfo: mappingChartInfo,
  114. EdbInfoList: mappingEdbList,
  115. }
  116. // 残差图表信息
  117. residualEdbList, R2, err := fillResidualChartInfo(req, edbInfoMappingA, edbInfoMappingB, mappingEdbList)
  118. if err != nil {
  119. return residual_analysis_model.ResidualAnalysisResp{}, err
  120. }
  121. residualChartInfo := createChartInfoResp(req, startDate, endDate, edbInfoMappingA.EdbName+"与"+edbInfoMappingA.EdbName+"映射残差/"+edbInfoMappingB.EdbName)
  122. resp.ResidualChartData = residual_analysis_model.ChartResp{
  123. ChartInfo: residualChartInfo,
  124. EdbInfoList: residualEdbList,
  125. }
  126. return
  127. }
  128. func fillMappingChartInfo(config ResidualAnalysisConfig, req *EdbInfo, edbInfoMappingA *EdbInfoList, edbInfoMappingB *EdbInfoList, originalEdbList []EdbInfoList, indexADataMap map[string]*EdbData, startDate string, endDate string, fullADataList []*EdbDataList, fullBDataList []*EdbDataList) ([]EdbInfoList, float64, float64, float64, error) {
  129. // 计算公式:Y=aX+b,Y为映射后的指标,X为自变量指标
  130. // 正序:a=(L2-L1)/(R2-R1) b=L2-R2*a
  131. // 逆序:a=(L2-L1)/(R1-R2) b=L2-R1*a
  132. // L2:左轴下限 R2:右轴上限 L1:左轴上限 R1:右轴下限
  133. var a, b, r float64
  134. // 映射残差 计算a,b
  135. if config.ResidualType == 1 {
  136. if config.IsOrder {
  137. a = (config.LeftIndexMax - config.LeftIndexMin) / (config.RightIndexMin - config.RightIndexMax)
  138. b = config.LeftIndexMax - config.RightIndexMin*a
  139. } else {
  140. a = (config.LeftIndexMax - config.LeftIndexMin) / (config.RightIndexMax - config.RightIndexMin)
  141. b = config.LeftIndexMax - config.RightIndexMax*a
  142. }
  143. }
  144. dataList := edbInfoMappingB.DataList
  145. // 指标B数据补充
  146. // 新建一个切片来保存补充的数据
  147. var replenishDataList []*EdbData
  148. for index := 0; index < len(dataList)-1; index++ {
  149. // 获取当前数据和下一个数据
  150. beforeIndexData := dataList[index]
  151. afterIndexData := dataList[index+1]
  152. // 从最早时间开始,补充时间为自然日
  153. for utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
  154. // 创建补充数据
  155. nextDay := utils.GetNextDay(beforeIndexData.DataTime)
  156. toTime := utils.StringToTime(nextDay)
  157. replenishIndexData := EdbData{
  158. DataTime: nextDay, // 计算下一个自然日
  159. DataTimestamp: toTime.UnixMilli(),
  160. Value: beforeIndexData.Value, // 可以选择使用前一天的值,或者其他逻辑来计算值
  161. }
  162. // 将补充数据加入补充数据列表
  163. replenishDataList = append(replenishDataList, &replenishIndexData)
  164. // 更新 beforeIndexData 为新创建的补充数据
  165. beforeIndexData = &replenishIndexData
  166. }
  167. }
  168. // 将补充数据插入原始数据列表
  169. dataList = append(dataList, replenishDataList...)
  170. // 排序
  171. sort.Sort(ByDataTime(dataList))
  172. // 拟合残差 计算a,b
  173. var coordinateList []utils.Coordinate
  174. var replenishADataList []*EdbDataList
  175. var replenishBDataList []*EdbDataList
  176. if config.ResidualType == 2 {
  177. //
  178. // 因变量指标也转换为日度
  179. for index := 0; index < len(fullADataList)-1; index++ {
  180. // 获取当前数据和下一个数据
  181. beforeIndexData := fullADataList[index]
  182. afterIndexData := fullADataList[index+1]
  183. replenishADataList = append(replenishADataList, beforeIndexData)
  184. // 从最早时间开始,补充时间为自然日
  185. if utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
  186. for {
  187. // 创建补充数据
  188. nextDay := utils.GetNextDay(beforeIndexData.DataTime)
  189. toTime := utils.StringToTime(nextDay)
  190. replenishIndexData := EdbDataList{
  191. DataTime: nextDay, // 计算下一个自然日
  192. DataTimestamp: toTime.UnixMilli(),
  193. Value: beforeIndexData.Value, // 可以选择使用前一天的值,或者其他逻辑来计算值
  194. }
  195. // 将补充数据加入补充数据列表
  196. replenishADataList = append(replenishADataList, &replenishIndexData)
  197. // 更新 beforeIndexData 为新创建的补充数据
  198. beforeIndexData = &replenishIndexData
  199. // 检查是否还需要继续补充数据
  200. if !utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
  201. break
  202. }
  203. }
  204. }
  205. }
  206. replenishADataList = append(replenishADataList, fullADataList[len(fullADataList)-1])
  207. // 自变量指标也转换为日度
  208. for index := 0; index < len(fullBDataList)-1; index++ {
  209. // 获取当前数据和下一个数据
  210. beforeIndexData := fullBDataList[index]
  211. afterIndexData := fullBDataList[index+1]
  212. replenishBDataList = append(replenishBDataList, beforeIndexData)
  213. // 从最早时间开始,补充时间为自然日
  214. if utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
  215. for {
  216. // 创建补充数据
  217. nextDay := utils.GetNextDay(beforeIndexData.DataTime)
  218. toTime := utils.StringToTime(nextDay)
  219. replenishIndexData := EdbDataList{
  220. DataTime: nextDay, // 计算下一个自然日
  221. DataTimestamp: toTime.UnixMilli(),
  222. Value: beforeIndexData.Value, // 可以选择使用前一天的值,或者其他逻辑来计算值
  223. }
  224. // 将补充数据加入补充数据列表
  225. replenishBDataList = append(replenishBDataList, &replenishIndexData)
  226. // 更新 beforeIndexData 为新创建的补充数据
  227. beforeIndexData = &replenishIndexData
  228. // 检查是否还需要继续补充数据
  229. if !utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
  230. break
  231. }
  232. }
  233. }
  234. }
  235. replenishBDataList = append(replenishBDataList, fullBDataList[len(fullBDataList)-1])
  236. // replenishADataList --> map
  237. replenishADataMap := make(map[string]*EdbDataList)
  238. for _, indexData := range replenishADataList {
  239. if (utils.StringToTime(indexData.DataTime).After(utils.StringToTime(startDate)) || utils.StringToTime(indexData.DataTime).Equal(utils.StringToTime(startDate))) && (endDate == "" || utils.StringToTime(indexData.DataTime).Before(utils.StringToTime(endDate))) {
  240. replenishADataMap[indexData.DataTime] = indexData
  241. }
  242. }
  243. for _, indexData := range replenishBDataList {
  244. if _, ok := replenishADataMap[indexData.DataTime]; ok {
  245. coordinate := utils.Coordinate{
  246. X: indexData.Value,
  247. Y: replenishADataMap[indexData.DataTime].Value,
  248. }
  249. coordinateList = append(coordinateList, coordinate)
  250. }
  251. }
  252. a, b = utils.GetLinearResult(coordinateList)
  253. r = utils.ComputeCorrelation(coordinateList)
  254. }
  255. // 填充映射指标值 使得时间长度一致
  256. dataList = FillDataBList(dataList, edbInfoMappingA)
  257. // 根据指标A的时间key,在B的映射指标中筛选出对应的值
  258. var dataBList []*EdbDataList
  259. var indexMax, indexMin string
  260. if len(dataList) > 0 {
  261. indexMax = dataList[0].Value
  262. indexMin = dataList[0].Value
  263. for _, indexData := range dataList {
  264. if _, ok := indexADataMap[indexData.DataTime]; ok {
  265. indexDataCopy := *indexData
  266. // 计算指标B映射值
  267. f, _ := strconv.ParseFloat(indexData.Value, 64)
  268. indexDataCopy.Value = fmt.Sprintf("%f", math.Round((a*f+b)*10000)/10000)
  269. // 比较最大值
  270. if indexData.Value > indexMax {
  271. indexMax = indexData.Value
  272. }
  273. // 比较最小值
  274. if indexData.Value < indexMin {
  275. indexMin = indexData.Value
  276. }
  277. // 将副本添加到 dataBList
  278. copyValue, _ := strconv.ParseFloat(indexDataCopy.Value, 64)
  279. dataBList = append(dataBList, &EdbDataList{
  280. DataTime: indexDataCopy.DataTime,
  281. DataTimestamp: indexDataCopy.DataTimestamp,
  282. EdbDataId: indexDataCopy.EdbDataId,
  283. EdbInfoId: indexDataCopy.EdbInfoId,
  284. Value: copyValue,
  285. })
  286. }
  287. }
  288. }
  289. mappingEdbList := make([]EdbInfoList, len(originalEdbList))
  290. copy(mappingEdbList, originalEdbList)
  291. for i, mapping := range mappingEdbList {
  292. if mapping.EdbInfoId != edbInfoMappingA.EdbInfoId {
  293. mappingEdbList[i].EdbInfoId = 0
  294. mappingEdbList[i].EdbCode = ""
  295. mappingEdbList[i].EdbName = edbInfoMappingB.EdbName + "映射" + edbInfoMappingA.EdbName
  296. if config.IndexType == 2 {
  297. if config.LeadValue > 0 {
  298. mappingEdbList[i].EdbName = edbInfoMappingB.EdbName + "映射" + edbInfoMappingA.EdbName + "(领先" + strconv.Itoa(config.LeadValue) + config.LeadFrequency + ")"
  299. }
  300. }
  301. mappingEdbList[i].DataList = dataBList
  302. }
  303. }
  304. return mappingEdbList, a, b, r, nil
  305. }
  306. // FillDataBList 填充B的数据 使得与A的时间保持一致
  307. func FillDataBList(dataList []*EdbData, edbInfoMappingA *EdbInfoList) []*EdbData {
  308. dataAList := edbInfoMappingA.DataList
  309. for utils.StringToTime(dataList[len(dataList)-1].DataTime).Before(utils.StringToTime(dataAList[len(dataAList)-1].DataTime)) {
  310. // 使用A的时间填充时间差
  311. timeDiff := utils.GetNextDayN(dataList[len(dataList)-1].DataTime, 1)
  312. // 创建新的数据点并填充 前值填充
  313. newDataPoint := &EdbData{
  314. DataTime: timeDiff,
  315. Value: dataList[len(dataList)-1].Value,
  316. DataTimestamp: utils.StringToTime(timeDiff).UnixMilli(),
  317. }
  318. // 将新数据点添加到dataList末尾
  319. dataList = append(dataList, newDataPoint)
  320. }
  321. return dataList
  322. }
  323. func fillOriginalChart(config ResidualAnalysisConfig, req *EdbInfo, mappingList []*EdbInfoList, startDate string, endDate string, edbInfoMappingA *EdbInfoList, edbInfoMappingB *EdbInfoList, originalEdbList []EdbInfoList) ([]EdbInfoList, []*EdbDataList, []*EdbDataList, error) {
  324. var fullADataList, fullBDataList []*EdbDataList
  325. for _, v := range mappingList {
  326. var edbInfoMapping EdbInfoList
  327. edbInfoMapping.EdbName = v.EdbName
  328. // 获取图表中的指标数据
  329. dataList, err := GetEdbDataList(v.Source, v.SubSource, v.EdbInfoId, startDate, endDate)
  330. if err != nil {
  331. return nil, nil, nil, fmt.Errorf("获取指标数据失败,Err:%s", err.Error())
  332. }
  333. data := convertEdbDataListToEdbData(dataList)
  334. // 重新获取指标数据 产品要求需要和计算指标-拟合残差逻辑保持一致
  335. fullDataList, err := GetEdbDataList(v.Source, v.SubSource, v.EdbInfoId, "", "")
  336. if err != nil {
  337. return nil, nil, nil, fmt.Errorf("获取指标数据失败,Err:%s", err.Error())
  338. }
  339. if v.EdbInfoId == edbInfoMappingB.EdbInfoId {
  340. // 领先指标 dataList进行数据处理
  341. if config.IndexType == 1 {
  342. if config.LeadValue < 0 {
  343. return nil, nil, nil, fmt.Errorf("领先值不能小于0")
  344. } else if config.LeadValue > 0 {
  345. edbInfoMapping.EdbName = v.EdbName + "(领先" + strconv.Itoa(config.LeadValue) + config.LeadFrequency + ")"
  346. for _, indexData := range dataList {
  347. switch config.LeadFrequency {
  348. case "天":
  349. indexData.DataTime = utils.GetNextDayN(indexData.DataTime, config.LeadValue)
  350. case "周":
  351. indexData.DataTime = utils.GetNextDayN(indexData.DataTime, config.LeadValue*7)
  352. case "月":
  353. indexData.DataTime = utils.TimeToString(utils.AddDate(utils.StringToTime(indexData.DataTime), 0, config.LeadValue), utils.YearMonthDay)
  354. case "季":
  355. indexData.DataTime = utils.TimeToString(utils.AddDate(utils.StringToTime(indexData.DataTime), 0, config.LeadValue*3), utils.YearMonthDay)
  356. case "年":
  357. indexData.DataTime = utils.TimeToString(utils.AddDate(utils.StringToTime(indexData.DataTime), config.LeadValue, 0), utils.YearMonthDay)
  358. }
  359. indexData.DataTimestamp = utils.StringToTime(indexData.DataTime).UnixMilli()
  360. }
  361. }
  362. }
  363. edbInfoMappingB.DataList = data
  364. fullBDataList = fullDataList
  365. } else {
  366. edbInfoMappingA.DataList = data
  367. fullADataList = fullDataList
  368. }
  369. edbInfoMapping.EdbInfoId = v.EdbInfoId
  370. edbInfoMapping.EdbCode = v.EdbCode
  371. edbInfoMapping.Unit = v.Unit
  372. edbInfoMapping.Frequency = v.Frequency
  373. edbInfoMapping.Source = v.Source
  374. edbInfoMapping.SourceName = v.SourceName
  375. edbInfoMapping.LatestDate = v.LatestDate
  376. edbInfoMapping.LatestValue = v.LatestValue
  377. edbInfoMapping.DataList = data
  378. originalEdbList = append(originalEdbList, edbInfoMapping)
  379. }
  380. return originalEdbList, fullADataList, fullBDataList, nil
  381. }
  382. func convertEdbDataListToEdbData(edbDataLists []*EdbDataList) []*EdbData {
  383. var edbDataList []*EdbData
  384. for _, edbData := range edbDataLists {
  385. data := &EdbData{
  386. DataTime: edbData.DataTime,
  387. DataTimestamp: edbData.DataTimestamp,
  388. EdbDataId: edbData.EdbDataId,
  389. EdbInfoId: edbData.EdbInfoId,
  390. Value: fmt.Sprintf("%f", edbData.Value),
  391. }
  392. edbDataList = append(edbDataList, data)
  393. }
  394. return edbDataList
  395. }
  396. type ByDataTime []*EdbData
  397. func (a ByDataTime) Len() int {
  398. return len(a)
  399. }
  400. func (a ByDataTime) Swap(i, j int) {
  401. a[i], a[j] = a[j], a[i]
  402. }
  403. func (a ByDataTime) Less(i, j int) bool {
  404. t1 := utils.StringToTime(a[i].DataTime)
  405. t2 := utils.StringToTime(a[j].DataTime)
  406. return t1.Before(t2)
  407. }