utils.hpp
1 // Copyright © 2020 Thomas Nagler
2 //
3 // This file is part of the wdm library and licensed under the terms of
4 // the MIT license. For a copy, see the LICENSE file in the root directory
5 // or https://github.com/tnagler/wdm/blob/master/LICENSE.
6 
7 #pragma once
8 
9 #include <algorithm>
10 #include <cmath>
11 #include <numeric>
12 #include <stdexcept>
13 #include <string>
14 #include <vector>
15 
16 namespace wdm {
17 
18 namespace utils {
19 
20 inline double
21 normalCDF(double x)
22 {
23  return std::erfc(-x / std::sqrt(2)) / 2;
24 }
25 
26 inline double
27 linear_interp(const double& x,
28  const std::vector<double>& grid,
29  const std::vector<double>& values)
30 {
31  // find upper end point of interval
32  size_t i = 1;
33  while (x > grid[i])
34  i++;
35 
36  // linear interpolation
37  double w = (x - grid[i - 1]) / (grid[i] - grid[i - 1]);
38  return w * values[i - 1] + (1 - w) * values[i];
39 }
40 
41 inline void
42 check_sizes(const std::vector<double>& x,
43  const std::vector<double>& y,
44  const std::vector<double>& weights)
45 {
46  if (y.size() != x.size())
47  throw std::runtime_error("x and y must have the same size.");
48  if ((weights.size() > 0) && (weights.size() != y.size()))
49  throw std::runtime_error("x, y, and weights must have the same size.");
50 }
51 
56 inline std::vector<double>
57 pow(const std::vector<double>& x, size_t n)
58 {
59  std::vector<double> res(x.size(), 1.0);
60  if (n > 0) {
61  for (size_t i = 0; i < x.size(); i++) {
62  for (size_t j = 0; j < n; j++) {
63  res[i] *= x[i];
64  }
65  }
66  }
67 
68  return res;
69 }
70 
73 inline double
74 sum(const std::vector<double>& x)
75 {
76  double res = 0.0;
77  for (size_t i = 0; i < x.size(); i++)
78  res += x[i];
79  return res;
80 }
81 
86 inline double
87 perm_sum(const std::vector<double>& x, size_t k)
88 {
89  if (k == 0)
90  return 1.0;
91  double s = 0;
92  for (size_t i = 1; i <= k; i++)
93  s += std::pow(-1.0, i - 1) * perm_sum(x, k - i) * sum(pow(x, i));
94  return s / k;
95 }
96 
100 inline double
101 effective_sample_size(size_t n, const std::vector<double>& weights)
102 {
103  double n_eff;
104  if (weights.size() == 0) {
105  n_eff = static_cast<double>(n);
106  } else {
107  n_eff = std::pow(sum(weights), 2);
108  n_eff /= sum(pow(weights, 2));
109  }
110 
111  return n_eff;
112 }
113 
117 inline std::vector<size_t>
118 invert_permutation(const std::vector<size_t>& perm)
119 {
120  std::vector<size_t> inv_perm(perm.size());
121  for (size_t i = 0; i < perm.size(); i++)
122  inv_perm[perm[i]] = i;
123  return inv_perm;
124 }
125 
129 inline std::vector<size_t>
130 get_order(const std::vector<double>& x, bool ascending = true)
131 {
132  size_t n = x.size();
133  std::vector<size_t> perm(n);
134  for (size_t i = 0; i < n; i++)
135  perm[i] = i;
136  auto sorter = [&](size_t i, size_t j) {
137  if (ascending)
138  return (x[i] < x[j]);
139  else
140  return (x[i] > x[j]);
141  };
142  std::sort(perm.begin(), perm.end(), sorter);
143 
144  return perm;
145 }
146 
149 inline void
150 sort_all(std::vector<double>& x,
151  std::vector<double>& y,
152  std::vector<double>& weights)
153 {
154  size_t n = x.size();
155  std::vector<size_t> order(n);
156  for (size_t i = 0; i < n; i++)
157  order[i] = i;
158  auto sorter_with_tie_break = [&](size_t i, size_t j) {
159  return (x[i] < x[j]) || ((x[i] == x[j]) && (y[i] < y[j]));
160  };
161  std::sort(order.begin(), order.end(), sorter_with_tie_break);
162 
163  std::vector<double> xx(n), yy(n);
164  for (size_t i = 0; i < n; i++) {
165  xx[i] = x[order[i]];
166  yy[i] = y[order[i]];
167  }
168 
169  // sort weights accordingly
170  std::vector<double> w = weights;
171  if (weights.size() > 0) {
172  for (size_t i = 0; i < n; i++) {
173  w[i] = weights[order[i]];
174  }
175  }
176 
177  x = xx;
178  y = yy;
179  weights = w;
180 }
181 
187 inline double
188 count_ties_v(const std::vector<double>& x, const std::vector<double>& weights)
189 {
190  bool weighted = (weights.size() > 0);
191  double count = 0.0, w1 = 0.0, w2 = 0.0;
192  size_t reps = 1;
193  for (size_t i = 1; i < x.size(); i++) {
194  if ((x[i] == x[i - 1])) {
195  if (weighted) {
196  if (reps == 1) {
197  w1 = weights[i - 1];
198  w2 = w1 * w1;
199  }
200  w1 += weights[i];
201  w2 += std::pow(weights[i], 2);
202  }
203  reps++;
204  } else if (reps > 1) {
205  if (weighted) {
206  count += (w1 * w1 - w2) * (2 * w1 + 5);
207  } else {
208  count += reps * (reps - 1) * (2 * reps + 5);
209  }
210  reps = 1;
211  }
212  }
213 
214  if (reps > 1) {
215  if (weighted) {
216  count += (w1 * w1 - w2) * (2 * w1 + 5);
217  } else {
218  count += reps * (reps - 1) * (2 * reps + 5);
219  }
220  }
221 
222  return count;
223 }
224 
229 inline double
230 count_tied_pairs(const std::vector<double>& x,
231  const std::vector<double>& weights)
232 {
233  bool weighted = (weights.size() > 0);
234  double count = 0.0, w1 = 0.0, w2 = 0.0;
235  size_t reps = 1;
236  for (size_t i = 1; i < x.size(); i++) {
237  if ((x[i] == x[i - 1])) {
238  if (weighted) {
239  if (reps == 1) {
240  w1 = weights[i - 1];
241  w2 = w1 * w1;
242  }
243  w1 += weights[i];
244  w2 += std::pow(weights[i], 2);
245  }
246  reps++;
247  } else if (reps > 1) {
248  if (weighted) {
249  count += (w1 * w1 - w2) / 2.0;
250  } else {
251  count += reps * (reps - 1) / 2.0;
252  }
253  reps = 1;
254  }
255  }
256 
257  if (reps > 1) {
258  if (weighted) {
259  count += (w1 * w1 - w2) / 2.0;
260  } else {
261  count += reps * (reps - 1) / 2.0;
262  }
263  }
264 
265  return count;
266 }
267 
273 inline double
274 count_tied_triplets(const std::vector<double>& x,
275  const std::vector<double>& weights)
276 {
277  bool weighted = (weights.size() > 0);
278  double count = 0.0, w1 = 0.0, w2 = 0.0, w3 = 0.0;
279  size_t reps = 2;
280  for (size_t i = 2; i < x.size(); i++) {
281  if ((x[i] == x[i - 1]) && (x[i] == x[i - 2])) {
282  if (weighted) {
283  if (reps == 1) {
284  w1 = weights[i - 1];
285  w2 = std::pow(weights[i - 1], 2);
286  w3 = std::pow(weights[i - 1], 3);
287  }
288  w1 += weights[i];
289  w2 += std::pow(weights[i], 2);
290  w3 += std::pow(weights[i], 3);
291  }
292  reps++;
293  } else if (reps > 2) {
294  if (weighted) {
295  count += (std::pow(w1, 3) - 3 * w2 * w1 + 2 * w3) / 6.0;
296  } else {
297  count += reps * (reps - 1) * (reps - 2) / 6.0;
298  }
299  reps = 1;
300  }
301  }
302 
303  if (reps > 2) {
304  if (weighted) {
305  count += (std::pow(w1, 3) - 3 * w2 * w1 + 2 * w3) / 6.0;
306  } else {
307  count += reps * (reps - 1) * (reps - 2) / 6.0;
308  }
309  }
310 
311  return count;
312 }
313 
319 inline double
320 count_joint_ties(const std::vector<double>& x,
321  const std::vector<double>& y,
322  const std::vector<double>& weights)
323 {
324  bool weighted = (weights.size() > 0);
325  double count = 0.0, w1 = 0.0, w2 = 0.0;
326  size_t reps = 1;
327  for (size_t i = 1; i < x.size(); i++) {
328  if ((x[i] == x[i - 1]) && (y[i] == y[i - 1])) {
329  if (weighted) {
330  if (reps == 1) {
331  w1 = weights[i - 1];
332  w2 = weights[i - 1] * weights[i - 1];
333  }
334  w1 += weights[i];
335  w2 += weights[i] * weights[i];
336  }
337  reps++;
338  } else if (reps > 1) {
339  if (weighted) {
340  count += (w1 * w1 - w2) / 2.0;
341  } else {
342  count += reps * (reps - 1) / 2.0;
343  }
344  reps = 1;
345  }
346  }
347 
348  if (reps > 1) {
349  if (weighted) {
350  count += (w1 * w1 - w2) / 2.0;
351  } else {
352  count += reps * (reps - 1) / 2.0;
353  }
354  }
355 
356  return count;
357 }
358 
367 inline void
368 merge(std::vector<double>& vec,
369  const std::vector<double>& vec1,
370  const std::vector<double>& vec2,
371  std::vector<double>& weights,
372  const std::vector<double>& weights1,
373  const std::vector<double>& weights2,
374  double& count)
375 {
376  double w_acc = 0.0, w1_sum = 0.0;
377  bool weighted = (weights.size() > 0);
378  if (weighted) {
379  for (size_t i = 0; i < weights1.size(); i++)
380  w1_sum += weights1[i];
381  }
382  size_t i, j, k;
383  for (i = 0, j = 0, k = 0; i < vec1.size() && j < vec2.size(); k++) {
384  if (vec1[i] <= vec2[j]) {
385  vec[k] = vec1[i];
386  if (weighted) {
387  weights[k] = weights1[i];
388  w_acc += weights1[i];
389  }
390  i++;
391  } else {
392  vec[k] = vec2[j];
393  if (weighted) {
394  weights[k] = weights2[j];
395  count += weights2[j] * (w1_sum - w_acc);
396  } else {
397  count += vec1.size() - i;
398  }
399  j++;
400  }
401  }
402 
403  while (i < vec1.size()) {
404  vec[k] = vec1[i];
405  if (weighted)
406  weights[k] = weights1[i];
407  k++;
408  i++;
409  }
410 
411  while (j < vec2.size()) {
412  vec[k] = vec2[j];
413  if (weighted)
414  weights[k] = weights2[j];
415  k++;
416  j++;
417  }
418 }
419 
425 inline void
426 merge_sort(std::vector<double>& vec,
427  std::vector<double>& weights,
428  double& count)
429 {
430  if (vec.size() > 1) {
431  size_t n = vec.size();
432  std::vector<double> vec1(vec.begin(), vec.begin() + n / 2);
433  std::vector<double> vec2(vec.begin() + n / 2, vec.end());
434 
435  n = weights.size();
436  std::vector<double> weights1(weights.begin(), weights.begin() + n / 2);
437  std::vector<double> weights2(weights.begin() + n / 2, weights.end());
438 
439  merge_sort(vec1, weights1, count);
440  merge_sort(vec2, weights2, count);
441  merge(vec, vec1, vec2, weights, weights1, weights2, count);
442  }
443 }
444 
456 inline void
457 merge_count_per_element(std::vector<double>& vec,
458  const std::vector<double>& vec1,
459  const std::vector<double>& vec2,
460  std::vector<double>& weights,
461  const std::vector<double>& weights1,
462  const std::vector<double>& weights2,
463  std::vector<double>& counts,
464  const std::vector<double>& counts1,
465  const std::vector<double>& counts2)
466 {
467  double w_acc = 0.0;
468  bool weighted = (weights.size() > 0);
469  double w1_sum = 0.0;
470  if (weighted) {
471  for (size_t i = 0; i < weights1.size(); i++)
472  w1_sum += weights1[i];
473  }
474  size_t i, j, k;
475  for (i = 0, j = 0, k = 0; i < vec1.size() && j < vec2.size(); k++) {
476  if (vec1[i] > vec2[j]) {
477  vec[k] = vec1[i];
478  counts[k] = counts1[i];
479  if (weighted) {
480  weights[k] = weights1[i];
481  w_acc += weights1[i];
482  }
483  i++;
484  } else {
485  vec[k] = vec2[j];
486  if (weighted) {
487  counts[k] = counts2[j] + w1_sum - w_acc;
488  weights[k] = weights2[j];
489  } else {
490  counts[k] = counts2[j] + vec1.size() - i;
491  }
492  j++;
493  }
494  }
495 
496  while (i < vec1.size()) {
497  vec[k] = vec1[i];
498  if (weighted)
499  weights[k] = weights1[i];
500  counts[k] = counts1[i];
501  k++;
502  i++;
503  }
504 
505  while (j < vec2.size()) {
506  vec[k] = vec2[j];
507  if (weighted)
508  weights[k] = weights2[j];
509  counts[k] = counts2[j];
510  k++;
511  j++;
512  }
513 }
514 
522 inline void
523 merge_sort_count_per_element(std::vector<double>& vec,
524  std::vector<double>& weights,
525  std::vector<double>& counts)
526 {
527  if (vec.size() > 1) {
528  size_t n = vec.size();
529  std::vector<double> vec1(vec.begin(), vec.begin() + n / 2);
530  std::vector<double> vec2(vec.begin() + n / 2, vec.end());
531 
532  n = weights.size();
533  std::vector<double> weights1(weights.begin(), weights.begin() + n / 2);
534  std::vector<double> weights2(weights.begin() + n / 2, weights.end());
535 
536  n = counts.size();
537  std::vector<double> counts1(counts.begin(), counts.begin() + n / 2);
538  std::vector<double> counts2(counts.begin() + n / 2, counts.end());
539 
540  merge_sort_count_per_element(vec1, weights1, counts1);
541  merge_sort_count_per_element(vec2, weights2, counts2);
542  merge_count_per_element(
543  vec, vec1, vec2, weights, weights1, weights2, counts, counts1, counts2);
544  }
545 }
546 
547 }
548 
549 } // end wdm
Weighted dependence measures.
Definition: wdm.hpp:19