statcpp
C++17 Header-Only Statistics Library
Loading...
Searching...
No Matches
time_series.hpp
Go to the documentation of this file.
1
6#pragma once
7
8#include <cmath>
9#include <cstddef>
10#include <iterator>
11#include <stdexcept>
12#include <utility>
13#include <vector>
14
16
17namespace statcpp {
18
19// ============================================================================
20// Autocorrelation Function (ACF)
21// ============================================================================
22
44template <typename Iterator>
45double autocorrelation(Iterator first, Iterator last, std::size_t lag)
46{
47 auto n = static_cast<std::size_t>(std::distance(first, last));
48 if (n == 0) {
49 throw std::invalid_argument("statcpp::autocorrelation: empty range");
50 }
51 if (lag >= n) {
52 throw std::invalid_argument("statcpp::autocorrelation: lag must be less than n");
53 }
54 if (lag == 0) {
55 return 1.0; // Autocorrelation at lag 0 is always 1
56 }
57
58 double m = statcpp::mean(first, last);
59
60 // Variance (divided by N)
61 double var = 0.0;
62 for (auto it = first; it != last; ++it) {
63 double diff = static_cast<double>(*it) - m;
64 var += diff * diff;
65 }
66
67 if (var == 0.0) {
68 throw std::invalid_argument("statcpp::autocorrelation: zero variance");
69 }
70
71 // Autocovariance (lag k)
72 double cov = 0.0;
73 auto it1 = first;
74 auto it2 = first;
75 std::advance(it2, lag);
76
77 for (std::size_t i = 0; i < n - lag; ++i) {
78 cov += (static_cast<double>(*it1) - m) * (static_cast<double>(*it2) - m);
79 ++it1;
80 ++it2;
81 }
82
83 return cov / var;
84}
85
96template <typename Iterator>
97std::vector<double> acf(Iterator first, Iterator last, std::size_t max_lag)
98{
99 auto n = static_cast<std::size_t>(std::distance(first, last));
100 if (n == 0) {
101 throw std::invalid_argument("statcpp::acf: empty range");
102 }
103 if (max_lag >= n) {
104 max_lag = n - 1;
105 }
106
107 std::vector<double> result;
108 result.reserve(max_lag + 1);
109
110 for (std::size_t lag = 0; lag <= max_lag; ++lag) {
111 result.push_back(autocorrelation(first, last, lag));
112 }
113
114 return result;
115}
116
117// ============================================================================
118// Partial Autocorrelation Function (PACF)
119// ============================================================================
120
133template <typename Iterator>
134std::vector<double> pacf(Iterator first, Iterator last, std::size_t max_lag)
135{
136 auto n = static_cast<std::size_t>(std::distance(first, last));
137 if (n == 0) {
138 throw std::invalid_argument("statcpp::pacf: empty range");
139 }
140 if (max_lag >= n) {
141 max_lag = n - 1;
142 }
143
144 // First calculate ACF
145 auto r = acf(first, last, max_lag);
146
147 std::vector<double> result;
148 result.reserve(max_lag + 1);
149 result.push_back(1.0); // PACF(0) = 1
150
151 if (max_lag == 0) return result;
152
153 // Durbin-Levinson algorithm
154 std::vector<double> phi(max_lag + 1);
155 std::vector<double> phi_prev(max_lag + 1);
156
157 phi[1] = r[1];
158 result.push_back(r[1]);
159 phi_prev = phi; // Initialize phi_prev with phi[1] = r[1] before k=2 iteration
160
161 for (std::size_t k = 2; k <= max_lag; ++k) {
162 // Calculate phi_{k,k}
163 double num = r[k];
164 double den = 1.0;
165
166 for (std::size_t j = 1; j < k; ++j) {
167 num -= phi_prev[j] * r[k - j];
168 den -= phi_prev[j] * r[j];
169 }
170
171 if (std::abs(den) < 1e-15) {
172 phi[k] = 0.0;
173 } else {
174 phi[k] = num / den;
175 }
176
177 // Update phi_{k,j}
178 for (std::size_t j = 1; j < k; ++j) {
179 phi[j] = phi_prev[j] - phi[k] * phi_prev[k - j];
180 }
181
182 result.push_back(phi[k]);
183 phi_prev = phi;
184 }
185
186 return result;
187}
188
189// ============================================================================
190// Forecast Error Metrics
191// ============================================================================
192
206template <typename Iterator1, typename Iterator2>
207double mae(Iterator1 first1, Iterator1 last1, Iterator2 first2)
208{
209 auto n = static_cast<std::size_t>(std::distance(first1, last1));
210 if (n == 0) {
211 throw std::invalid_argument("statcpp::mae: empty range");
212 }
213
214 double sum = 0.0;
215 auto it2 = first2;
216 for (auto it1 = first1; it1 != last1; ++it1, ++it2) {
217 sum += std::abs(static_cast<double>(*it1) - static_cast<double>(*it2));
218 }
219
220 return sum / static_cast<double>(n);
221}
222
236template <typename Iterator1, typename Iterator2>
237double mse(Iterator1 first1, Iterator1 last1, Iterator2 first2)
238{
239 auto n = static_cast<std::size_t>(std::distance(first1, last1));
240 if (n == 0) {
241 throw std::invalid_argument("statcpp::mse: empty range");
242 }
243
244 double sum = 0.0;
245 auto it2 = first2;
246 for (auto it1 = first1; it1 != last1; ++it1, ++it2) {
247 double diff = static_cast<double>(*it1) - static_cast<double>(*it2);
248 sum += diff * diff;
249 }
250
251 return sum / static_cast<double>(n);
252}
253
265template <typename Iterator1, typename Iterator2>
266double rmse(Iterator1 first1, Iterator1 last1, Iterator2 first2)
267{
268 return std::sqrt(mse(first1, last1, first2));
269}
270
284template <typename Iterator1, typename Iterator2>
285double mape(Iterator1 first1, Iterator1 last1, Iterator2 first2)
286{
287 auto n = static_cast<std::size_t>(std::distance(first1, last1));
288 if (n == 0) {
289 throw std::invalid_argument("statcpp::mape: empty range");
290 }
291
292 double sum = 0.0;
293 std::size_t valid_count = 0;
294 auto it2 = first2;
295 for (auto it1 = first1; it1 != last1; ++it1, ++it2) {
296 double actual = static_cast<double>(*it1);
297 if (actual != 0.0) {
298 double predicted = static_cast<double>(*it2);
299 sum += std::abs((actual - predicted) / actual);
300 valid_count++;
301 }
302 }
303
304 if (valid_count == 0) {
305 throw std::invalid_argument("statcpp::mape: all actual values are zero");
306 }
307
308 return sum / static_cast<double>(valid_count) * 100.0;
309}
310
311// ============================================================================
312// Moving Average
313// ============================================================================
314
327template <typename Iterator>
328std::vector<double> moving_average(Iterator first, Iterator last, std::size_t window)
329{
330 auto n = static_cast<std::size_t>(std::distance(first, last));
331 if (n == 0) {
332 throw std::invalid_argument("statcpp::moving_average: empty range");
333 }
334 if (window == 0 || window > n) {
335 throw std::invalid_argument("statcpp::moving_average: invalid window size");
336 }
337
338 std::vector<double> data;
339 data.reserve(n);
340 for (auto it = first; it != last; ++it) {
341 data.push_back(static_cast<double>(*it));
342 }
343
344 std::vector<double> result;
345 result.reserve(n - window + 1);
346
347 double sum = 0.0;
348 for (std::size_t i = 0; i < window; ++i) {
349 sum += data[i];
350 }
351 result.push_back(sum / static_cast<double>(window));
352
353 for (std::size_t i = window; i < n; ++i) {
354 sum += data[i] - data[i - window];
355 result.push_back(sum / static_cast<double>(window));
356 }
357
358 return result;
359}
360
373template <typename Iterator>
374std::vector<double> exponential_moving_average(Iterator first, Iterator last, double alpha)
375{
376 auto n = static_cast<std::size_t>(std::distance(first, last));
377 if (n == 0) {
378 throw std::invalid_argument("statcpp::exponential_moving_average: empty range");
379 }
380 if (alpha <= 0.0 || alpha > 1.0) {
381 throw std::invalid_argument("statcpp::exponential_moving_average: alpha must be in (0, 1]");
382 }
383
384 std::vector<double> result;
385 result.reserve(n);
386
387 auto it = first;
388 double ema = static_cast<double>(*it);
389 result.push_back(ema);
390 ++it;
391
392 for (; it != last; ++it) {
393 ema = alpha * static_cast<double>(*it) + (1.0 - alpha) * ema;
394 result.push_back(ema);
395 }
396
397 return result;
398}
399
400// ============================================================================
401// Differencing
402// ============================================================================
403
416template <typename Iterator>
417std::vector<double> diff(Iterator first, Iterator last, std::size_t order = 1)
418{
419 auto n = static_cast<std::size_t>(std::distance(first, last));
420 if (n <= order) {
421 throw std::invalid_argument("statcpp::diff: insufficient data for differencing order");
422 }
423
424 std::vector<double> data;
425 data.reserve(n);
426 for (auto it = first; it != last; ++it) {
427 data.push_back(static_cast<double>(*it));
428 }
429
430 for (std::size_t d = 0; d < order; ++d) {
431 std::vector<double> new_data;
432 new_data.reserve(data.size() - 1);
433 for (std::size_t i = 1; i < data.size(); ++i) {
434 new_data.push_back(data[i] - data[i - 1]);
435 }
436 data = std::move(new_data);
437 }
438
439 return data;
440}
441
454template <typename Iterator>
455std::vector<double> seasonal_diff(Iterator first, Iterator last, std::size_t period)
456{
457 if (period == 0) {
458 throw std::invalid_argument("statcpp::seasonal_diff: period must be positive");
459 }
460
461 auto n = static_cast<std::size_t>(std::distance(first, last));
462 if (n <= period) {
463 throw std::invalid_argument("statcpp::seasonal_diff: insufficient data for period");
464 }
465
466 std::vector<double> data;
467 data.reserve(n);
468 for (auto it = first; it != last; ++it) {
469 data.push_back(static_cast<double>(*it));
470 }
471
472 std::vector<double> result;
473 result.reserve(n - period);
474
475 for (std::size_t i = period; i < n; ++i) {
476 result.push_back(data[i] - data[i - period]);
477 }
478
479 return result;
480}
481
482// ============================================================================
483// Lag
484// ============================================================================
485
498template <typename Iterator>
499std::vector<double> lag(Iterator first, Iterator last, std::size_t k)
500{
501 auto n = static_cast<std::size_t>(std::distance(first, last));
502 if (k >= n) {
503 throw std::invalid_argument("statcpp::lag: lag exceeds data length");
504 }
505
506 std::vector<double> result;
507 result.reserve(n - k);
508
509 auto it = first;
510 for (std::size_t i = 0; i < n - k; ++i, ++it) {
511 result.push_back(static_cast<double>(*it));
512 }
513
514 return result;
515}
516
517} // namespace statcpp
Basic statistical computation functions.
double rmse(Iterator1 first1, Iterator1 last1, Iterator2 first2)
Root Mean Squared Error (RMSE)
auto sum(Iterator first, Iterator last)
Sum.
double var(Iterator first, Iterator last, std::size_t ddof=0)
Variance (ddof = Delta Degrees of Freedom)
double mae(Iterator1 first1, Iterator1 last1, Iterator2 first2)
Mean Absolute Error (MAE)
std::vector< double > seasonal_diff(Iterator first, Iterator last, std::size_t period)
Seasonal differencing.
double mape(Iterator1 first1, Iterator1 last1, Iterator2 first2)
Mean Absolute Percentage Error (MAPE)
double mean(Iterator first, Iterator last)
Arithmetic mean.
double autocorrelation(Iterator first, Iterator last, std::size_t lag)
Calculate autocorrelation coefficient (lag k)
std::vector< double > lag(Iterator first, Iterator last, std::size_t k)
Generate lag series.
std::vector< double > diff(Iterator first, Iterator last, std::size_t order=1)
Difference series (first-order or d-th order differencing)
std::vector< double > pacf(Iterator first, Iterator last, std::size_t max_lag)
Calculate partial autocorrelation function (PACF) (Durbin-Levinson algorithm)
std::vector< double > acf(Iterator first, Iterator last, std::size_t max_lag)
Calculate autocorrelation function (ACF) (from lag 0 to max_lag)
double mse(Iterator1 first1, Iterator1 last1, Iterator2 first2)
Mean Squared Error (MSE)
std::vector< double > moving_average(Iterator first, Iterator last, std::size_t window)
Simple moving average.
std::vector< double > exponential_moving_average(Iterator first, Iterator last, double alpha)
Exponential moving average.