Guest User

Untitled

a guest
Nov 22nd, 2017
64
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.80 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7. namespace Mathematics
  8. {
  9. public class HoltWinters
  10. {
  11. /// <summary>
  12. /// returns best initial trend factor so that we can start forecasting.
  13. /// </summary>
  14. public static double InitialTrendFactor(double[] data, int seasonLength)
  15. {
  16. if (data.Length < seasonLength * 2)
  17. {
  18. throw new Exception("Not enough data to compute initial trend factor, need at least two seasons");
  19. }
  20.  
  21. double tot = 0;
  22. for (int i = 0; i < seasonLength; i++)
  23. {
  24. tot += (data[i + seasonLength] - data[i]) / (double)seasonLength;
  25.  
  26. }
  27. return tot / (double)seasonLength;
  28. }
  29.  
  30. /// <summary>
  31. /// returns a list of best initial seasonal factor so that we can start forecasting.
  32. /// </summary>
  33. public static double[] InitialSeasonalFactors(double[] data, int seasonLength)
  34. {
  35.  
  36. int nSeasons = data.Length / seasonLength;
  37. if (nSeasons == 0)
  38. {
  39. throw new Exception("Not enough data to compute initial seasonal factors, at least one full season required");
  40. }
  41.  
  42. // take the same offset from each of the seasons and average it
  43. double[] seasonAverages = new double[nSeasons];
  44.  
  45. for (int j = 0; j < nSeasons; j++)
  46. {
  47. double sum = 0;
  48. for (int i = 0; i < seasonLength; i++)
  49. {
  50. sum += data[j * seasonLength + i];
  51.  
  52. }
  53. seasonAverages[j] = sum / (double)seasonLength;
  54. }
  55.  
  56. double[] result = new double[seasonLength];
  57. for (int i = 0; i < seasonLength; i++)
  58. {
  59. double sumOverAvg = 0;
  60. for (int j = 0; j < nSeasons; j++)
  61. {
  62. sumOverAvg += data[j * seasonLength + i] / seasonAverages[j];
  63. }
  64. result[i] = sumOverAvg / (double)nSeasons;
  65. }
  66.  
  67. return result;
  68. }
  69.  
  70. /// <summary>
  71. /// performs triple exponential smoothing (multiplicative) on data
  72. /// You can obtain trend and seasonal from InitialTrendFactor and InitialSeasonalFactors.
  73. /// Best alpha, beta and gamma can be calculated by repeatedly calling
  74. /// TripleExponentialSmoothing to find the combination with the smallest SSE,
  75. /// you can use MinimiseSSE for this.
  76. /// </summary>
  77. /// <param name="seasonLength">number of data points per season</param>
  78. /// <param name="trend">initial trend</param>
  79. /// <param name="seasonal">slice of initial seasonal</param>
  80. /// <param name="alpha">smoothing factor</param>
  81. /// <param name="beta">smoothing factor</param>
  82. /// <param name="gamma">smoothing factor</param>
  83. /// <returns>
  84. /// A new slice of smoothed and forcasted data points, a slice of deviation
  85. /// such that y±y*d could be used as "confidence bands" as well as SSE
  86. /// (Sum of Squared Errors)
  87. /// </returns>
  88. public static double TripleExponentialSmoothing(double[] data, int seasonLength, double trend, double[] seasonal
  89. , int nPredictions, double alpha, double beta, double gamma
  90. , out double[] result, out double[] deviation)
  91. {
  92. double sse = 0;
  93. double level = 0, lastLevel = 0;
  94. double[] seasonalDev = new double[seasonLength];
  95.  
  96. result = new double[data.Length + nPredictions];
  97. deviation = new double[data.Length + nPredictions];
  98.  
  99. if ((alpha <= 0 || alpha >= 1) || (beta <= 0 || beta >= 1) || (gamma <= 0 || gamma >= 1))
  100. {
  101. return double.MaxValue;
  102. }
  103.  
  104. for (int i = 0; i < data.Length + nPredictions; i++)
  105. {
  106.  
  107. if (i == 0)
  108. {
  109. level = data[i];
  110. result[i] = data[i];
  111. continue;
  112. }
  113.  
  114. if (i >= data.Length)
  115. {
  116. // we are forecasting
  117. int m = i - data.Length + 1;
  118.  
  119. result[i] = (level + (double)(m) * trend) * seasonal[i % seasonLength];
  120.  
  121. }
  122. else
  123. {
  124. double val = data[i];
  125.  
  126. lastLevel = level;
  127. level = alpha * val / seasonal[i % seasonLength] + (1 - alpha) * (level + trend);
  128. trend = beta * (level - lastLevel) + (1 - beta) * trend;
  129.  
  130. seasonal[i % seasonLength] = gamma * val / level + (1 - gamma) * seasonal[i % seasonLength];
  131.  
  132. result[i] = (level + trend) * seasonal[i % seasonLength];
  133.  
  134. if (i > 2)
  135. {
  136. // smoothed deviation for confidence band
  137. double factor = result[i] / val;
  138. if (factor > 1)
  139. {
  140. factor = 1 / factor;
  141. }
  142. seasonalDev[i % seasonLength] = gamma * (1 - factor) + (1 - gamma) * seasonalDev[i % seasonLength];
  143. // SSE
  144. double diff = result[i] - val;
  145. sse += diff * diff;
  146. }
  147. }
  148. deviation[i] = seasonalDev[i % seasonLength];
  149. }
  150.  
  151. return sse;
  152. }
  153.  
  154. /// <summary>
  155. // HWMinimizeSSE repeatedly executes TripleExponentialSmoothing over
  156. // data, season, seasonLength, trend and nPred with varying alpha, beta and gamma using
  157. // the BOBYQA algorithm to minimize SSE. It returns the final
  158. // (best) smooth and deviation returned by TripleExponentialSmoothing, as
  159. // well as the resulting alpha, beta, gamma (which can be reused later), k
  160. // (number of passes) and e (evocation count for
  161. // TripleExponentialSmoothing).
  162. /// </summary>
  163. public static double[] MinimizeSSE(double[] data, int seasonLength, int nPredictions,
  164. int maxIterations, out double alpha, out double beta, out double gamma)
  165. {
  166. double trend = InitialTrendFactor(data, seasonLength);
  167. double[] seasonal = InitialSeasonalFactors(data, seasonLength);
  168.  
  169. BobyqaFunctionDelegate callFn = delegate (int l, double[] x)
  170. {
  171. double[] smooth, deviation;
  172.  
  173. double[] sc = new double[seasonal.Length];
  174. Array.Copy(seasonal, sc, sc.Length);
  175.  
  176. double sse = TripleExponentialSmoothing(data, seasonLength, trend, sc, nPredictions
  177. , x[0], x[1], x[2], out smooth, out deviation);
  178.  
  179. return sse;
  180. };
  181.  
  182. double[] startPoints = new double[] { 0.5, 0.2, 0.2 };
  183. int n = startPoints.Length;
  184.  
  185. var optimizer = new BOBYQA(n, callFn
  186. , new double[] { 0, 0, 0 }, new double[] { 1, 1, 1 })
  187. {
  188. TrustRegionRadiusEnd = 1.0e-8,
  189. MaximumFunctionCalls = maxIterations,
  190. Logger = null // Console.Out
  191. };
  192.  
  193. optimizer.InterpolationConditions = 2 * n + 1;
  194. var r = optimizer.FindMinimum(startPoints);
  195.  
  196. alpha = r.X[0];
  197. beta = r.X[1];
  198. gamma = r.X[2];
  199.  
  200. double[] result, dev;
  201.  
  202. TripleExponentialSmoothing(data, seasonLength, trend, seasonal, nPredictions
  203. , alpha, beta, gamma, out result, out dev);
  204.  
  205. var pred = new double[nPredictions];
  206. Array.Copy(result, result.Length - nPredictions, pred, 0, nPredictions);
  207.  
  208. return pred;
  209. }
  210. }
  211. }
Add Comment
Please, Sign In to add comment