chart_info.go 16 KB

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