ztsdb
array_ops.hpp
1 // (C) 2016 Leonardo Silvestri
2 //
3 // This file is part of ztsdb.
4 //
5 // ztsdb is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // ztsdb is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with ztsdb. If not, see <http://www.gnu.org/licenses/>.
17 
18 
19 #ifndef ARRAY_OPS
20 #define ARRAY_OPS
21 
22 
23 #include "valuevector.hpp"
24 #include "functional"
25 #include "array.hpp"
26 #include "globals.hpp"
27 
28 
29 namespace arr {
30 
33  template<typename T>
34  Array<T>& rollmean_inplace(Array<T>& a, idx_type window, idx_type nbvalid)
35  {
36  size_t ringsz = 1;
37  while (ringsz < window) ringsz <<= 1;
38  vector<T> ring(ringsz);
39 
40  for (idx_type c=0; c<a.v.size(); c++) {
41  idx_type nbv = 0;
42  T mean = 0; // R is the result type (because
43  // e.g. for T as an int we probably
44  // still want R as double)
45  for (idx_type r=0; r<a.dim[0]; ++r) {
46  T outvalue = ring[r & (ringsz-1)];
47  if (!std::isnan((*a.v[c])[r])) {
48  mean += (*a.v[c])[r];
49  ring[r & (ringsz - 1)] = (*a.v[c])[r];
50  ++nbv;
51  }
52  if (r >= window && !std::isnan(outvalue)) {
53  mean -= outvalue;
54  --nbv;
55  }
56  if (nbv >= nbvalid) {
57  setv(a.getcol(c), r, mean / window);
58  } else {
59  setv(a.getcol(c), r, Global::ZNAN);
60  }
61  }
62  }
63  return a;
64  }
65 
66 
67 
74  template<typename T>
75  Array<T>& rollmin_inplace(Array<T>& a, idx_type window, idx_type nbvalid) {
76  auto minima = new std::pair<T, idx_type>[a.dim[0]]; // the minimum and its position
77 
78  for (idx_type c=0; c<a.v.size(); c++) {
79  idx_type nbv = 0, front = 0, back = 0;
80  for (idx_type r=0; r<a.dim[0]; ++r) {
81  if (!std::isnan((*a.v[c])[r])) {
82  // pop at the back the values that are greater than what we
83  // are adding because they cannot be minima anymore:
84  while (back > front && minima[back-1].first >= (*a.v[c])[r]) --back;
85  minima[back].first = (*a.v[c])[r];
86  minima[back].second = r;
87  ++back;
88  ++nbv;
89  }
90  if (r >= window && !std::isnan((*a.v[c])[r-window])) {
91  --nbv;
92  }
93  if (nbv >= nbvalid) {
94  if (r >= window) {
95  // pop at the front until we find a minimum that is in our
96  // window:
97  while (minima[front].second <= r-window) ++front;
98  }
99  setv(a.getcol(c), r, minima[front].first); // that's our minimum!
100  } else {
101  setv(a.getcol(c), r, Global::ZNAN);
102  }
103  }
104  }
105  delete[] minima; // this is OK as the above can't throw
106  return a;
107  }
108 
109 
110 
117  template<typename T>
118  Array<T>& rollmax_inplace(Array<T>& a, idx_type window, idx_type nbvalid) {
119  auto maxima = new std::pair<T, idx_type>[a.dim[0]]; // the minimum and its position
120 
121  for (idx_type c=0; c<a.v.size(); c++) {
122  idx_type nbv = 0, front = 0, back = 0;
123  for (idx_type r=0; r<a.dim[0]; ++r) {
124  if (!std::isnan((*a.v[c])[r])) {
125  // pop at the back the values that are smaller than what we
126  // are adding because they cannot be maxima anymore:
127  while (back > front && maxima[back-1].first <= (*a.v[c])[r]) --back;
128  maxima[back].first = (*a.v[c])[r];
129  maxima[back].second = r;
130  ++back;
131  ++nbv;
132  }
133  if (r >= window && !std::isnan((*a.v[c])[r-window])) {
134  --nbv;
135  }
136  if (nbv >= nbvalid) {
137  if (r >= window) {
138  // pop at the front until we find a maximum that is in our
139  // window:
140  while (maxima[front].second <= r-window) ++front;
141  }
142  setv(a.getcol(c), r, maxima[front].first); // that's our minimum!
143  } else {
144  setv(a.getcol(c), r, Global::ZNAN);
145  }
146  }
147  }
148  delete[] maxima; // this is OK as the above can't throw
149  return a;
150  }
151 
156  //
157  // 1/(n-1) S (x - mx)^2 = 1/(n-1) S (x2 - 2mxx + mx^2) = 1/(n-1) (S
158  // x2 - 2 mx S x + n mx^2) = 1/(n-1) (S x2 - 2/n (S x)^2 + 1/n (S
159  // x)^2) = 1/(n-1) (S x2 - 1/n (S x)^2)
160  template<typename T>
161  Array<T>& rollvar_inplace(Array<T>& a, idx_type window, idx_type nbvalid) {
162  size_t ringsz = 1;
163  while (ringsz < window) ringsz <<= 1;
164  vector<T> ring(ringsz);
165  vector<T> ring2(ringsz);
166 
167 
168  for (idx_type c=0; c<a.v.size(); c++) {
169  idx_type nbv = 0;
170  T sum = 0, sum2 = 0;
171 
172  for (idx_type r=0; r<a.dim[0]; ++r) {
173  T outvalue = ring[r & (ringsz-1)];
174  T outvalue2 = ring2[r & (ringsz-1)];
175  if (!std::isnan((*a.v[c])[r])) {
176  sum += (*a.v[c])[r];
177  ring[r & (ringsz - 1)] = (*a.v[c])[r];
178  sum2 += (*a.v[c])[r] * (*a.v[c])[r];
179  ring2[r & (ringsz - 1)] = (*a.v[c])[r] * (*a.v[c])[r];
180  ++nbv;
181  }
182  if (r >= window && !std::isnan(outvalue)) {
183  sum -= outvalue;
184  sum2 -= outvalue2;
185  --nbv;
186  }
187  if (nbv >= nbvalid) {
188  setv(a.getcol(c), r, (sum2 - sum*sum / nbv) / (nbv - 1));
189  if ((*a.v[c])[r] < 0) { // possible because of small rounding errors
190  setv(a.getcol(c), r, 0.0);
191  }
192  } else {
193  setv(a.getcol(c), r, Global::ZNAN);
194  }
195  }
196  }
197  return a;
198  }
199 
200 
201  template<typename T>
202  Array<T>& locf_inplace(Array<T>& a, ssize_t n) {
203  for (idx_type c=0; c<a.v.size(); c++) {
204  auto last_idx = -1L;
205  for (idx_type r=1; r<a.dim[0]; ++r) {
206  if (std::isnan((*a.v[c])[r])) {
207  if (last_idx < 0) {
208  last_idx = r - 1;
209  }
210  setv(a.getcol(c), r, (r - last_idx <= static_cast<idx_type>(n)) ? (*a.v[c])[r-1] : Global::ZNAN);
211  } else {
212  last_idx = -1;
213  }
214  }
215  }
216  return a;
217  }
218 
219 
220  template<typename T>
221  Array<T>& move_inplace(Array<T>& a, ssize_t n) {
222  if (n > 0 ) {
223  for (idx_type c=0; c<a.v.size(); c++) {
224  for (idx_type r=a.dim[0]-1; r >= static_cast<idx_type>(n); --r) {
225  setv(a.getcol(c), r, (*a.v[c])[r - n]);
226  }
227  for (idx_type r=0; r<static_cast<idx_type>(n); ++r) {
228  setv(a.getcol(c), r, Global::ZNAN);
229  }
230  }
231  }
232  else {
233  for (idx_type c=0; c<a.v.size(); c++) {
234  for (idx_type r=0; r<a.dim[0] + n; ++r) {
235  setv(a.getcol(c), r, (*a.v[c])[r - n]);
236  }
237  for (idx_type r=a.dim[0]+n; r<a.dim[0]; ++r) {
238  setv(a.getcol(c), r, Global::ZNAN);
239  }
240  }
241  }
242  return a;
243  }
244 
245 
246  template<typename T>
247  Array<T>& rotate_inplace(Array<T>& a, ssize_t n) {
248  if (n > 0 ) {
249  vector<T> v(n);
250  for (idx_type c=0; c<a.v.size(); c++) {
251  for (idx_type r=a.dim[0]-n; r<a.dim[0]; ++r) {
252  v[r-(a.dim[0]-n)] = (*a.v[c])[r];
253  }
254  for (idx_type r=a.dim[0]-1; r >= static_cast<idx_type>(n); --r) {
255  setv(a.getcol(c), r, (*a.v[c])[r - n]);
256  }
257  for (idx_type r=0; r<static_cast<idx_type>(n); ++r) {
258  setv(a.getcol(c), r, v[r]);
259  }
260  }
261  }
262  else {
263  vector<T> v(-n);
264  for (idx_type c=0; c<a.v.size(); c++) {
265  for (idx_type r=0; r<static_cast<idx_type>(-n); ++r) {
266  v[r] = (*a.v[c])[r];
267  }
268  for (idx_type r=0; r<a.dim[0] + n; ++r) {
269  setv(a.getcol(c), r, (*a.v[c])[r - n]);
270  }
271  for (idx_type r=a.dim[0]+n; r<a.dim[0]; ++r) {
272  setv(a.getcol(c), r, v[r - (a.dim[0]+n)]);
273  }
274  }
275  }
276  return a;
277  }
278 
279 
280  template<typename T>
281  Array<T>& diff_inplace(Array<T>& a, ssize_t n) {
282  if (n > 0 ) {
283  for (idx_type c=0; c<a.v.size(); c++) {
284  for (idx_type r=a.dim[0]-1; r >= static_cast<idx_type>(n); --r) {
285  setv(a.getcol(c), r, (*a.v[c])[r] - (*a.v[c])[r - n]);
286  }
287  for (idx_type r=0; r<static_cast<idx_type>(n); ++r) {
288  setv(a.getcol(c), r, Global::ZNAN);
289  }
290  }
291  }
292  else {
293  for (idx_type c=0; c<a.v.size(); c++) {
294  for (idx_type r=0; r<a.dim[0] + n; ++r) {
295  setv(a.getcol(c), r, (*a.v[c])[r] - (*a.v[c])[r - n]);
296  }
297  for (idx_type r=a.dim[0]+n; r<a.dim[0]; ++r) {
298  setv(a.getcol(c), r, Global::ZNAN);
299  }
300  }
301  }
302  return a;
303  }
304 
305 
306 
307 
314  //
315  // S (x-xm)(y-ym) = S (xy - y xm - x ym + xm ym) =
316  // S xy - xm S y - ym S x + n xm ym =
317  // S xy - (S x S y) / n - (S x S y) / n + n S x S y / n^2 =
318  // S xy - S x S y / n
319  //
320  // so we can use the unbiased estimator:
321  // cov(x, y) = 1/(n-1) (S xy - S x S y / n)
322  template<typename T>
323  Array<T> rollcov(const Array<T>& x, const Array<T>& y, idx_type window, idx_type nbvalid) {
324  size_t ringsz = 1;
325  while (ringsz < window) ringsz <<= 1;
326  vector<T> ring_x(ringsz);
327  vector<T> ring_y(ringsz);
328  vector<T> ring_xy(ringsz);
329 
330  if (!(x.getdim().size() && y.getdim().size() && (x.getdim(0) == y.getdim(0)))) {
331  throw std::range_error("invalid dimensions");
332  }
333 
334  Array<T> z(rsv, Vector<idx_type>{0, x.ncols(), y.ncols()});
335 
336  // obviously we could improve the efficiency and calculate the cov
337  // only for the triangle of columns...
338  const idx_type nrows = y.getdim(0);
339  idx_type zcol = 0;
340  for (idx_type colx=0; colx < x.ncols(); ++colx) {
341  for (idx_type coly=0; coly < y.ncols(); ++coly) {
342  idx_type nbv = 0;
343  T sum_x = 0, sum_y = 0, sum_xy = 0;
344 
345  for (idx_type r=0; r<nrows; ++r) { // rows
346  T outvalue_x = ring_x[r & (ringsz-1)];
347  T outvalue_y = ring_y[r & (ringsz-1)];
348  T outvalue_xy = ring_xy[r & (ringsz-1)];
349  if (!std::isnan(x.getcol(colx)[r]) && !std::isnan(y.getcol(coly)[r])) {
350  sum_x += x.getcol(colx)[r];
351  ring_x[r & (ringsz - 1)] = x.getcol(colx)[r];
352  sum_y += y.getcol(coly)[r];
353  ring_y[r & (ringsz - 1)] = y.getcol(coly)[r];
354  sum_xy += x.getcol(colx)[r] * y.getcol(coly)[r];
355  ring_xy[r & (ringsz - 1)] = x.getcol(colx)[r] * y.getcol(coly)[r];
356  ++nbv;
357  }
358  if (r >= window && !std::isnan(outvalue_x) && !std::isnan(outvalue_y)) {
359  sum_x -= outvalue_x;
360  sum_y -= outvalue_y;
361  sum_xy -= outvalue_xy;
362  --nbv;
363  }
364  if (nbv >= nbvalid) {
365  z.getcol(zcol).push_back((sum_xy - sum_x*sum_y / nbv) / (nbv - 1));
366  } else {
367  z.getcol(zcol).push_back(Global::ZNAN);
368  }
369  }
370  ++zcol;
371  }
372 
373  // build names
374  z.names[0]->resize(nrows);
375  setv(z.dim, 0, nrows);
376  }
377  return z;
378  }
379 
380 
381  template<typename T, typename F>
382  Array<T>& cumul_inplace(Array<T>& a, bool rev) {
383  if (!rev) {
384  for (idx_type c=0; c < a.ncols(); c++) {
385  for (idx_type r=1; r < a.getcol(c).size(); ++r) {
386  setv(a.getcol(c), r, F()(a.getcol(c)[r], a.getcol(c)[r-1]));
387  }
388  }
389  }
390  else {
391  for (idx_type c=0; c < a.ncols(); c++) {
392  for (idx_type r=a.getcol(c).size()-1; r >= 1 ; --r) {
393  setv(a.getcol(c), r-1, F()(a.getcol(c)[r-1], a.getcol(c)[r]));
394  }
395  }
396  }
397  return a;
398  }
399 
400  template<typename T>
401  Array<T>& rev_inplace(Array<T>& a) {
402  for (idx_type c=0; c < a.ncols(); c++) {
403  auto iter_start = a.getcol(c).begin();
404  auto iter_end = a.getcol(c).end();
405  for (arr::idx_type j=0; j<a.getcol(c).size() / 2; j++) {
406  std::iter_swap(iter_start++, --iter_end);
407  }
408  }
409  return a;
410  }
411 
412 } // namespace arr
413 
414 
415 #endif
arr::Vector< idx_type >
arr::rollmean_inplace
Array< T > & rollmean_inplace(Array< T > &a, idx_type window, idx_type nbvalid)
Definition: array_ops.hpp:34
arr::rollvar_inplace
Array< T > & rollvar_inplace(Array< T > &a, idx_type window, idx_type nbvalid)
Definition: array_ops.hpp:161
arr
Contains the classes and functions that implement a multidimentional array type.
Definition: allocator.hpp:29
arr::rollcov
Array< T > rollcov(const Array< T > &x, const Array< T > &y, idx_type window, idx_type nbvalid)
Definition: array_ops.hpp:323
arr::rollmax_inplace
Array< T > & rollmax_inplace(Array< T > &a, idx_type window, idx_type nbvalid)
Definition: array_ops.hpp:118
arr::Array
Definition: array.hpp:109
arr::rollmin_inplace
Array< T > & rollmin_inplace(Array< T > &a, idx_type window, idx_type nbvalid)
Definition: array_ops.hpp:75