chart_info.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. package correlation
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "github.com/shopspring/decimal"
  7. "hongze/hongze_yb/models/response/chart_info"
  8. "hongze/hongze_yb/models/tables/chart_edb_mapping"
  9. "hongze/hongze_yb/models/tables/chart_info_correlation"
  10. "hongze/hongze_yb/models/tables/edb_data"
  11. "hongze/hongze_yb/services/alarm_msg"
  12. "hongze/hongze_yb/services/chart"
  13. "hongze/hongze_yb/utils"
  14. "math"
  15. "time"
  16. )
  17. // HandleDataByLinearRegression 线性方程插值法补全数据
  18. func HandleDataByLinearRegression(originList []*edb_data.EdbDataList, handleDataMap map[string]float64) (newList []*edb_data.EdbDataList, err error) {
  19. if len(originList) < 2 {
  20. return
  21. }
  22. var startEdbInfoData *edb_data.EdbDataList
  23. for _, v := range originList {
  24. handleDataMap[v.DataTime] = v.Value
  25. // 第一个数据就给过滤了,给后面的试用
  26. if startEdbInfoData == nil {
  27. startEdbInfoData = v
  28. newList = append(newList, &edb_data.EdbDataList{
  29. DataTime: v.DataTime,
  30. Value: v.Value,
  31. })
  32. continue
  33. }
  34. // 获取两条数据之间相差的天数
  35. startDataTime, _ := time.ParseInLocation(utils.FormatDate, startEdbInfoData.DataTime, time.Local)
  36. currDataTime, _ := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
  37. betweenHour := int(currDataTime.Sub(startDataTime).Hours())
  38. betweenDay := betweenHour / 24
  39. // 如果相差一天,那么过滤
  40. if betweenDay <= 1 {
  41. startEdbInfoData = v
  42. newList = append(newList, &edb_data.EdbDataList{
  43. DataTime: v.DataTime,
  44. Value: v.Value,
  45. })
  46. continue
  47. }
  48. // 生成线性方程式
  49. var a, b float64
  50. {
  51. coordinateData := make([]utils.Coordinate, 0)
  52. tmpCoordinate1 := utils.Coordinate{
  53. X: 1,
  54. Y: startEdbInfoData.Value,
  55. }
  56. coordinateData = append(coordinateData, tmpCoordinate1)
  57. tmpCoordinate2 := utils.Coordinate{
  58. X: float64(betweenDay) + 1,
  59. Y: v.Value,
  60. }
  61. coordinateData = append(coordinateData, tmpCoordinate2)
  62. a, b = utils.GetLinearResult(coordinateData)
  63. if math.IsNaN(a) || math.IsNaN(b) {
  64. err = fmt.Errorf("线性方程公式生成失败")
  65. return
  66. }
  67. }
  68. // 生成对应的值
  69. {
  70. for i := 1; i < betweenDay; i++ {
  71. tmpDataTime := startDataTime.AddDate(0, 0, i)
  72. aDecimal := decimal.NewFromFloat(a)
  73. xDecimal := decimal.NewFromInt(int64(i) + 1)
  74. bDecimal := decimal.NewFromFloat(b)
  75. val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).Round(4).Float64()
  76. handleDataMap[tmpDataTime.Format(utils.FormatDate)] = val
  77. newList = append(newList, &edb_data.EdbDataList{
  78. DataTime: tmpDataTime.Format(utils.FormatDate),
  79. Value: val,
  80. })
  81. }
  82. }
  83. startEdbInfoData = v
  84. }
  85. return
  86. }
  87. // MoveDataDaysToNewDataList 平移指标数据生成新的数据序列
  88. func MoveDataDaysToNewDataList(dataList []*edb_data.EdbDataList, moveDay int) (newDataList []edb_data.EdbDataList, dateDataMap map[string]float64) {
  89. dateMap := make(map[time.Time]float64)
  90. var minDate, maxDate time.Time
  91. dateDataMap = make(map[string]float64)
  92. for _, v := range dataList {
  93. currDate, _ := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
  94. if minDate.IsZero() || currDate.Before(minDate) {
  95. minDate = currDate
  96. }
  97. if maxDate.IsZero() || currDate.After(maxDate) {
  98. maxDate = currDate
  99. }
  100. dateMap[currDate] = v.Value
  101. }
  102. // 处理领先、滞后数据
  103. newDateMap := make(map[time.Time]float64)
  104. for currDate, value := range dateMap {
  105. newDate := currDate.AddDate(0, 0, moveDay)
  106. newDateMap[newDate] = value
  107. }
  108. minDate = minDate.AddDate(0, 0, moveDay)
  109. maxDate = maxDate.AddDate(0, 0, moveDay)
  110. // 获取日期相差日
  111. dayNum := utils.GetTimeSubDay(minDate, maxDate)
  112. for i := 0; i <= dayNum; i++ {
  113. currDate := minDate.AddDate(0, 0, i)
  114. tmpValue, ok := newDateMap[currDate]
  115. if !ok {
  116. //找不到数据,那么就用前面的数据吧
  117. if len(newDataList)-1 < 0 {
  118. tmpValue = 0
  119. } else {
  120. tmpValue = newDataList[len(newDataList)-1].Value
  121. }
  122. }
  123. tmpData := edb_data.EdbDataList{
  124. DataTime: currDate.Format(utils.FormatDate),
  125. Value: tmpValue,
  126. }
  127. dateDataMap[tmpData.DataTime] = tmpData.Value
  128. newDataList = append(newDataList, tmpData)
  129. }
  130. return
  131. }
  132. // GetChartEdbInfoFormat 相关性图表-获取指标信息
  133. func GetChartEdbInfoFormat(chartInfoId int, edbInfoMappingA, edbInfoMappingB *chart_edb_mapping.ChartEdbInfoMapping) (edbList []*chart_edb_mapping.ChartEdbInfoMappingList, err error) {
  134. edbList = make([]*chart_edb_mapping.ChartEdbInfoMappingList, 0)
  135. if edbInfoMappingA == nil || edbInfoMappingB == nil {
  136. err = fmt.Errorf("指标信息有误")
  137. return
  138. }
  139. edbInfoMappingA.FrequencyEn = chart.GetFrequencyEn(edbInfoMappingA.Frequency)
  140. if edbInfoMappingA.Unit == `无` {
  141. edbInfoMappingA.Unit = ``
  142. }
  143. if edbInfoMappingB.Unit == `无` {
  144. edbInfoMappingB.Unit = ``
  145. }
  146. if chartInfoId <= 0 {
  147. edbInfoMappingA.IsAxis = 1
  148. edbInfoMappingA.LeadValue = 0
  149. edbInfoMappingA.LeadUnit = ""
  150. edbInfoMappingA.ChartEdbMappingId = 0
  151. edbInfoMappingA.ChartInfoId = 0
  152. edbInfoMappingA.IsOrder = false
  153. edbInfoMappingA.EdbInfoType = 1
  154. edbInfoMappingA.ChartStyle = ""
  155. edbInfoMappingA.ChartColor = ""
  156. edbInfoMappingA.ChartWidth = 0
  157. edbInfoMappingB.IsAxis = 1
  158. edbInfoMappingB.LeadValue = 0
  159. edbInfoMappingB.LeadUnit = ""
  160. edbInfoMappingB.ChartEdbMappingId = 0
  161. edbInfoMappingB.ChartInfoId = 0
  162. edbInfoMappingB.IsOrder = false
  163. edbInfoMappingB.EdbInfoType = 1
  164. edbInfoMappingB.ChartStyle = ""
  165. edbInfoMappingB.ChartColor = ""
  166. edbInfoMappingB.ChartWidth = 0
  167. } else {
  168. edbInfoMappingA.LeadUnitEn = chart.GetLeadUnitEn(edbInfoMappingA.LeadUnit)
  169. edbInfoMappingB.LeadUnitEn = chart.GetLeadUnitEn(edbInfoMappingB.LeadUnit)
  170. }
  171. aList := new(chart_edb_mapping.ChartEdbInfoMappingList)
  172. aList.ChartEdbInfoMapping = *edbInfoMappingA
  173. bList := new(chart_edb_mapping.ChartEdbInfoMappingList)
  174. bList.ChartEdbInfoMapping = *edbInfoMappingB
  175. edbList = append(edbList, aList, bList)
  176. return
  177. }
  178. // GetChartDataByEdbInfo 相关性图表-根据指标信息获取x轴和y轴
  179. func GetChartDataByEdbInfo(edbInfoMappingA, edbInfoMappingB *chart_edb_mapping.ChartEdbInfoMapping, leadValue int, leadUnit, startDate, endDate string) (xEdbIdValue []int, yDataList []chart_info.YData, err error) {
  180. xData := make([]int, 0)
  181. yData := make([]float64, 0)
  182. if leadValue == 0 {
  183. xData = append(xData, 0)
  184. }
  185. if leadValue > 0 {
  186. leadMin := 0 - leadValue
  187. xLen := 2*leadValue + 1
  188. for i := 0; i < xLen; i++ {
  189. n := leadMin + i
  190. xData = append(xData, n)
  191. }
  192. }
  193. //// 2023-03-02 时间序列始终以指标B为基准, 始终是A进行平移
  194. //baseEdbInfo := edbInfoMappingB
  195. //changeEdbInfo := edbInfoMappingA
  196. // 2023-03-17 时间序列始终以指标A为基准, 始终是B进行平移
  197. baseEdbInfo := edbInfoMappingA
  198. changeEdbInfo := edbInfoMappingB
  199. // 获取时间基准指标在时间区间内的值
  200. aDataList := make([]*edb_data.EdbDataList, 0)
  201. switch baseEdbInfo.EdbInfoCategoryType {
  202. case 0:
  203. aDataList, err = edb_data.GetEdbDataList(baseEdbInfo.Source, baseEdbInfo.EdbInfoId, startDate, endDate)
  204. case 1:
  205. _, aDataList, _, _, err, _ = chart.GetPredictDataListByPredictEdbInfoId(baseEdbInfo.EdbInfoId, startDate, endDate, false)
  206. default:
  207. err = errors.New("指标base类型异常")
  208. return
  209. }
  210. // 获取变频指标所有日期的值, 插值法完善数据
  211. bDataList := make([]*edb_data.EdbDataList, 0)
  212. switch changeEdbInfo.EdbInfoCategoryType {
  213. case 0:
  214. bDataList, err = edb_data.GetEdbDataList(changeEdbInfo.Source, changeEdbInfo.EdbInfoId, "", "")
  215. case 1:
  216. _, bDataList, _, _, err, _ = chart.GetPredictDataListByPredictEdbInfoId(changeEdbInfo.EdbInfoId, "", "", false)
  217. default:
  218. err = errors.New("指标change类型异常")
  219. return
  220. }
  221. //changeDataMap := make(map[string]float64)
  222. //newChangeDataList, e := HandleDataByLinearRegression(bDataList, changeDataMap)
  223. //if e != nil {
  224. // err = fmt.Errorf("获取变频指标插值法Map失败, Err: %s", e.Error())
  225. // return
  226. //}
  227. // 2023-03-17 时间序列始终以指标A为基准, 始终是B进行平移
  228. baseDataList := make([]*edb_data.EdbDataList, 0)
  229. changeDataList := make([]*edb_data.EdbDataList, 0)
  230. changeDataMap := make(map[string]float64)
  231. // 先把低频指标升频为高频
  232. {
  233. frequencyIntMap := map[string]int{
  234. "日度": 1,
  235. "周度": 2,
  236. "旬度": 3,
  237. "月度": 4,
  238. "季度": 5,
  239. "年度": 6,
  240. }
  241. // 如果A指标是高频,那么就需要对B指标进行升频
  242. if frequencyIntMap[edbInfoMappingA.Frequency] < frequencyIntMap[edbInfoMappingB.Frequency] {
  243. aDataMap := make(map[string]float64)
  244. tmpNewChangeDataList, e := HandleDataByLinearRegression(aDataList, aDataMap)
  245. if e != nil {
  246. err = fmt.Errorf("获取变频指标插值法Map失败, Err: %s", e.Error())
  247. return
  248. }
  249. baseDataList = tmpNewChangeDataList
  250. } else {
  251. baseDataList = aDataList
  252. }
  253. // 如果B指标是高频,那么就需要对A指标进行升频
  254. if frequencyIntMap[edbInfoMappingA.Frequency] > frequencyIntMap[edbInfoMappingB.Frequency] {
  255. tmpNewChangeDataList, e := HandleDataByLinearRegression(bDataList, changeDataMap)
  256. if e != nil {
  257. err = fmt.Errorf("获取变频指标插值法Map失败, Err: %s", e.Error())
  258. return
  259. }
  260. changeDataList = tmpNewChangeDataList
  261. } else {
  262. changeDataList = bDataList
  263. for _, v := range baseDataList {
  264. changeDataMap[v.DataTime] = v.Value
  265. }
  266. }
  267. }
  268. // 计算不领先也不滞后时的相关系数
  269. baseCalculateData := make([]float64, 0)
  270. baseDataTimeArr := make([]string, 0)
  271. for i := range baseDataList {
  272. baseDataTimeArr = append(baseDataTimeArr, baseDataList[i].DataTime)
  273. baseCalculateData = append(baseCalculateData, baseDataList[i].Value)
  274. }
  275. zeroCalculateData := make([]float64, 0)
  276. for i := range baseDataTimeArr {
  277. zeroCalculateData = append(zeroCalculateData, changeDataMap[baseDataTimeArr[i]])
  278. }
  279. if len(baseCalculateData) != len(zeroCalculateData) {
  280. err = fmt.Errorf("相关系数两组序列元素数不一致, %d-%d", len(baseCalculateData), len(zeroCalculateData))
  281. return
  282. }
  283. zeroRatio := utils.CalculateCorrelationByIntArr(baseCalculateData, zeroCalculateData)
  284. if leadValue == 0 {
  285. yData = append(yData, zeroRatio)
  286. }
  287. // 计算领先/滞后N期
  288. if leadValue > 0 {
  289. // 平移变频指标领先/滞后的日期(单位天)
  290. moveUnitDays := utils.FrequencyDaysMap[leadUnit]
  291. for i := range xData {
  292. if xData[i] == 0 {
  293. yData = append(yData, zeroRatio)
  294. continue
  295. }
  296. yCalculateData := make([]float64, 0)
  297. // 平移指定天数
  298. mDays := int(moveUnitDays) * xData[i]
  299. _, dMap := MoveDataDaysToNewDataList(changeDataList, mDays)
  300. // 取出对应的基准日期的值
  301. for i2 := range baseDataTimeArr {
  302. yCalculateData = append(yCalculateData, dMap[baseDataTimeArr[i2]])
  303. }
  304. if len(baseCalculateData) != len(yCalculateData) {
  305. err = fmt.Errorf("领先滞后相关系数两组序列元素数不一致, %d-%d", len(baseCalculateData), len(yCalculateData))
  306. return
  307. }
  308. // 公式计算出领先/滞后频度对应点的相关性系数
  309. ratio := utils.CalculateCorrelationByIntArr(baseCalculateData, yCalculateData)
  310. yData = append(yData, ratio)
  311. }
  312. }
  313. xEdbIdValue = xData
  314. yDataList = make([]chart_info.YData, 0)
  315. yDate := "0000-00-00"
  316. yDataList = append(yDataList, chart_info.YData{
  317. Date: yDate,
  318. Value: yData,
  319. })
  320. return
  321. }
  322. // GetRollingCorrelationChartDataByEdbInfo 滚动相关性计算
  323. func GetRollingCorrelationChartDataByEdbInfo(edbInfoMappingA, edbInfoMappingB *chart_edb_mapping.ChartEdbInfoMapping, leadValue int, leadUnit string, calculateValue int, calculateUnit string, startDate, endDate string) (xDateTimeValue []string, yDataList []chart_info.YData, err error) {
  324. xDateTimeValue = make([]string, 0)
  325. yData := make([]float64, 0)
  326. baseEdbInfo := edbInfoMappingA
  327. changeEdbInfo := edbInfoMappingB
  328. // 获取时间基准指标在时间区间内的值
  329. aDataList := make([]*edb_data.EdbDataList, 0)
  330. switch baseEdbInfo.EdbInfoCategoryType {
  331. case 0:
  332. aDataList, err = edb_data.GetEdbDataList(baseEdbInfo.Source, baseEdbInfo.EdbInfoId, startDate, endDate)
  333. case 1:
  334. _, aDataList, _, _, err, _ = chart.GetPredictDataListByPredictEdbInfoId(baseEdbInfo.EdbInfoId, startDate, endDate, false)
  335. default:
  336. err = errors.New("指标base类型异常")
  337. return
  338. }
  339. // 获取变频指标所有日期的值, 插值法完善数据
  340. bDataList := make([]*edb_data.EdbDataList, 0)
  341. switch changeEdbInfo.EdbInfoCategoryType {
  342. case 0:
  343. bDataList, err = edb_data.GetEdbDataList(changeEdbInfo.Source, changeEdbInfo.EdbInfoId, "", "")
  344. case 1:
  345. _, bDataList, _, _, err, _ = chart.GetPredictDataListByPredictEdbInfoId(changeEdbInfo.EdbInfoId, "", "", false)
  346. default:
  347. err = errors.New("指标change类型异常")
  348. return
  349. }
  350. // 数据平移变频指标领先/滞后的日期(单位天)
  351. // 2023-03-17 时间序列始终以指标A为基准, 始终是B进行平移
  352. //baseDataList := make([]*edb_data.EdbDataList, 0)
  353. baseDataMap := make(map[string]float64)
  354. changeDataList := make([]*edb_data.EdbDataList, 0)
  355. changeDataMap := make(map[string]float64)
  356. // A指标不管三七二十一,先变个频再说
  357. {
  358. _, e := HandleDataByLinearRegression(aDataList, baseDataMap)
  359. if e != nil {
  360. err = fmt.Errorf("获取变频指标插值法Map失败, Err: %s", e.Error())
  361. return
  362. }
  363. //baseDataList = tmpNewChangeDataList
  364. }
  365. // A指标不管三七二十一,先变个频再说
  366. {
  367. tmpNewChangeDataList, e := HandleDataByLinearRegression(bDataList, changeDataMap)
  368. if e != nil {
  369. err = fmt.Errorf("获取变频指标插值法Map失败, Err: %s", e.Error())
  370. return
  371. }
  372. changeDataList = tmpNewChangeDataList
  373. // 平移下日期
  374. moveUnitDays := utils.FrequencyDaysMap[leadUnit]
  375. _, changeDataMap = MoveDataDaysToNewDataList(changeDataList, leadValue*moveUnitDays)
  376. }
  377. // 计算计算时,需要多少个日期内数据
  378. calculateDay := utils.FrequencyDaysMap[calculateUnit] * calculateValue
  379. // 计算 每个日期的相关性值
  380. {
  381. startDateTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
  382. endDateTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
  383. for currDay := startDateTime; !currDay.After(endDateTime); currDay = currDay.AddDate(0, 0, 1) {
  384. yCalculateData := make([]float64, 0)
  385. baseCalculateData := make([]float64, 0)
  386. // 取出对应的基准日期的值
  387. for i := 0; i < calculateDay; i++ {
  388. iDay := currDay.AddDate(0, 0, i).Format(utils.FormatDate)
  389. baseCalculateData = append(baseCalculateData, baseDataMap[iDay])
  390. yCalculateData = append(yCalculateData, changeDataMap[iDay])
  391. }
  392. // 公式计算出领先/滞后频度对应点的相关性系数
  393. var ratio float64
  394. if len(baseCalculateData) > 0 {
  395. ratio = utils.CalculateCorrelationByIntArr(baseCalculateData, yCalculateData)
  396. }
  397. yData = append(yData, ratio)
  398. xDateTimeValue = append(xDateTimeValue, currDay.AddDate(0, 0, calculateDay).Format(utils.FormatDate))
  399. }
  400. }
  401. yDataList = make([]chart_info.YData, 0)
  402. yDate := "0000-00-00"
  403. yDataList = append(yDataList, chart_info.YData{
  404. Date: yDate,
  405. Value: yData,
  406. })
  407. return
  408. }
  409. // ChartInfoRefresh 图表刷新
  410. func ChartInfoRefresh(chartInfoId int) (err error) {
  411. var errMsg string
  412. defer func() {
  413. if err != nil {
  414. //fmt.Println(err.Error())
  415. go alarm_msg.SendAlarmMsg("CorrelationChartInfoRefresh: "+errMsg, 3)
  416. }
  417. }()
  418. correlationChart := new(chart_info_correlation.ChartInfoCorrelation)
  419. if err = correlationChart.GetItemById(chartInfoId); err != nil {
  420. errMsg = "获取相关性图表失败, Err: " + err.Error()
  421. return
  422. }
  423. // 批量刷新ETA指标
  424. err, errMsg = chart.EdbInfoRefreshAllFromBase([]int{correlationChart.EdbInfoIdFirst, correlationChart.EdbInfoIdSecond}, false)
  425. if err != nil {
  426. return
  427. }
  428. // 重新生成数据并更新
  429. edbInfoMappingA, err := chart_edb_mapping.GetChartEdbMappingByEdbInfoId(correlationChart.EdbInfoIdFirst)
  430. if err != nil {
  431. errMsg = "获取相关性图表, A指标mapping信息失败, Err:" + err.Error()
  432. return
  433. }
  434. edbInfoMappingB, err := chart_edb_mapping.GetChartEdbMappingByEdbInfoId(correlationChart.EdbInfoIdSecond)
  435. if err != nil {
  436. errMsg = "获取相关性图表, B指标mapping信息失败, Err:" + err.Error()
  437. return
  438. }
  439. periodData, correlationData, err := GetChartDataByEdbInfo(edbInfoMappingA, edbInfoMappingB, correlationChart.LeadValue, correlationChart.LeadUnit, correlationChart.StartDate.Format(utils.FormatDate), correlationChart.EndDate.Format(utils.FormatDate))
  440. if err != nil {
  441. errMsg = "获取相关性图表, 图表计算值失败, Err:" + err.Error()
  442. return
  443. }
  444. periodDataByte, err := json.Marshal(periodData)
  445. if err != nil {
  446. errMsg = "相关性图表, X轴信息有误, Err:" + err.Error()
  447. return
  448. }
  449. correlationDataByte, err := json.Marshal(correlationData[0].Value)
  450. if err != nil {
  451. errMsg = "相关性图表, Y轴信息有误, Err:" + err.Error()
  452. return
  453. }
  454. correlationChart.PeriodData = string(periodDataByte)
  455. correlationChart.CorrelationData = string(correlationDataByte)
  456. correlationChart.ModifyTime = time.Now().Local()
  457. correlationUpdateCols := []string{"PeriodData", "CorrelationData", "ModifyTime"}
  458. if err = correlationChart.Update(correlationUpdateCols); err != nil {
  459. errMsg = "更新相关性图表失败, Err:" + err.Error()
  460. return
  461. }
  462. return
  463. }