predict_edb_info_rule.go 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825
  1. package chart
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "github.com/nosixtools/solarlunar"
  7. "github.com/shopspring/decimal"
  8. edbDataModel "hongze/hongze_yb/models/tables/edb_data"
  9. edbInfoModel "hongze/hongze_yb/models/tables/edb_info"
  10. predictEdbRuleDataModel "hongze/hongze_yb/models/tables/predict_edb_rule_data"
  11. "hongze/hongze_yb/utils"
  12. "math"
  13. "strings"
  14. "time"
  15. )
  16. // GetChartPredictEdbInfoDataListByRule1 根据规则1获取预测数据
  17. func GetChartPredictEdbInfoDataListByRule1(edbInfoId int, dataValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList) {
  18. newPredictEdbInfoData = predictEdbInfoData
  19. //获取后面的预测数据
  20. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  21. predictEdbInfoData = make([]*edbDataModel.EdbDataList, 0)
  22. for k, v := range dayList {
  23. newPredictEdbInfoData = append(newPredictEdbInfoData, &edbDataModel.EdbDataList{
  24. EdbDataId: edbInfoId + 10000000000 + k,
  25. EdbInfoId: edbInfoId,
  26. DataTime: v.Format(utils.FormatDate),
  27. Value: dataValue,
  28. DataTimestamp: v.UnixNano() / 1e6,
  29. })
  30. existMap[v.Format(utils.FormatDate)] = dataValue
  31. }
  32. return
  33. }
  34. // GetChartPredictEdbInfoDataListByRuleTb 根据同比值规则获取预测数据
  35. // 2.1 同比: 在未来某一个时间段内,给定一个固定的同比增速a,用去年同期值X乘以同比增速(1+a),得到预测值Y=X(1+a)
  36. // 例: 今年1-3月值,100,100,120。给定同比增速a=0.1,则明年1-3月预测值为: 100*1.1=110,100*1.1=110,120*1.1=132。
  37. func GetChartPredictEdbInfoDataListByRuleTb(edbInfoId int, tbValue float64, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64) {
  38. allDataList := make([]*edbDataModel.EdbDataList, 0)
  39. allDataList = append(allDataList, realPredictEdbInfoData...)
  40. allDataList = append(allDataList, predictEdbInfoData...)
  41. newPredictEdbInfoData = predictEdbInfoData
  42. index := len(allDataList)
  43. //获取后面的预测数据
  44. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  45. predictEdbInfoData = make([]*edbDataModel.EdbDataList, 0)
  46. for k, currentDate := range dayList {
  47. tmpData := &edbDataModel.EdbDataList{
  48. EdbDataId: edbInfoId + 10000000000 + index + k,
  49. EdbInfoId: edbInfoId,
  50. DataTime: currentDate.Format(utils.FormatDate),
  51. //Value: dataValue,
  52. DataTimestamp: currentDate.UnixNano() / 1e6,
  53. }
  54. var val float64
  55. var calculateStatus bool //计算结果
  56. //currentItem := existMap[av]
  57. //上一年的日期
  58. preDate := currentDate.AddDate(-1, 0, 0)
  59. preDateStr := preDate.Format(utils.FormatDate)
  60. if preValue, ok := existMap[preDateStr]; ok { //上一年同期找到
  61. val = TbzDiv(preValue, tbValue)
  62. calculateStatus = true
  63. } else {
  64. switch frequency {
  65. case "月度":
  66. //向上和向下,各找一个月
  67. nextDateDay := preDate
  68. preDateDay := preDate
  69. for i := 0; i <= 35; i++ {
  70. nextDateDayStr := nextDateDay.Format(utils.FormatDate)
  71. if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
  72. val = TbzDiv(preValue, tbValue)
  73. calculateStatus = true
  74. break
  75. } else {
  76. preDateDayStr := preDateDay.Format(utils.FormatDate)
  77. if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
  78. val = TbzDiv(preValue, tbValue)
  79. calculateStatus = true
  80. break
  81. }
  82. }
  83. nextDateDay = nextDateDay.AddDate(0, 0, 1)
  84. preDateDay = preDateDay.AddDate(0, 0, -1)
  85. }
  86. case "季度", "年度":
  87. if preValue, ok := existMap[preDateStr]; ok { //上一年同期->下一个月找到
  88. val = TbzDiv(preValue, tbValue)
  89. calculateStatus = true
  90. break
  91. }
  92. default:
  93. nextDateDay := preDate
  94. preDateDay := preDate
  95. for i := 0; i < 35; i++ {
  96. nextDateDayStr := nextDateDay.Format(utils.FormatDate)
  97. if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
  98. val = TbzDiv(preValue, tbValue)
  99. calculateStatus = true
  100. break
  101. } else {
  102. preDateDayStr := preDateDay.Format(utils.FormatDate)
  103. if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
  104. val = TbzDiv(preValue, tbValue)
  105. calculateStatus = true
  106. break
  107. } else {
  108. //fmt.Println("pre not find:", preDateStr, "i:", i)
  109. }
  110. }
  111. nextDateDay = nextDateDay.AddDate(0, 0, 1)
  112. preDateDay = preDateDay.AddDate(0, 0, -1)
  113. }
  114. }
  115. }
  116. if calculateStatus {
  117. tmpData.Value = val
  118. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  119. allDataList = append(allDataList, tmpData)
  120. existMap[tmpData.DataTime] = val
  121. // 最大最小值
  122. if val < minValue {
  123. minValue = val
  124. }
  125. if val > maxValue {
  126. maxValue = val
  127. }
  128. }
  129. }
  130. return
  131. }
  132. // TbzDiv 同比值计算
  133. // @params a float64 去年同期值
  134. // @params b float64 固定同比增速
  135. func TbzDiv(a, b float64) (result float64) {
  136. if b != 0 {
  137. // 去年同期值
  138. af := decimal.NewFromFloat(a)
  139. // 同比增速
  140. bf := decimal.NewFromFloat(b)
  141. // 默认1
  142. cf := decimal.NewFromFloat(1)
  143. // 总增速
  144. val := bf.Add(cf)
  145. // 计算
  146. result, _ = val.Mul(af).RoundCeil(4).Float64()
  147. } else {
  148. result = 0
  149. }
  150. return
  151. }
  152. // GetChartPredictEdbInfoDataListByRuleTc 根据同差值规则获取预测数据
  153. // 2.2 同差: 在未来某一个时间段内,给定一个固定的同比增加值a,用去年同期值X加上同比增加值A,得到预测值Y=X+a
  154. // 例: 今年1-3月值,100,100,120。给定同比增加值a=10,则明年1-3月预测值为: 100+10=110,100+10=110,120+10=130
  155. func GetChartPredictEdbInfoDataListByRuleTc(edbInfoId int, tcValue float64, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64) {
  156. allDataList := make([]*edbDataModel.EdbDataList, 0)
  157. allDataList = append(allDataList, realPredictEdbInfoData...)
  158. allDataList = append(allDataList, predictEdbInfoData...)
  159. newPredictEdbInfoData = predictEdbInfoData
  160. index := len(allDataList)
  161. //获取后面的预测数据
  162. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  163. predictEdbInfoData = make([]*edbDataModel.EdbDataList, 0)
  164. for k, currentDate := range dayList {
  165. tmpData := &edbDataModel.EdbDataList{
  166. EdbDataId: edbInfoId + 10000000000 + index + k,
  167. EdbInfoId: edbInfoId,
  168. DataTime: currentDate.Format(utils.FormatDate),
  169. //Value: dataValue,
  170. DataTimestamp: currentDate.UnixNano() / 1e6,
  171. }
  172. var val float64
  173. var calculateStatus bool //计算结果
  174. //currentItem := existMap[av]
  175. //上一年的日期
  176. preDate := currentDate.AddDate(-1, 0, 0)
  177. preDateStr := preDate.Format(utils.FormatDate)
  178. if preValue, ok := existMap[preDateStr]; ok { //上一年同期找到
  179. val = TczDiv(preValue, tcValue)
  180. calculateStatus = true
  181. } else {
  182. switch frequency {
  183. case "月度":
  184. //向上和向下,各找一个月
  185. nextDateDay := preDate
  186. preDateDay := preDate
  187. for i := 0; i <= 35; i++ {
  188. nextDateDayStr := nextDateDay.Format(utils.FormatDate)
  189. if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
  190. val = TczDiv(preValue, tcValue)
  191. calculateStatus = true
  192. break
  193. } else {
  194. preDateDayStr := preDateDay.Format(utils.FormatDate)
  195. if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
  196. val = TczDiv(preValue, tcValue)
  197. calculateStatus = true
  198. break
  199. }
  200. }
  201. nextDateDay = nextDateDay.AddDate(0, 0, 1)
  202. preDateDay = preDateDay.AddDate(0, 0, -1)
  203. }
  204. case "季度", "年度":
  205. if preValue, ok := existMap[preDateStr]; ok { //上一年同期->下一个月找到
  206. val = TczDiv(preValue, tcValue)
  207. calculateStatus = true
  208. break
  209. }
  210. default:
  211. nextDateDay := preDate
  212. preDateDay := preDate
  213. for i := 0; i < 35; i++ {
  214. nextDateDayStr := nextDateDay.Format(utils.FormatDate)
  215. if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
  216. val = TczDiv(preValue, tcValue)
  217. calculateStatus = true
  218. break
  219. } else {
  220. preDateDayStr := preDateDay.Format(utils.FormatDate)
  221. if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
  222. val = TczDiv(preValue, tcValue)
  223. calculateStatus = true
  224. break
  225. } else {
  226. //fmt.Println("pre not find:", preDateStr, "i:", i)
  227. }
  228. }
  229. nextDateDay = nextDateDay.AddDate(0, 0, 1)
  230. preDateDay = preDateDay.AddDate(0, 0, -1)
  231. }
  232. }
  233. }
  234. if calculateStatus {
  235. tmpData.Value = val
  236. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  237. allDataList = append(allDataList, tmpData)
  238. existMap[tmpData.DataTime] = val
  239. // 最大最小值
  240. if val < minValue {
  241. minValue = val
  242. }
  243. if val > maxValue {
  244. maxValue = val
  245. }
  246. }
  247. }
  248. return
  249. }
  250. // TczDiv 环差值计算
  251. // @params a float64 上一期值
  252. // @params b float64 固定的环比增加值
  253. func TczDiv(a, b float64) (result float64) {
  254. if b != 0 {
  255. // 上一期值
  256. af := decimal.NewFromFloat(a)
  257. // 固定的环比增加值
  258. bf := decimal.NewFromFloat(b)
  259. // 计算
  260. result, _ = af.Add(bf).RoundCeil(4).Float64()
  261. } else {
  262. result = 0
  263. }
  264. return
  265. }
  266. // GetChartPredictEdbInfoDataListByRuleHb 根据环比值规则获取预测数据
  267. // 环比:在未来某一个时间段内,给定一个固定的环比增速a,用上一期值X乘以环比增速(1+a),得到预测值Y=X(1+a)
  268. // 例: 最近1期值为100,给定环比增速a=0.2,则未来3期预测值为: 100*1.2=120,120*1.2=144,144*1.2=172.8
  269. func GetChartPredictEdbInfoDataListByRuleHb(edbInfoId int, hbValue float64, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64) {
  270. allDataList := make([]*edbDataModel.EdbDataList, 0)
  271. allDataList = append(allDataList, realPredictEdbInfoData...)
  272. allDataList = append(allDataList, predictEdbInfoData...)
  273. newPredictEdbInfoData = predictEdbInfoData
  274. index := len(allDataList)
  275. //获取后面的预测数据
  276. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  277. for k, currentDate := range dayList {
  278. tmpK := index + k - 1 //上1期的值
  279. // 环比值计算
  280. val := HbzDiv(allDataList[tmpK].Value, hbValue)
  281. currentDateStr := currentDate.Format(utils.FormatDate)
  282. tmpData := &edbDataModel.EdbDataList{
  283. EdbDataId: edbInfoId + 10000000000 + index + k,
  284. EdbInfoId: edbInfoId,
  285. DataTime: currentDateStr,
  286. Value: val,
  287. DataTimestamp: currentDate.UnixNano() / 1e6,
  288. }
  289. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  290. allDataList = append(allDataList, tmpData)
  291. existMap[currentDateStr] = val
  292. // 最大最小值
  293. if val < minValue {
  294. minValue = val
  295. }
  296. if val > maxValue {
  297. maxValue = val
  298. }
  299. }
  300. return
  301. }
  302. // HbzDiv 环比值计算
  303. // @params a float64 上一期值
  304. // @params b float64 固定的环比增速
  305. func HbzDiv(a, b float64) (result float64) {
  306. if b != 0 {
  307. // 上一期值
  308. af := decimal.NewFromFloat(a)
  309. // 固定的环比增速
  310. bf := decimal.NewFromFloat(b)
  311. // 默认1
  312. cf := decimal.NewFromFloat(1)
  313. // 总增速
  314. val := bf.Add(cf)
  315. // 计算
  316. result, _ = val.Mul(af).RoundCeil(4).Float64()
  317. } else {
  318. result = 0
  319. }
  320. return
  321. }
  322. // GetChartPredictEdbInfoDataListByRuleHc 根据环差值规则获取预测数据
  323. // 2.4 环差:在未来某一个时间段内,给定一个固定的环比增加值a,用上一期值X加上环比增加值a,得到预测值Y=X+a
  324. // 例: 最近1期值为100,给定环比增加值a=10,则未来3期预测值为: 100+10=110,110+10=120,120+10=130
  325. func GetChartPredictEdbInfoDataListByRuleHc(edbInfoId int, hcValue float64, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64) {
  326. allDataList := make([]*edbDataModel.EdbDataList, 0)
  327. allDataList = append(allDataList, realPredictEdbInfoData...)
  328. allDataList = append(allDataList, predictEdbInfoData...)
  329. newPredictEdbInfoData = predictEdbInfoData
  330. index := len(allDataList)
  331. //获取后面的预测数据
  332. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  333. for k, currentDate := range dayList {
  334. tmpK := index + k - 1 //上1期的值
  335. // 环差别值计算
  336. val := HczDiv(allDataList[tmpK].Value, hcValue)
  337. currentDateStr := currentDate.Format(utils.FormatDate)
  338. tmpData := &edbDataModel.EdbDataList{
  339. EdbDataId: edbInfoId + 10000000000 + index + k,
  340. EdbInfoId: edbInfoId,
  341. DataTime: currentDateStr,
  342. Value: val,
  343. DataTimestamp: currentDate.UnixNano() / 1e6,
  344. }
  345. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  346. allDataList = append(allDataList, tmpData)
  347. existMap[currentDateStr] = val
  348. // 最大最小值
  349. if val < minValue {
  350. minValue = val
  351. }
  352. if val > maxValue {
  353. maxValue = val
  354. }
  355. }
  356. return
  357. }
  358. // HczDiv 环差值计算
  359. // @params a float64 上一期值
  360. // @params b float64 固定的环比增加值
  361. func HczDiv(a, b float64) (result float64) {
  362. if b != 0 {
  363. // 上一期值
  364. af := decimal.NewFromFloat(a)
  365. // 固定的环比增加值
  366. bf := decimal.NewFromFloat(b)
  367. // 计算
  368. result, _ = af.Add(bf).RoundCeil(4).Float64()
  369. } else {
  370. result = 0
  371. }
  372. return
  373. }
  374. // GetChartPredictEdbInfoDataListByRuleNMoveMeanValue 根据N期移动均值规则获取预测数据
  375. // 2.5 N期移动均值:在未来某一个时间段内,下一期值等于过去N期值得平均值。
  376. // 例:最近3期值(N=3),为95,98,105则未来第1期值为 1/3*(95+98+105)=99.33, 未来第2期值为 1/3*(98+105+99.33)=100.78依次类推。
  377. func GetChartPredictEdbInfoDataListByRuleNMoveMeanValue(edbInfoId int, nValue int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64) {
  378. allDataList := make([]*edbDataModel.EdbDataList, 0)
  379. allDataList = append(allDataList, realPredictEdbInfoData...)
  380. allDataList = append(allDataList, predictEdbInfoData...)
  381. newPredictEdbInfoData = predictEdbInfoData
  382. lenAllData := len(allDataList)
  383. if lenAllData < nValue || lenAllData <= 0 {
  384. return
  385. }
  386. if nValue <= 0 {
  387. return
  388. }
  389. // 分母
  390. decimalN := decimal.NewFromInt(int64(nValue))
  391. //获取后面的预测数据
  392. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  393. for k, currentDate := range dayList {
  394. tmpIndex := lenAllData + k - 1 //上1期的值
  395. // 数据集合中的最后一个数据
  396. tmpDecimalVal := decimal.NewFromFloat(allDataList[tmpIndex].Value)
  397. for tmpK := 2; tmpK <= nValue; tmpK++ {
  398. tmpIndex2 := tmpIndex - tmpK //上N期的值
  399. tmpDecimalVal2 := decimal.NewFromFloat(allDataList[tmpIndex2].Value)
  400. tmpDecimalVal = tmpDecimalVal.Add(tmpDecimalVal2)
  401. }
  402. // N期移动均值计算
  403. val, _ := tmpDecimalVal.Div(decimalN).RoundCeil(4).Float64()
  404. currentDateStr := currentDate.Format(utils.FormatDate)
  405. tmpData := &edbDataModel.EdbDataList{
  406. EdbDataId: edbInfoId + 10000000000 + lenAllData + k,
  407. EdbInfoId: edbInfoId,
  408. DataTime: currentDateStr,
  409. Value: val,
  410. DataTimestamp: currentDate.UnixNano() / 1e6,
  411. }
  412. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  413. allDataList = append(allDataList, tmpData)
  414. existMap[currentDateStr] = val
  415. // 最大最小值
  416. if val < minValue {
  417. minValue = val
  418. }
  419. if val > maxValue {
  420. maxValue = val
  421. }
  422. }
  423. return
  424. }
  425. // GetChartPredictEdbInfoDataListByRuleNLinearRegression 根据N期移动均值规则获取预测数据
  426. // 2.6N期段线性外推值:给出过去N期值所确定的线性回归方程(Y=aX+b)在未来一段时间内的推算值。回归方程虽然比较复杂,但各种编程语言应该都有现成的模块或函数,应该无需自己编写。
  427. // 例1:过去5期值(N=5)分别为:3,5,7,9,11(每两期值之间的时间间隔相等)。那么按照线性回归方程推算,未来三期的预测值是:13,15,17。
  428. //
  429. // 例2:过去6期值(N=6)分别为:3,3,5,7,9,11(每两期值之间的时间间隔相等)。那么按照线性回归方程推算,未来三期的预测值是:12.33,14.05,15.76。例1和例2的区别在于,多加了一期数据,导致回归方程发生改变,从而预测值不同。
  430. func GetChartPredictEdbInfoDataListByRuleNLinearRegression(edbInfoId int, nValue int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64, err error) {
  431. //var errMsg string
  432. //defer func() {
  433. // if errMsg != `` {
  434. // go alarm_msg.SendAlarmMsg("更新上海的token失败;ERR:"+err.Error(), 3)
  435. // }
  436. //}()
  437. allDataList := make([]*edbDataModel.EdbDataList, 0)
  438. allDataList = append(allDataList, realPredictEdbInfoData...)
  439. allDataList = append(allDataList, predictEdbInfoData...)
  440. newPredictEdbInfoData = predictEdbInfoData
  441. lenAllData := len(allDataList)
  442. if lenAllData < nValue || lenAllData <= 0 {
  443. return
  444. }
  445. if nValue <= 1 {
  446. return
  447. }
  448. //获取后面的预测数据
  449. // 获取线性方程公式的a、b的值
  450. coordinateData := make([]Coordinate, 0)
  451. for tmpK := nValue; tmpK > 0; tmpK-- {
  452. tmpIndex2 := lenAllData - tmpK //上N期的值
  453. tmpCoordinate := Coordinate{
  454. X: float64(nValue - tmpK + 1),
  455. Y: allDataList[tmpIndex2].Value,
  456. }
  457. coordinateData = append(coordinateData, tmpCoordinate)
  458. }
  459. a, b := getLinearResult(coordinateData)
  460. if math.IsNaN(a) || math.IsNaN(b) {
  461. err = errors.New("线性方程公式生成失败")
  462. return
  463. }
  464. //fmt.Println("a:", a, ";======b:", b)
  465. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  466. for k, currentDate := range dayList {
  467. tmpK := nValue + k + 1
  468. aDecimal := decimal.NewFromFloat(a)
  469. xDecimal := decimal.NewFromInt(int64(tmpK))
  470. bDecimal := decimal.NewFromFloat(b)
  471. val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).RoundCeil(4).Float64()
  472. currentDateStr := currentDate.Format(utils.FormatDate)
  473. tmpData := &edbDataModel.EdbDataList{
  474. EdbDataId: edbInfoId + 10000000000 + lenAllData + k,
  475. EdbInfoId: edbInfoId,
  476. DataTime: currentDateStr,
  477. Value: val,
  478. DataTimestamp: currentDate.UnixNano() / 1e6,
  479. }
  480. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  481. allDataList = append(allDataList, tmpData)
  482. existMap[currentDateStr] = val
  483. // 最大最小值
  484. if val < minValue {
  485. minValue = val
  486. }
  487. if val > maxValue {
  488. maxValue = val
  489. }
  490. }
  491. return
  492. }
  493. // Series is a container for a series of data
  494. type Series []Coordinate
  495. // Coordinate holds the data in a series
  496. type Coordinate struct {
  497. X, Y float64
  498. }
  499. func getLinearResult(s []Coordinate) (gradient, intercept float64) {
  500. if len(s) <= 1 {
  501. return
  502. }
  503. // Placeholder for the math to be done
  504. var sum [5]float64
  505. // Loop over data keeping index in place
  506. i := 0
  507. for ; i < len(s); i++ {
  508. sum[0] += s[i].X
  509. sum[1] += s[i].Y
  510. sum[2] += s[i].X * s[i].X
  511. sum[3] += s[i].X * s[i].Y
  512. sum[4] += s[i].Y * s[i].Y
  513. }
  514. // Find gradient and intercept
  515. f := float64(i)
  516. gradient = (f*sum[3] - sum[0]*sum[1]) / (f*sum[2] - sum[0]*sum[0])
  517. intercept = (sum[1] / f) - (gradient * sum[0] / f)
  518. //fmt.Println("gradient:", gradient, ";intercept:", intercept)
  519. // Create the new regression series
  520. //for j := 0; j < len(s); j++ {
  521. // regressions = append(regressions, Coordinate{
  522. // X: s[j].X,
  523. // Y: s[j].X*gradient + intercept,
  524. // })
  525. //}
  526. return
  527. }
  528. // GetChartPredictEdbInfoDataListByRuleTrendsHC 根据动态环比增加值的计算规则获取预测数据
  529. //
  530. // 研究员有对预测指标进行动态环差计算的需求,即预测指标使用环差规则进行预测时,环比增加值不是固定值,而是由几个预测指标计算得出的动态变化的值;
  531. // 需求说明:
  532. // 1、增加“动态环差”预测规则;
  533. // 2、环比增加值在弹窗设置;
  534. // 3、动态环差预测举例:
  535. // 指标A实际最新数据为2022-10-27(100);
  536. // 预测指标B预测数据为2022-10-28(240)、2022-10-29(300);
  537. // 预测指标C预测数据为2022-10-28(260)、2022-10-29(310);
  538. // 计算公式为B-C;
  539. // 则指标A至2022-10-29的预测值为2022-10-28(100+(240-260)=80)、2022-10-29(80+(300-310)=90);
  540. // 注:动态环比增加值的计算遵从计算指标的计算规则,即用于计算的指标若有部分指标缺少部分日期数据,则这部分日期数据不做计算,为空;若动态环比增加值某一天为空,则往前追溯最近一期有值的环比增加值作为该天的数值参与计算;
  541. func GetChartPredictEdbInfoDataListByRuleTrendsHC(edbInfoId, configId int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64) {
  542. allDataList := make([]*edbDataModel.EdbDataList, 0)
  543. allDataList = append(allDataList, realPredictEdbInfoData...)
  544. allDataList = append(allDataList, predictEdbInfoData...)
  545. newPredictEdbInfoData = predictEdbInfoData
  546. lenAllData := len(allDataList)
  547. if lenAllData <= 0 {
  548. return
  549. }
  550. hcDataMap := make(map[string]float64) //规则计算的环差值map
  551. //已经生成的动态数据
  552. tmpPredictEdbRuleDataList, err := predictEdbRuleDataModel.GetPredictEdbRuleDataList(edbInfoId, configId, startDate.Format(utils.FormatDate), endDate.Format(utils.FormatDate))
  553. if err != nil {
  554. return
  555. }
  556. for _, v := range tmpPredictEdbRuleDataList {
  557. hcDataMap[v.DataTime.Format(utils.FormatDate)] = v.Value
  558. }
  559. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  560. for k, currentDate := range dayList {
  561. // 最近一条数据
  562. tmpLenAllDataList := len(allDataList)
  563. lastValue := allDataList[tmpLenAllDataList-1].Value
  564. // 动态环差值数据
  565. currentDateStr := currentDate.Format(utils.FormatDate)
  566. hcVal, ok := hcDataMap[currentDateStr]
  567. if !ok {
  568. continue
  569. }
  570. lastValueDecimal := decimal.NewFromFloat(lastValue)
  571. hcValDecimal := decimal.NewFromFloat(hcVal)
  572. val, _ := lastValueDecimal.Add(hcValDecimal).RoundCeil(4).Float64()
  573. tmpData := &edbDataModel.EdbDataList{
  574. EdbDataId: edbInfoId + 10000000000 + lenAllData + k,
  575. EdbInfoId: edbInfoId,
  576. DataTime: currentDateStr,
  577. Value: val,
  578. DataTimestamp: currentDate.UnixNano() / 1e6,
  579. }
  580. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  581. allDataList = append(allDataList, tmpData)
  582. existMap[currentDateStr] = val
  583. // 最大最小值
  584. if val < minValue {
  585. minValue = val
  586. }
  587. if val > maxValue {
  588. maxValue = val
  589. }
  590. }
  591. return
  592. }
  593. // GetChartPredictEdbInfoDataListByRuleFinalValueHc 根据 给定终值后插值 规则获取预测数据
  594. //
  595. // 项目背景:
  596. // 假设螺纹产量在2023年1月1号的预测值是255万吨,从当下到2023年1月1号,螺纹产量将会线性变化,那么每一期的螺纹产量是多少?
  597. // 算法:从当下(2022/10/28)到2023/1/1号,一共65天,从当前值(305.02)到255,差值-50.02,
  598. // 则每日环差为-50.02/65=-0.7695。因为数据点是周度频率,每周环差为,-0.3849*7=-5.3868。
  599. // 从以上计算过程可看出,“给定终值后差值”的算法,是在“环差”算法的基础上,做的一个改动。即这个”环差值”=【(终值-最新值)/终值与最新值得日期差】*数据频率
  600. // 需求说明:
  601. // 1、增加一个预测规则,名为“给定终值后插值”,给定预测截止日期和预测终值,计算最新数据日期至预测截止日期的时间差T,计算最新数据和预测终值的数据差S,数据频率与指标频度有关,日度=1,周度=7,旬度=10,月度=30,季度=90,年度=365,环差值=S/T*频率,预测数值=前一天数值+环差值;
  602. // 2、最新数据值和日期改动后,需重新计算环差值和预测数值;
  603. func GetChartPredictEdbInfoDataListByRuleFinalValueHc(edbInfoId int, finalValue float64, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64) {
  604. allDataList := make([]*edbDataModel.EdbDataList, 0)
  605. allDataList = append(allDataList, realPredictEdbInfoData...)
  606. allDataList = append(allDataList, predictEdbInfoData...)
  607. newPredictEdbInfoData = predictEdbInfoData
  608. index := len(allDataList)
  609. //获取后面的预测日期
  610. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  611. lenDay := len(dayList)
  612. if lenDay <= 0 {
  613. return
  614. }
  615. var hcValue float64
  616. lastValueDeciamal := decimal.NewFromFloat(allDataList[index-1].Value) // 实际数据的最后一个值
  617. finalValueDeciamal := decimal.NewFromFloat(finalValue) // 给定的终止数据
  618. dayDecimal := decimal.NewFromInt(int64(lenDay)) // 需要作为分母的期数
  619. hcValue, _ = finalValueDeciamal.Sub(lastValueDeciamal).Div(dayDecimal).Float64() // 计算出来的环差值
  620. //获取后面的预测数据
  621. predictEdbInfoData = make([]*edbDataModel.EdbDataList, 0)
  622. lastK := lenDay - 1 // 最后的日期
  623. for k, currentDate := range dayList {
  624. tmpK := index + k - 1 //上1期的值
  625. var val float64
  626. // 环差别值计算
  627. if k == lastK { //如果是最后一天,那么就用最终值,否则就计算
  628. val = finalValue
  629. } else {
  630. val = HczDiv(allDataList[tmpK].Value, hcValue)
  631. }
  632. currentDateStr := currentDate.Format(utils.FormatDate)
  633. tmpData := &edbDataModel.EdbDataList{
  634. EdbDataId: edbInfoId + 10000000000 + index + k,
  635. EdbInfoId: edbInfoId,
  636. DataTime: currentDateStr,
  637. Value: val,
  638. DataTimestamp: currentDate.UnixNano() / 1e6,
  639. }
  640. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  641. allDataList = append(allDataList, tmpData)
  642. existMap[currentDateStr] = val
  643. // 最大最小值
  644. if val < minValue {
  645. minValue = val
  646. }
  647. if val > maxValue {
  648. maxValue = val
  649. }
  650. }
  651. return
  652. }
  653. // SeasonConf 季节性规则的配置
  654. type SeasonConf struct {
  655. Calendar string `description:"公历、农历"`
  656. YearType int `description:"选择方式,1:连续N年;2:指定年份"`
  657. NValue int `description:"连续N年"`
  658. YearList []int `description:"指定年份列表"`
  659. }
  660. // GetChartPredictEdbInfoDataListByRuleSeason 根据 季节性 规则获取预测数据
  661. //
  662. // ETA预测规则:季节性
  663. // 已知选定指标A最近更新日期: 2022-12-6 200
  664. // 设置预测截止日期2023-01-06
  665. // 1、选择过去N年,N=3
  666. // 则过去N年为2021、2020、2019
  667. // 指标A日期 实际值 指标A日期
  668. // 2019/12/5 150 2019/12/6
  669. // 2020/12/5 180 2020/12/6
  670. // 2021/12/5 210 2021/12/6
  671. // 2019/12/31 200 2020/1/1
  672. // 2020/12/31 210 2021/1/1
  673. // 2021/12/31 250 2022/1/1
  674. //
  675. // 计算12.7预测值,求过去N年环差均值=[(100-150)+(160-180)+(250-210)]/3=-10
  676. // 则12.7预测值=12.6值+过去N年环差均值=200-10=190
  677. // 以此类推...
  678. //
  679. // 计算2023.1.2预测值,求过去N年环差均值=[(300-200)+(220-210)+(260-250)]/3=40
  680. // 则2023.1.2预测值=2023.1.1值+过去N年环差均值
  681. func GetChartPredictEdbInfoDataListByRuleSeason(edbInfoId int, yearsList []int, calendar string, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64, err error) {
  682. allDataList := make([]*edbDataModel.EdbDataList, 0)
  683. allDataList = append(allDataList, realPredictEdbInfoData...)
  684. allDataList = append(allDataList, predictEdbInfoData...)
  685. newPredictEdbInfoData = predictEdbInfoData
  686. // 插值法数据处理
  687. handleDataMap := make(map[string]float64)
  688. err = HandleDataByLinearRegression(allDataList, handleDataMap)
  689. if err != nil {
  690. return
  691. }
  692. // 获取每个年份的日期数据需要平移的天数
  693. moveDayMap := make(map[int]int, 0) // 每个年份的春节公历
  694. {
  695. if calendar == "公历" {
  696. for _, year := range yearsList {
  697. moveDayMap[year] = 0 //公历就不平移了
  698. }
  699. } else {
  700. currentDay := time.Now()
  701. if currentDay.Month() >= 11 { //如果大于等于11月份,那么用的是下一年的春节
  702. currentDay = currentDay.AddDate(1, 0, 0)
  703. }
  704. currentYear := currentDay.Year()
  705. currentYearCjnl := fmt.Sprintf("%d-01-01", currentYear) //当年的春节农历
  706. currentYearCjgl := solarlunar.LunarToSolar(currentYearCjnl, false) //当年的春节公历
  707. currentYearCjglTime, tmpErr := time.ParseInLocation(utils.FormatDate, currentYearCjgl, time.Local)
  708. if tmpErr != nil {
  709. err = errors.New("当前春节公历日期转换失败:" + tmpErr.Error())
  710. return
  711. }
  712. // 指定的年份
  713. for _, year := range yearsList {
  714. tmpYearCjnl := fmt.Sprintf("%d-01-01", year) //指定年的春节农历
  715. tmpYearCjgl := solarlunar.LunarToSolar(tmpYearCjnl, false) //指定年的春节公历
  716. //moveDayList = append(moveDayList, 0) //公历就不平移了
  717. tmpYearCjglTime, tmpErr := time.ParseInLocation(utils.FormatDate, tmpYearCjgl, time.Local)
  718. if tmpErr != nil {
  719. err = errors.New(fmt.Sprintf("%d公历日期转换失败:%s", year, tmpErr.Error()))
  720. return
  721. }
  722. tmpCurrentYearCjglTime := currentYearCjglTime.AddDate(year-currentYear, 0, 0)
  723. moveDay := utils.GetTimeSubDay(tmpYearCjglTime, tmpCurrentYearCjglTime)
  724. moveDayMap[year] = moveDay //公历平移
  725. }
  726. }
  727. }
  728. index := len(allDataList)
  729. //获取后面的预测日期
  730. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  731. //获取后面的预测数据
  732. predictEdbInfoData = make([]*edbDataModel.EdbDataList, 0)
  733. for k, currentDate := range dayList {
  734. // 如果遇到闰二月,如2.29,去掉该天数据
  735. if strings.Contains(currentDate.Format(utils.FormatDate), "02-29") {
  736. continue
  737. }
  738. tmpHistoryVal := decimal.NewFromFloat(0) //往期的差值总和
  739. tmpHistoryValNum := 0 // 往期差值计算的数量
  740. tmpLenAllDataList := len(allDataList)
  741. tmpK := tmpLenAllDataList - 1 //上1期数据的下标
  742. lastDayData := allDataList[tmpK] // 上1期的数据
  743. lastDayStr := lastDayData.DataTime
  744. lastDayVal := lastDayData.Value
  745. lastDay, tmpErr := time.ParseInLocation(utils.FormatDate, lastDayStr, time.Local)
  746. if tmpErr != nil {
  747. err = errors.New("获取上期日期转换失败:" + tmpErr.Error())
  748. }
  749. for _, year := range yearsList {
  750. moveDay := moveDayMap[year] //需要移动的天数
  751. var tmpHistoryCurrentVal, tmpHistoryLastVal float64
  752. var isFindHistoryCurrent, isFindHistoryLast bool //是否找到前几年的数据
  753. //前几年当日的日期
  754. tmpHistoryCurrentDate := currentDate.AddDate(year-currentDate.Year(), 0, -moveDay)
  755. for i := 0; i <= 35; i++ { // 前后35天找数据,找到最近的值,先向后面找,再往前面找
  756. tmpDate := tmpHistoryCurrentDate.AddDate(0, 0, i)
  757. if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
  758. tmpHistoryCurrentVal = val
  759. isFindHistoryCurrent = true
  760. break
  761. } else {
  762. tmpDate := tmpHistoryCurrentDate.AddDate(0, 0, -i)
  763. if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
  764. tmpHistoryCurrentVal = val
  765. isFindHistoryCurrent = true
  766. break
  767. }
  768. }
  769. }
  770. //前几年上一期的日期
  771. tmpHistoryLastDate := lastDay.AddDate(year-lastDay.Year(), 0, -moveDay)
  772. for i := 0; i <= 35; i++ { // 前后35天找数据,找到最近的值,先向后面找,再往前面找
  773. tmpDate := tmpHistoryLastDate.AddDate(0, 0, i)
  774. if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
  775. tmpHistoryLastVal = val
  776. isFindHistoryLast = true
  777. break
  778. } else {
  779. tmpDate := tmpHistoryLastDate.AddDate(0, 0, -i)
  780. if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
  781. tmpHistoryLastVal = val
  782. isFindHistoryLast = true
  783. break
  784. }
  785. }
  786. }
  787. // 如果两个日期对应的数据都找到了,那么计算两期的差值
  788. if isFindHistoryCurrent && isFindHistoryLast {
  789. af := decimal.NewFromFloat(tmpHistoryCurrentVal)
  790. bf := decimal.NewFromFloat(tmpHistoryLastVal)
  791. tmpHistoryVal = tmpHistoryVal.Add(af.Sub(bf))
  792. tmpHistoryValNum++
  793. }
  794. }
  795. //计算的差值与选择的年份数量不一致,那么当前日期不计算
  796. if tmpHistoryValNum != len(yearsList) {
  797. continue
  798. }
  799. lastDayValDec := decimal.NewFromFloat(lastDayVal)
  800. val, _ := tmpHistoryVal.Div(decimal.NewFromInt(int64(tmpHistoryValNum))).Add(lastDayValDec).RoundCeil(4).Float64()
  801. currentDateStr := currentDate.Format(utils.FormatDate)
  802. tmpData := &edbDataModel.EdbDataList{
  803. EdbDataId: edbInfoId + 10000000000 + index + k,
  804. EdbInfoId: edbInfoId,
  805. DataTime: currentDateStr,
  806. Value: val,
  807. DataTimestamp: currentDate.UnixNano() / 1e6,
  808. }
  809. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  810. allDataList = append(allDataList, tmpData)
  811. existMap[currentDateStr] = val
  812. // 继续使用插值法补充新预测日期的数据之间的值
  813. err = HandleDataByLinearRegression([]*edbDataModel.EdbDataList{
  814. lastDayData, tmpData,
  815. }, handleDataMap)
  816. if err != nil {
  817. return
  818. }
  819. // 最大最小值
  820. if val < minValue {
  821. minValue = val
  822. }
  823. if val > maxValue {
  824. maxValue = val
  825. }
  826. }
  827. return
  828. }
  829. // MoveAverageConf 移动平均同比规则的配置
  830. type MoveAverageConf struct {
  831. Year int `description:"指定年份"`
  832. NValue int `description:"N期的数据"`
  833. }
  834. // GetChartPredictEdbInfoDataListByRuleMoveAverageTb 根据 移动平均同比 规则获取预测数据
  835. //
  836. // ETA预测规则:季节性
  837. // 2、选择指定N年,N=3
  838. // 指定N年为2012、2015、2018
  839. // 指标A日期 实际值 指标A日期 实际值
  840. // 2012/12/5 150 2012/12/6 130
  841. // 2015/12/5 180 2015/12/6 150
  842. // 2018/12/5 210 2018/12/6 260
  843. // 2012/12/31 200 2013/1/1 200
  844. // 2015/12/31 210 2016/1/1 250
  845. // 2018/12/31 250 2019/1/1 270
  846. // 计算12.7预测值,求过去N年环差均值=[(130-150)+(150-180)+(290-210)]/3=10
  847. // 则12.7预测值=12.6值+过去N年环差均值=200+10=210
  848. // 以此类推...
  849. // 计算2023.1.2预测值,求过去N年环差均值=[(200-200)+(250-210)+(270-250)]/3=16.67
  850. // 则2023.1.2预测值=2023.1.1值+过去N年环差均值
  851. func GetChartPredictEdbInfoDataListByRuleMoveAverageTb(edbInfoId int, nValue, year int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64, err error) {
  852. allDataList := make([]*edbDataModel.EdbDataList, 0)
  853. allDataList = append(allDataList, realPredictEdbInfoData...)
  854. allDataList = append(allDataList, predictEdbInfoData...)
  855. newPredictEdbInfoData = predictEdbInfoData
  856. lenAllData := len(allDataList)
  857. if lenAllData < nValue || lenAllData <= 0 {
  858. return
  859. }
  860. if nValue <= 0 {
  861. return
  862. }
  863. // 分母
  864. decimalN := decimal.NewFromInt(int64(nValue))
  865. //获取后面的预测数据
  866. dayList := getPredictEdbDayList(startDate, endDate, frequency)
  867. if len(dayList) <= 0 {
  868. return
  869. }
  870. // 需要减去的年份
  871. subYear := year - dayList[0].Year()
  872. for k, currentDate := range dayList {
  873. tmpLenAllDataList := len(allDataList)
  874. tmpIndex := tmpLenAllDataList - 1 //上1期数据的下标
  875. averageDateList := make([]string, 0) //计算平均数的日期
  876. // 数据集合中的最后一个数据
  877. tmpDecimalVal := decimal.NewFromFloat(allDataList[tmpIndex].Value)
  878. averageDateList = append(averageDateList, allDataList[tmpIndex].DataTime)
  879. for tmpK := 1; tmpK < nValue; tmpK++ {
  880. tmpIndex2 := tmpIndex - tmpK //上N期的值
  881. tmpDecimalVal2 := decimal.NewFromFloat(allDataList[tmpIndex2].Value)
  882. tmpDecimalVal = tmpDecimalVal.Add(tmpDecimalVal2)
  883. averageDateList = append(averageDateList, allDataList[tmpIndex2].DataTime)
  884. }
  885. // 最近的N期平均值
  886. tmpAverageVal := tmpDecimalVal.Div(decimalN)
  887. var tmpHistoryCurrentVal float64 // 前几年当日的数据值
  888. var isFindHistoryCurrent, isFindHistoryLast bool //是否找到前几年的数据
  889. tmpHistoryDecimalVal := decimal.NewFromFloat(0) //前几年N期数据总值
  890. {
  891. // 前几年N期汇总期数
  892. tmpHistoryValNum := 0
  893. {
  894. //前几年当日的日期
  895. tmpHistoryCurrentDate := currentDate.AddDate(subYear, 0, 0)
  896. for i := 0; i <= 35; i++ { // 前后35天找数据,找到最近的值,先向后面找,再往前面找
  897. tmpDate := tmpHistoryCurrentDate.AddDate(0, 0, i)
  898. if val, ok := existMap[tmpDate.Format(utils.FormatDate)]; ok {
  899. tmpHistoryCurrentVal = val
  900. isFindHistoryCurrent = true
  901. break
  902. } else {
  903. tmpDate := tmpHistoryCurrentDate.AddDate(0, 0, -i)
  904. if val, ok := existMap[tmpDate.Format(utils.FormatDate)]; ok {
  905. tmpHistoryCurrentVal = val
  906. isFindHistoryCurrent = true
  907. break
  908. }
  909. }
  910. }
  911. }
  912. for _, averageDate := range averageDateList {
  913. lastDay, tmpErr := time.ParseInLocation(utils.FormatDate, averageDate, time.Local)
  914. if tmpErr != nil {
  915. err = tmpErr
  916. return
  917. }
  918. //前几年上一期的日期
  919. tmpHistoryLastDate := lastDay.AddDate(subYear, 0, 0)
  920. for i := 0; i <= 35; i++ { // 前后35天找数据,找到最近的值,先向后面找,再往前面找
  921. tmpDate := tmpHistoryLastDate.AddDate(0, 0, i)
  922. if val, ok := existMap[tmpDate.Format(utils.FormatDate)]; ok {
  923. tmpDecimalVal2 := decimal.NewFromFloat(val)
  924. tmpHistoryDecimalVal = tmpHistoryDecimalVal.Add(tmpDecimalVal2)
  925. tmpHistoryValNum++
  926. break
  927. } else {
  928. tmpDate := tmpHistoryLastDate.AddDate(0, 0, -i)
  929. if val, ok := existMap[tmpDate.Format(utils.FormatDate)]; ok {
  930. tmpDecimalVal2 := decimal.NewFromFloat(val)
  931. tmpHistoryDecimalVal = tmpHistoryDecimalVal.Add(tmpDecimalVal2)
  932. tmpHistoryValNum++
  933. break
  934. }
  935. }
  936. }
  937. }
  938. // 汇总期数与配置的N期数量一致
  939. if tmpHistoryValNum == nValue {
  940. isFindHistoryLast = true
  941. }
  942. }
  943. // 如果没有找到前几年的汇总数据,或者没有找到前几年当日的数据,那么退出当前循环,进入下一循环
  944. if !isFindHistoryLast || !isFindHistoryCurrent {
  945. continue
  946. }
  947. // 计算最近N期平均值
  948. tmpHistoryAverageVal := tmpHistoryDecimalVal.Div(decimalN)
  949. // 计算最近N期同比值
  950. tbVal := tmpAverageVal.Div(tmpHistoryAverageVal)
  951. // 预测值结果 = 同比年份同期值(tmpHistoryCurrentVal的值)* 同比值(tbVal的值)
  952. val, _ := decimal.NewFromFloat(tmpHistoryCurrentVal).Mul(tbVal).RoundCeil(4).Float64()
  953. currentDateStr := currentDate.Format(utils.FormatDate)
  954. tmpData := &edbDataModel.EdbDataList{
  955. EdbDataId: edbInfoId + 10000000000 + lenAllData + k,
  956. EdbInfoId: edbInfoId,
  957. DataTime: currentDateStr,
  958. Value: val,
  959. DataTimestamp: currentDate.UnixNano() / 1e6,
  960. }
  961. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  962. allDataList = append(allDataList, tmpData)
  963. existMap[currentDateStr] = val
  964. // 最大最小值
  965. if val < minValue {
  966. minValue = val
  967. }
  968. if val > maxValue {
  969. maxValue = val
  970. }
  971. }
  972. return
  973. }
  974. // GetChartPredictEdbInfoDataListByRuleTbzscz 根据 同比增速差值 规则获取预测数据
  975. // 同比增速差值计算方式:
  976. // 1、首先计算出所选指标实际最新日期值的同比增速:(本期数值-同期数值)÷同期数值*100%
  977. // 2、根据预测截止日期的同比增速终值、最新日期值的同比增速、与最新日期距离截止日期的期数,计算出到截止日期为止的每一期的同比增速。(等差规则计算每一期的同比增速,结合去年同期值,计算出每一期的同比预测值)。公差=(末项-首项)÷(n-1),an=a1+(n-1)d,(n为正整数,n大于等于2)
  978. // 3、根据去年同期值和未来每一期的同比增速值,求出同比预测值,同比预测值=同期值*(1+同比增速)
  979. // 同比增速差值:计算最新数据的同比增速((本期数值-同期数值)÷同期数值*100%),结合同比增速终值与期数,计算每一期同比增速,进而求出同比预测值。
  980. //
  981. // 例:如上图所示指标,(1)最新日期值2022-12-31 141175 ,结合同期值,计算同比增速;
  982. // (2)同比增速终值,若为50%, 预测日期为2023-03-31,则根据(1)中的同比增速值与同比增速终值,计算出中间两期的同比增速;
  983. // (3)求出每一期的预测同比值,预测同比值=同期值*(1+同比增速)
  984. func GetChartPredictEdbInfoDataListByRuleTbzscz(edbInfoId int, tbEndValue float64, dayList []time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64) {
  985. allDataList := make([]*edbDataModel.EdbDataList, 0)
  986. allDataList = append(allDataList, realPredictEdbInfoData...)
  987. allDataList = append(allDataList, predictEdbInfoData...)
  988. newPredictEdbInfoData = predictEdbInfoData
  989. index := len(allDataList)
  990. // 获取近期数据的同比值
  991. if index <= 0 {
  992. return
  993. }
  994. lastData := allDataList[index-1]
  995. lastDayTime, _ := time.ParseInLocation(utils.FormatDate, lastData.DataTime, time.Local)
  996. var lastTb decimal.Decimal // 计算最新数据与上一期的数据同比值
  997. {
  998. //上一年的日期
  999. preDate := lastDayTime.AddDate(-1, 0, 0)
  1000. preDateStr := preDate.Format(utils.FormatDate)
  1001. if preValue, ok := existMap[preDateStr]; ok { //上一年同期找到
  1002. lastTb = (decimal.NewFromFloat(lastData.Value)).Sub(decimal.NewFromFloat(preValue)).Div(decimal.NewFromFloat(preValue))
  1003. } else {
  1004. switch frequency {
  1005. case "月度":
  1006. //向上和向下,各找一个月
  1007. nextDateDay := preDate
  1008. preDateDay := preDate
  1009. for i := 0; i <= 35; i++ {
  1010. nextDateDayStr := nextDateDay.Format(utils.FormatDate)
  1011. if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
  1012. lastTb = (decimal.NewFromFloat(lastData.Value)).Sub(decimal.NewFromFloat(preValue)).Div(decimal.NewFromFloat(preValue))
  1013. break
  1014. } else {
  1015. preDateDayStr := preDateDay.Format(utils.FormatDate)
  1016. if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
  1017. lastTb = (decimal.NewFromFloat(lastData.Value)).Sub(decimal.NewFromFloat(preValue)).Div(decimal.NewFromFloat(preValue))
  1018. break
  1019. }
  1020. }
  1021. nextDateDay = nextDateDay.AddDate(0, 0, 1)
  1022. preDateDay = preDateDay.AddDate(0, 0, -1)
  1023. }
  1024. case "季度", "年度":
  1025. if preValue, ok := existMap[preDateStr]; ok { //上一年同期->下一个月找到
  1026. lastTb = (decimal.NewFromFloat(lastData.Value)).Sub(decimal.NewFromFloat(preValue)).Div(decimal.NewFromFloat(preValue))
  1027. break
  1028. }
  1029. default:
  1030. nextDateDay := preDate
  1031. preDateDay := preDate
  1032. for i := 0; i < 35; i++ {
  1033. nextDateDayStr := nextDateDay.Format(utils.FormatDate)
  1034. if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
  1035. lastTb = (decimal.NewFromFloat(lastData.Value)).Sub(decimal.NewFromFloat(preValue)).Div(decimal.NewFromFloat(preValue))
  1036. break
  1037. } else {
  1038. preDateDayStr := preDateDay.Format(utils.FormatDate)
  1039. if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
  1040. lastTb = (decimal.NewFromFloat(lastData.Value)).Sub(decimal.NewFromFloat(preValue)).Div(decimal.NewFromFloat(preValue))
  1041. break
  1042. } else {
  1043. //fmt.Println("pre not find:", preDateStr, "i:", i)
  1044. }
  1045. }
  1046. nextDateDay = nextDateDay.AddDate(0, 0, 1)
  1047. preDateDay = preDateDay.AddDate(0, 0, -1)
  1048. }
  1049. }
  1050. }
  1051. }
  1052. //获取后面的预测数据
  1053. lenDay := len(dayList)
  1054. tbEndValueDecimal := decimal.NewFromFloat(tbEndValue)
  1055. avgTbVal := tbEndValueDecimal.Sub(lastTb).Div(decimal.NewFromInt(int64(lenDay)))
  1056. predictEdbInfoData = make([]*edbDataModel.EdbDataList, 0)
  1057. for k, currentDate := range dayList {
  1058. var tbValue decimal.Decimal
  1059. if k == lenDay-1 { // 如果是最后的日期了,那么就用终值去计算
  1060. tbValue = tbEndValueDecimal.Add(decimal.NewFromInt(1))
  1061. } else { // 最近数据的同比值 + (平均增值乘以当前期数)
  1062. tbValue = lastTb.Add(avgTbVal.Mul(decimal.NewFromInt(int64(k + 1)))).Add(decimal.NewFromInt(1))
  1063. }
  1064. tmpData := &edbDataModel.EdbDataList{
  1065. EdbDataId: edbInfoId + 100000 + index + k,
  1066. EdbInfoId: edbInfoId,
  1067. DataTime: currentDate.Format(utils.FormatDate),
  1068. //Value: dataValue,
  1069. DataTimestamp: currentDate.UnixNano() / 1e6,
  1070. }
  1071. var val float64
  1072. var calculateStatus bool //计算结果
  1073. //currentItem := existMap[av]
  1074. //上一年的日期
  1075. preDate := currentDate.AddDate(-1, 0, 0)
  1076. preDateStr := preDate.Format(utils.FormatDate)
  1077. if preValue, ok := existMap[preDateStr]; ok { //上一年同期找到
  1078. val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).RoundCeil(4).Float64()
  1079. calculateStatus = true
  1080. } else {
  1081. switch frequency {
  1082. case "月度":
  1083. //向上和向下,各找一个月
  1084. nextDateDay := preDate
  1085. preDateDay := preDate
  1086. for i := 0; i <= 35; i++ {
  1087. nextDateDayStr := nextDateDay.Format(utils.FormatDate)
  1088. if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
  1089. val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).RoundCeil(4).Float64()
  1090. calculateStatus = true
  1091. break
  1092. } else {
  1093. preDateDayStr := preDateDay.Format(utils.FormatDate)
  1094. if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
  1095. val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).RoundCeil(4).Float64()
  1096. calculateStatus = true
  1097. break
  1098. }
  1099. }
  1100. nextDateDay = nextDateDay.AddDate(0, 0, 1)
  1101. preDateDay = preDateDay.AddDate(0, 0, -1)
  1102. }
  1103. case "季度", "年度":
  1104. if preValue, ok := existMap[preDateStr]; ok { //上一年同期->下一个月找到
  1105. val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).RoundCeil(4).Float64()
  1106. calculateStatus = true
  1107. break
  1108. }
  1109. default:
  1110. nextDateDay := preDate
  1111. preDateDay := preDate
  1112. for i := 0; i < 35; i++ {
  1113. nextDateDayStr := nextDateDay.Format(utils.FormatDate)
  1114. if preValue, ok := existMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
  1115. val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).RoundCeil(4).Float64()
  1116. calculateStatus = true
  1117. break
  1118. } else {
  1119. preDateDayStr := preDateDay.Format(utils.FormatDate)
  1120. if preValue, ok := existMap[preDateDayStr]; ok { //上一年同期->上一个月找到
  1121. val, _ = decimal.NewFromFloat(preValue).Mul(tbValue).RoundCeil(4).Float64()
  1122. calculateStatus = true
  1123. break
  1124. } else {
  1125. //fmt.Println("pre not find:", preDateStr, "i:", i)
  1126. }
  1127. }
  1128. nextDateDay = nextDateDay.AddDate(0, 0, 1)
  1129. preDateDay = preDateDay.AddDate(0, 0, -1)
  1130. }
  1131. }
  1132. }
  1133. if calculateStatus {
  1134. tmpData.Value = val
  1135. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  1136. allDataList = append(allDataList, tmpData)
  1137. existMap[tmpData.DataTime] = val
  1138. // 最大最小值
  1139. if val < minValue {
  1140. minValue = val
  1141. }
  1142. if val > maxValue {
  1143. maxValue = val
  1144. }
  1145. }
  1146. }
  1147. return
  1148. }
  1149. // RuleLineNhConf 一元线性拟合规则的配置
  1150. type RuleLineNhConf struct {
  1151. StartDate string `description:"开始日期"`
  1152. EndDate string `description:"结束日期"`
  1153. MoveDay int `description:"移动天数"`
  1154. EdbInfoId int `description:"指标id"`
  1155. }
  1156. // GetChartPredictEdbInfoDataListByRuleLineNh 根据 一元线性拟合 的计算规则获取预测数据
  1157. //
  1158. // 选择被预测的指标B(作为自变量,非预测指标),选择指标A(作为因变量,可以是基础指标和预测指标)
  1159. // 2、选择拟合时间段,起始日期至今或指定时间段,选择至今,在计算时截止到指标B的最新日期
  1160. // 3、设定A领先B时间(天),正整数、负整数、0
  1161. // 4、调用拟合残差的数据预处理和算法,给出拟合方程Y=aX+b的系数a,b
  1162. // 5、指标A代入拟合方程得到拟合预测指标B',拟合预测指标使用指标B的频度,在指标B的实际值后面连接拟合预测指标B'对应日期的预测值
  1163. //
  1164. // 注:选择预测截止日期,若所选日期 ≤ 指标A设置领先后的日期序列,则预测指标日期最新日期有值(在指标B'的有值范围内);若所选日期 > 指标A设置领先后的日期序列,则预测指标只到指标A领先后的日期序列(超出指标B'的有值范围,最多到指标B'的最新值);指标A、B更新后,更新预测指标
  1165. func GetChartPredictEdbInfoDataListByRuleLineNh(edbInfoId int, dayList []time.Time, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, newNhccDataMap, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64, err error) {
  1166. allDataList := make([]*edbDataModel.EdbDataList, 0)
  1167. allDataList = append(allDataList, realPredictEdbInfoData...)
  1168. allDataList = append(allDataList, predictEdbInfoData...)
  1169. newPredictEdbInfoData = predictEdbInfoData
  1170. lenAllData := len(allDataList)
  1171. if lenAllData <= 0 {
  1172. return
  1173. }
  1174. for k, currentDate := range dayList {
  1175. // 动态拟合残差值数据
  1176. currentDateStr := currentDate.Format(utils.FormatDate)
  1177. val, ok := newNhccDataMap[currentDateStr]
  1178. if !ok {
  1179. continue
  1180. }
  1181. tmpData := &edbDataModel.EdbDataList{
  1182. EdbDataId: edbInfoId + 100000 + lenAllData + k,
  1183. EdbInfoId: edbInfoId,
  1184. DataTime: currentDateStr,
  1185. Value: val,
  1186. DataTimestamp: currentDate.UnixNano() / 1e6,
  1187. }
  1188. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  1189. allDataList = append(allDataList, tmpData)
  1190. existMap[currentDateStr] = val
  1191. // 最大最小值
  1192. if val < minValue {
  1193. minValue = val
  1194. }
  1195. if val > maxValue {
  1196. maxValue = val
  1197. }
  1198. }
  1199. return
  1200. }
  1201. // getCalculateNhccData 获取计算出来的 拟合残差 数据
  1202. func getCalculateNhccData(secondDataList []*edbDataModel.EdbDataList, ruleConf RuleLineNhConf) (newBDataMap map[string]float64, err error) {
  1203. firstEdbInfoId := ruleConf.EdbInfoId
  1204. moveDay := ruleConf.MoveDay
  1205. startDate, _ := time.ParseInLocation(utils.FormatDate, ruleConf.StartDate, time.Local)
  1206. endDate, _ := time.ParseInLocation(utils.FormatDate, ruleConf.EndDate, time.Local)
  1207. //查询当前指标现有的数据
  1208. edbInfo, err := edbInfoModel.GetEdbInfoById(firstEdbInfoId)
  1209. if err != nil {
  1210. return
  1211. }
  1212. //第一个指标
  1213. aDataList := make([]edbDataModel.EdbDataList, 0)
  1214. aDataMap := make(map[string]float64)
  1215. {
  1216. //第一个指标的数据列表
  1217. var firstDataList []*edbDataModel.EdbDataList
  1218. switch edbInfo.EdbInfoType {
  1219. case 0:
  1220. firstDataList, err = edbDataModel.GetEdbDataList(edbInfo.Source, edbInfo.EdbInfoId, ``, ``)
  1221. case 1:
  1222. _, firstDataList, _, _, err, _ = GetPredictDataListByPredictEdbInfoId(edbInfo.EdbInfoId, ``, ``, false)
  1223. default:
  1224. err = errors.New(fmt.Sprint("获取失败,指标类型异常", edbInfo.EdbInfoType))
  1225. }
  1226. if err != nil {
  1227. return
  1228. }
  1229. aDataList, aDataMap = handleNhccData(firstDataList, moveDay)
  1230. }
  1231. //第二个指标
  1232. bDataList := make([]edbDataModel.EdbDataList, 0)
  1233. bDataMap := make(map[string]float64)
  1234. {
  1235. bDataList, bDataMap = handleNhccData(secondDataList, 0)
  1236. }
  1237. if len(aDataList) <= 0 {
  1238. err = errors.New("指标A没有数据")
  1239. return
  1240. }
  1241. if len(bDataList) <= 0 {
  1242. err = errors.New("指标B没有数据")
  1243. return
  1244. }
  1245. // 拟合残差计算的结束日期判断
  1246. {
  1247. endAData := aDataList[len(aDataList)-1]
  1248. tmpEndDate, tmpErr := time.ParseInLocation(utils.FormatDate, endAData.DataTime, time.Local)
  1249. if tmpErr != nil {
  1250. err = tmpErr
  1251. return
  1252. }
  1253. // 如果A指标的最新数据日期早于拟合残差的结束日期,那么就用A指标的最新数据日期
  1254. if tmpEndDate.Before(endDate) {
  1255. endDate = tmpEndDate
  1256. }
  1257. endBData := bDataList[len(bDataList)-1]
  1258. tmpEndDate, tmpErr = time.ParseInLocation(utils.FormatDate, endBData.DataTime, time.Local)
  1259. if tmpErr != nil {
  1260. err = tmpErr
  1261. return
  1262. }
  1263. // 如果B指标的最新数据日期早于拟合残差的结束日期,那么就用A指标的最新数据日期
  1264. if tmpEndDate.Before(endDate) {
  1265. endDate = tmpEndDate
  1266. }
  1267. }
  1268. // 计算线性方程公式
  1269. var a, b float64
  1270. {
  1271. coordinateData := make([]utils.Coordinate, 0)
  1272. for i := startDate; i.Before(endDate) || i.Equal(endDate); i = i.AddDate(0, 0, 1) {
  1273. dateStr := i.Format(utils.FormatDate)
  1274. xValue, ok := aDataMap[dateStr]
  1275. if !ok {
  1276. err = errors.New("指标A日期:" + dateStr + "数据异常,导致计算线性方程公式失败")
  1277. return
  1278. }
  1279. yValue, ok := bDataMap[dateStr]
  1280. if !ok {
  1281. err = errors.New("指标B日期:" + dateStr + "数据异常,导致计算线性方程公式失败")
  1282. return
  1283. }
  1284. tmpCoordinate := utils.Coordinate{
  1285. X: xValue,
  1286. Y: yValue,
  1287. }
  1288. coordinateData = append(coordinateData, tmpCoordinate)
  1289. }
  1290. a, b = utils.GetLinearResult(coordinateData)
  1291. }
  1292. if math.IsNaN(a) || math.IsNaN(b) {
  1293. err = errors.New("线性方程公式生成失败")
  1294. return
  1295. }
  1296. //fmt.Println("a:", a, ";======b:", b)
  1297. //计算B’
  1298. newBDataMap = make(map[string]float64)
  1299. {
  1300. //B’=aA+b
  1301. aDecimal := decimal.NewFromFloat(a)
  1302. bDecimal := decimal.NewFromFloat(b)
  1303. for _, aData := range aDataList {
  1304. xDecimal := decimal.NewFromFloat(aData.Value)
  1305. val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).RoundCeil(4).Float64()
  1306. newBDataMap[aData.DataTime] = val
  1307. }
  1308. }
  1309. return
  1310. }
  1311. // handleNhccData 处理拟合残差需要的数据
  1312. func handleNhccData(dataList []*edbDataModel.EdbDataList, moveDay int) (newDataList []edbDataModel.EdbDataList, dateDataMap map[string]float64) {
  1313. dateMap := make(map[time.Time]float64)
  1314. var minDate, maxDate time.Time
  1315. dateDataMap = make(map[string]float64)
  1316. for _, v := range dataList {
  1317. currDate, _ := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
  1318. if minDate.IsZero() || currDate.Before(minDate) {
  1319. minDate = currDate
  1320. }
  1321. if maxDate.IsZero() || currDate.After(maxDate) {
  1322. maxDate = currDate
  1323. }
  1324. dateMap[currDate] = v.Value
  1325. }
  1326. // 处理领先、滞后数据
  1327. newDateMap := make(map[time.Time]float64)
  1328. for currDate, value := range dateMap {
  1329. newDate := currDate.AddDate(0, 0, moveDay)
  1330. newDateMap[newDate] = value
  1331. }
  1332. minDate = minDate.AddDate(0, 0, moveDay)
  1333. maxDate = maxDate.AddDate(0, 0, moveDay)
  1334. // 开始平移天数
  1335. dayNum := utils.GetTimeSubDay(minDate, maxDate)
  1336. for i := 0; i <= dayNum; i++ {
  1337. currDate := minDate.AddDate(0, 0, i)
  1338. tmpValue, ok := newDateMap[currDate]
  1339. if !ok {
  1340. // 万一没有数据,那么就过滤当次循环
  1341. if len(newDataList) <= 0 {
  1342. continue
  1343. }
  1344. //找不到数据,那么就用前面的数据吧
  1345. tmpValue = newDataList[len(newDataList)-1].Value
  1346. }
  1347. tmpData := edbDataModel.EdbDataList{
  1348. //EdbDataId: 0,
  1349. DataTime: currDate.Format(utils.FormatDate),
  1350. Value: tmpValue,
  1351. }
  1352. dateDataMap[tmpData.DataTime] = tmpData.Value
  1353. newDataList = append(newDataList, tmpData)
  1354. }
  1355. return
  1356. }
  1357. // GetChartPredictEdbInfoDataListByRuleNAnnualAverage 根据 N年均值 规则获取预测数据
  1358. // ETA预测规则:N年均值:过去N年同期均值。过去N年可以连续或者不连续,指标数据均用线性插值补全为日度数据后计算;
  1359. func GetChartPredictEdbInfoDataListByRuleNAnnualAverage(edbInfoId int, configValue string, dayList []time.Time, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64, err error) {
  1360. // 获取配置的年份列表
  1361. yearList, _, err := getYearListBySeasonConf(configValue)
  1362. if err != nil {
  1363. return
  1364. }
  1365. allDataList := make([]*edbDataModel.EdbDataList, 0)
  1366. allDataList = append(allDataList, realPredictEdbInfoData...)
  1367. allDataList = append(allDataList, predictEdbInfoData...)
  1368. newPredictEdbInfoData = predictEdbInfoData
  1369. // 插值法数据处理
  1370. handleDataMap := make(map[string]float64)
  1371. err = HandleDataByLinearRegression(allDataList, handleDataMap)
  1372. if err != nil {
  1373. return
  1374. }
  1375. index := len(allDataList)
  1376. //获取后面的预测数据
  1377. predictEdbInfoData = make([]*edbDataModel.EdbDataList, 0)
  1378. for k, currentDate := range dayList {
  1379. // 如果遇到闰二月,如2.29,去掉该天数据
  1380. if strings.Contains(currentDate.Format(utils.FormatDate), "02-29") {
  1381. continue
  1382. }
  1383. tmpK := len(allDataList) - 1 //上1期数据的下标
  1384. lastDayData := allDataList[tmpK] // 上1期的数据
  1385. tmpHistoryVal := decimal.NewFromFloat(0) //往期的差值总和
  1386. tmpHistoryValNum := 0 // 往期差值计算的数量
  1387. for _, year := range yearList {
  1388. //前几年当日的日期
  1389. tmpHistoryCurrentDate := currentDate.AddDate(year-currentDate.Year(), 0, 0)
  1390. if val, ok := handleDataMap[tmpHistoryCurrentDate.Format(utils.FormatDate)]; ok {
  1391. tmpHistoryVal = tmpHistoryVal.Add(decimal.NewFromFloat(val))
  1392. tmpHistoryValNum++
  1393. break
  1394. }
  1395. }
  1396. //计算的差值与选择的年份数量不一致,那么当前日期不计算
  1397. if tmpHistoryValNum != len(yearList) {
  1398. continue
  1399. }
  1400. val, _ := tmpHistoryVal.Div(decimal.NewFromInt(int64(tmpHistoryValNum))).RoundCeil(4).Float64()
  1401. currentDateStr := currentDate.Format(utils.FormatDate)
  1402. tmpData := &edbDataModel.EdbDataList{
  1403. EdbDataId: edbInfoId + 100000 + index + k,
  1404. EdbInfoId: edbInfoId,
  1405. DataTime: currentDateStr,
  1406. Value: val,
  1407. DataTimestamp: currentDate.UnixNano() / 1e6,
  1408. }
  1409. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  1410. allDataList = append(allDataList, tmpData)
  1411. existMap[currentDateStr] = val
  1412. // 继续使用插值法补充新预测日期的数据之间的值
  1413. err = HandleDataByLinearRegression([]*edbDataModel.EdbDataList{
  1414. lastDayData, tmpData,
  1415. }, handleDataMap)
  1416. if err != nil {
  1417. return
  1418. }
  1419. // 最大最小值
  1420. if val < minValue {
  1421. minValue = val
  1422. }
  1423. if val > maxValue {
  1424. maxValue = val
  1425. }
  1426. }
  1427. return
  1428. }
  1429. // AnnualValueInversionConf 年度值倒推规则
  1430. type AnnualValueInversionConf struct {
  1431. Value float64 `description:"年度值"`
  1432. Type int `description:"分配方式,1:均值法;2:同比法"`
  1433. Year int `description:"同比年份"`
  1434. }
  1435. // GetChartPredictEdbInfoDataListByRuleAnnualValueInversion 根据 年度值倒推 规则获取预测数据
  1436. // ETA预测规则:年度值倒推:设定年度值,余额=年度值-年初至今累计值(算法参考累计值),进行余额分配,均值法分配时保证每期数值相等(日度/周度:剩余期数=剩余自然日历天数/今年指标最新日期自然日历天数*今年至今指标数据期数;旬度/月度/季度/半年度:剩余期数=全年期数(36\12\4\2)-今年至今自然日历期数),同比法保证每期同比相等(同比增速=余额/同比年份相应日期的余额,预测值等于同比年份同期值*同比增速)
  1437. // 举例:
  1438. // 指标A 日度 最新日期 2023-05-19 年初至今累计值100
  1439. // 设置年度值1000
  1440. // 则余额=1000-100=900
  1441. // 均值法分配:剩余期数=226/139*120=195.11
  1442. // 今年之后的每一期预测值=900/195.11=4.6128
  1443. // 同比法分配:同比增速=900/同比年份5.19的余额
  1444. // 预测值=同比年份5-20的值*(1+同比增速)
  1445. func GetChartPredictEdbInfoDataListByRuleAnnualValueInversion(edbInfoId int, configValue string, dayList []time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*edbDataModel.EdbDataList, existMap map[string]float64) (newPredictEdbInfoData []*edbDataModel.EdbDataList, minValue, maxValue float64, err error) {
  1446. if frequency == "年度" {
  1447. err = errors.New("当前指标频度是年度,不允许配置年度值倒推")
  1448. return
  1449. }
  1450. // 获取配置
  1451. var annualValueInversionConf AnnualValueInversionConf
  1452. err = json.Unmarshal([]byte(configValue), &annualValueInversionConf)
  1453. if err != nil {
  1454. err = errors.New("年度值倒推配置信息异常:" + err.Error())
  1455. return
  1456. }
  1457. allDataList := make([]*edbDataModel.EdbDataList, 0)
  1458. allDataList = append(allDataList, realPredictEdbInfoData...)
  1459. allDataList = append(allDataList, predictEdbInfoData...)
  1460. newPredictEdbInfoData = predictEdbInfoData
  1461. index := len(allDataList)
  1462. // 配置的年度值
  1463. yearValueConfig := annualValueInversionConf.Value
  1464. // 当前年的日期
  1465. currDayTime := dayList[0]
  1466. lastDayTime := dayList[len(dayList)-1]
  1467. if currDayTime.Year() != lastDayTime.Year() {
  1468. err = errors.New("年度值倒推不支持跨年预测")
  1469. return
  1470. }
  1471. // 均值法
  1472. if annualValueInversionConf.Type == 1 {
  1473. // 当前年的期数
  1474. currYearN := 0
  1475. var currYearVal float64
  1476. for _, v := range allDataList {
  1477. currTime, tmpErr := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
  1478. if tmpErr != nil {
  1479. err = tmpErr
  1480. return
  1481. }
  1482. // 只是计算今年的
  1483. if currTime.Year() != currDayTime.Year() {
  1484. continue
  1485. }
  1486. currYearN++
  1487. currYearVal = currYearVal + v.Value
  1488. }
  1489. var averageVal float64
  1490. switch frequency {
  1491. case "半年度":
  1492. averageVal, _ = (decimal.NewFromFloat(yearValueConfig).Sub(decimal.NewFromFloat(currYearVal))).Div(decimal.NewFromInt(int64(2 - currYearN))).Float64()
  1493. case "季度":
  1494. averageVal, _ = (decimal.NewFromFloat(yearValueConfig).Sub(decimal.NewFromFloat(currYearVal))).Div(decimal.NewFromInt(int64(4 - currYearN))).Float64()
  1495. case "月度":
  1496. averageVal, _ = (decimal.NewFromFloat(yearValueConfig).Sub(decimal.NewFromFloat(currYearVal))).Div(decimal.NewFromInt(int64(12 - currYearN))).Float64()
  1497. case "旬度":
  1498. averageVal, _ = (decimal.NewFromFloat(yearValueConfig).Sub(decimal.NewFromFloat(currYearVal))).Div(decimal.NewFromInt(int64(36 - currYearN))).Float64()
  1499. case "周度", "日度":
  1500. //剩余期数=剩余自然日历天数/今年指标最新日期自然日历天数*今年至今指标数据期数
  1501. // 当前年的第一天
  1502. yearFirstDay := time.Date(currDayTime.Year(), 1, 1, 0, 0, 0, 0, time.Local)
  1503. subDay := utils.GetTimeSubDay(yearFirstDay, currDayTime) + 1
  1504. // 当前年的最后一天
  1505. yearLastDay := time.Date(currDayTime.Year(), 12, 31, 0, 0, 0, 0, time.Local)
  1506. subDay2 := utils.GetTimeSubDay(yearFirstDay, yearLastDay) + 1
  1507. surplusN := decimal.NewFromInt(int64(subDay2 - subDay)).Div(decimal.NewFromInt(int64(subDay))).Mul(decimal.NewFromInt(int64(currYearN)))
  1508. averageVal, _ = (decimal.NewFromFloat(annualValueInversionConf.Value).Sub(decimal.NewFromFloat(currYearVal))).Div(surplusN).Round(4).Float64()
  1509. }
  1510. for k, currentDate := range dayList {
  1511. currentDateStr := currentDate.Format(utils.FormatDate)
  1512. tmpData := &edbDataModel.EdbDataList{
  1513. EdbDataId: edbInfoId + 100000 + index + k,
  1514. EdbInfoId: edbInfoId,
  1515. DataTime: currentDateStr,
  1516. Value: averageVal,
  1517. DataTimestamp: currentDate.UnixNano() / 1e6,
  1518. }
  1519. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  1520. allDataList = append(allDataList, tmpData)
  1521. existMap[currentDateStr] = averageVal
  1522. }
  1523. // 最大最小值
  1524. if averageVal < minValue {
  1525. minValue = averageVal
  1526. }
  1527. if averageVal > maxValue {
  1528. maxValue = averageVal
  1529. }
  1530. return
  1531. }
  1532. // 同比法分配
  1533. // 同比法保证每期同比相等(同比增速=余额/同比年份相应日期的余额,预测值等于同比年份同期值*同比增速);
  1534. // 同比法分配:同比增速=900/同比年份5.19的余额
  1535. // 每年截止到当前日期的累计值
  1536. dateTotalMap := make(map[time.Time]float64)
  1537. // 每年的累计值(计算使用)
  1538. yearTotalMap := make(map[int]float64)
  1539. for _, v := range allDataList {
  1540. currTime, tmpErr := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
  1541. if tmpErr != nil {
  1542. err = tmpErr
  1543. return
  1544. }
  1545. yearVal := yearTotalMap[currTime.Year()]
  1546. yearVal = yearVal + v.Value
  1547. yearTotalMap[currTime.Year()] = yearVal
  1548. dateTotalMap[currTime] = yearVal
  1549. }
  1550. //(同比增速=余额/同比年份相应日期的余额,预测值等于同比年份同期值*同比增速);
  1551. for k, currentDate := range dayList {
  1552. currYearBalance := yearValueConfig - yearTotalMap[currentDate.Year()] // 当年的余额
  1553. //同比年份相应日期
  1554. lastYear := annualValueInversionConf.Year + (currentDate.Year() - currDayTime.Year())
  1555. var lastDateTime time.Time
  1556. switch frequency {
  1557. case "半年度", "季度":
  1558. lastDateTime = time.Date(lastYear, currentDate.Month(), currentDate.Day(), 0, 0, 0, 0, currentDate.Location())
  1559. case "月度":
  1560. lastDateTime = time.Date(lastYear, currentDate.Month()+1, 1, 0, 0, 0, 0, currentDate.Location()).AddDate(0, 0, -1)
  1561. case "旬度":
  1562. if currentDate.Day() == 10 || currentDate.Day() == 20 {
  1563. lastDateTime = time.Date(lastYear, currentDate.Month(), currentDate.Day(), 0, 0, 0, 0, currentDate.Location())
  1564. } else {
  1565. lastDateTime = time.Date(lastYear, currentDate.Month()+1, 1, 0, 0, 0, 0, currentDate.Location()).AddDate(0, 0, -1)
  1566. }
  1567. case "周度", "日度":
  1568. lastDateTime = time.Date(lastYear, currentDate.Month(), currentDate.Day(), 0, 0, 0, 0, currentDate.Location())
  1569. }
  1570. // 同比年份相应日期的累计值
  1571. var dateTotal float64
  1572. dateTotal, ok := dateTotalMap[lastDateTime]
  1573. if !ok { //如果没有找到这个日期,那么就往前面找,一直到找到这个累计值,或者找完这一年
  1574. yearFirstDayTime := time.Date(lastDateTime.Year(), 1, 1, 0, 0, 0, 0, lastDateTime.Location())
  1575. for tmpDateTime := lastDateTime.AddDate(0, 0, -1); tmpDateTime.After(yearFirstDayTime) || tmpDateTime.Equal(yearFirstDayTime); tmpDateTime = tmpDateTime.AddDate(0, 0, -1) {
  1576. dateTotal, ok = dateTotalMap[tmpDateTime]
  1577. if ok {
  1578. break
  1579. }
  1580. }
  1581. }
  1582. //同比年份相应日期的余额
  1583. lastYearDateBalance := yearValueConfig - dateTotal
  1584. // 同比增速=余额/同比年份相应日期的余额
  1585. tbVal := decimal.NewFromFloat(currYearBalance).Div(decimal.NewFromFloat(lastYearDateBalance))
  1586. // 获取同比年份同期值,获取失败的话,就不处理
  1587. if lastDateVal, ok := existMap[lastDateTime.Format(utils.FormatDate)]; ok {
  1588. //预测值 = 同比年份同期值*同比增速
  1589. tmpVal, _ := decimal.NewFromFloat(lastDateVal).Mul(tbVal).Round(4).Float64()
  1590. currentDateStr := currentDate.Format(utils.FormatDate)
  1591. tmpData := &edbDataModel.EdbDataList{
  1592. EdbDataId: edbInfoId + 100000 + index + k,
  1593. EdbInfoId: edbInfoId,
  1594. DataTime: currentDateStr,
  1595. Value: tmpVal,
  1596. DataTimestamp: currentDate.UnixNano() / 1e6,
  1597. }
  1598. newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
  1599. allDataList = append(allDataList, tmpData)
  1600. existMap[currentDateStr] = tmpVal
  1601. yearVal := yearTotalMap[currentDate.Year()]
  1602. yearVal = yearVal + tmpVal
  1603. yearTotalMap[currentDate.Year()] = yearVal
  1604. dateTotalMap[currentDate] = yearVal
  1605. // 最大最小值
  1606. if tmpVal < minValue {
  1607. minValue = tmpVal
  1608. }
  1609. if tmpVal > maxValue {
  1610. maxValue = tmpVal
  1611. }
  1612. }
  1613. }
  1614. return
  1615. }
  1616. // getYearListBySeasonConf 根据配置获取年份列表
  1617. func getYearListBySeasonConf(configValue string) (yearList []int, seasonConf SeasonConf, err error) {
  1618. tmpErr := json.Unmarshal([]byte(configValue), &seasonConf)
  1619. if tmpErr != nil {
  1620. err = errors.New("年份配置信息异常:" + tmpErr.Error())
  1621. return
  1622. }
  1623. //选择方式,1:连续N年;2:指定年份
  1624. if seasonConf.YearType == 1 {
  1625. if seasonConf.NValue < 1 {
  1626. err = errors.New("连续N年不允许小于1")
  1627. return
  1628. }
  1629. currYear := time.Now().Year()
  1630. for i := 0; i < seasonConf.NValue; i++ {
  1631. yearList = append(yearList, currYear-i-1)
  1632. }
  1633. } else {
  1634. yearList = seasonConf.YearList
  1635. }
  1636. return
  1637. }