wdm.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 "wdm/bbeta.hpp"
10 #include "wdm/cxi.hpp"
11 #include "wdm/hoeffd.hpp"
12 #include "wdm/ktau.hpp"
13 #include "wdm/methods.hpp"
14 #include "wdm/nan_handling.hpp"
15 #include "wdm/prho.hpp"
16 #include "wdm/srho.hpp"
17 
19 namespace wdm {
20 
48 inline double
49 wdm(std::vector<double> x,
50  std::vector<double> y,
51  std::string method,
52  std::vector<double> weights = std::vector<double>(),
53  bool remove_missing = true,
54  std::vector<int> seeds = std::vector<int>())
55 {
56  utils::check_sizes(x, y, weights);
57  // na handling
58  if (utils::preproc(x, y, weights, method, remove_missing) == "return_nan")
59  return std::numeric_limits<double>::quiet_NaN();
60 
61  if (methods::is_hoeffding(method))
62  return impl::hoeffd(x, y, weights);
63  if (methods::is_kendall(method))
64  return impl::ktau(x, y, weights);
65  if (methods::is_pearson(method))
66  return impl::prho(x, y, weights);
67  if (methods::is_spearman(method))
68  return impl::srho(x, y, weights);
69  if (methods::is_blomqvist(method))
70  return impl::bbeta(x, y, weights);
71  if (methods::is_chatterjee(method)) {
72  auto xi_and_std = impl::cxi(x, y, weights, false, "max", seeds);
73  return std::get<0>(xi_and_std);
74  }
75  throw std::runtime_error("method not implemented.");
76 }
77 
106 {
107 public:
108  Indep_test() = delete;
109 
131  Indep_test(std::vector<double> x,
132  std::vector<double> y,
133  std::string method,
134  std::vector<double> weights = std::vector<double>(),
135  bool remove_missing = true,
136  std::string alternative = "two-sided",
137  std::vector<int> seeds = std::vector<int>(),
138  bool y_continuous = true)
139  : method_(method)
140  , alternative_(alternative)
141  {
142  utils::check_sizes(x, y, weights);
143  if (utils::preproc(x, y, weights, method, remove_missing) == "return_nan") {
144  n_eff_ = utils::effective_sample_size(x.size(), weights);
145  estimate_ = std::numeric_limits<double>::quiet_NaN();
146  statistic_ = std::numeric_limits<double>::quiet_NaN();
147  p_value_ = std::numeric_limits<double>::quiet_NaN();
148  } else {
149  n_eff_ = utils::effective_sample_size(x.size(), weights);
150  if (methods::is_chatterjee(method)) {
151  auto stats = impl::cxi(x, y, weights, true, "max", seeds, y_continuous);
152  estimate_ = std::get<0>(stats);
153  statistic_ =
154  (std::get<3>(stats) - std::get<2>(stats)) / std::get<1>(stats);
155  } else {
156  estimate_ = wdm(x, y, method, weights, false);
157  statistic_ =
158  compute_test_stat(estimate_, method, n_eff_, x, y, weights);
159  }
160  p_value_ = compute_p_value(statistic_, method, alternative, n_eff_);
161  }
162  }
163 
165  std::string method() const { return method_; }
166 
168  std::string alternative() const { return alternative_; }
169 
171  double n_eff() const { return n_eff_; }
172 
174  double estimate() const { return estimate_; }
175 
177  double statistic() const { return statistic_; }
178 
180  double p_value() const { return p_value_; }
181 
182 private:
183  inline double compute_test_stat(double estimate,
184  std::string method,
185  double n_eff,
186  const std::vector<double>& x,
187  const std::vector<double>& y,
188  const std::vector<double>& weights)
189  {
190  // prevent overflow in atanh
191  if (estimate >= 1.0)
192  estimate = 1 - 1e-12;
193  if (estimate <= -1.0)
194  estimate = -1 + 1e-12;
195 
196  double stat;
197  if (methods::is_hoeffding(method)) {
198  stat = estimate / 30.0 + 1.0 / (36.0 * n_eff);
199  } else if (methods::is_kendall(method)) {
200  stat = estimate * impl::ktau_stat_adjust(x, y, weights);
201  } else if (methods::is_pearson(method)) {
202  stat = std::atanh(estimate) * std::sqrt(n_eff - 3);
203  } else if (methods::is_spearman(method)) {
204  stat = std::atanh(estimate) * std::sqrt((n_eff - 3) / 1.06);
205  } else if (methods::is_blomqvist(method)) {
206  stat = std::atanh(estimate) * std::sqrt(n_eff);
207  } else {
208  throw std::runtime_error("method not implemented.");
209  }
210 
211  return stat;
212  }
213 
214  inline double compute_p_value(double statistic,
215  std::string method,
216  std::string alternative,
217  double n_eff = 0.0)
218  {
219  double p_value;
220  if (methods::is_hoeffding(method)) {
221  if (n_eff == 0.0)
222  throw std::runtime_error("must provide n_eff for method 'hoeffd'.");
223  if (alternative != "two-sided")
224  throw std::runtime_error(
225  "only two-sided test available for Hoeffding's D.");
226  p_value = impl::phoeffb(statistic, n_eff);
227  } else {
228  if (alternative == "two-sided") {
229  p_value = 2 * utils::normalCDF(-std::abs(statistic));
230  } else if (alternative == "less") {
231  p_value = utils::normalCDF(statistic);
232  } else if (alternative == "greater") {
233  p_value = 1 - utils::normalCDF(statistic);
234  } else {
235  throw std::runtime_error("alternative not implemented.");
236  }
237  }
238 
239  return p_value;
240  }
241 
242  std::string method_;
243  std::string alternative_;
244  double n_eff_;
245  double estimate_;
246  double statistic_;
247  double p_value_;
248 };
249 
250 }
Definition: wdm.hpp:106
double p_value() const
Returns the asymptotic p-value.
Definition: wdm.hpp:180
Indep_test(std::vector< double > x, std::vector< double > y, std::string method, std::vector< double > weights=std::vector< double >(), bool remove_missing=true, std::string alternative="two-sided", std::vector< int > seeds=std::vector< int >(), bool y_continuous=true)
Definition: wdm.hpp:131
std::string method() const
Returns the requested method name.
Definition: wdm.hpp:165
double estimate() const
Returns the estimated dependence measure.
Definition: wdm.hpp:174
double statistic() const
Returns the method-specific transformed test statistic.
Definition: wdm.hpp:177
double n_eff() const
Returns Kish's effective sample size after missing-value removal.
Definition: wdm.hpp:171
std::string alternative() const
Returns the requested alternative hypothesis.
Definition: wdm.hpp:168
Weighted dependence measures.
Definition: wdm.hpp:19
double wdm(std::vector< double > x, std::vector< double > y, std::string method, std::vector< double > weights=std::vector< double >(), bool remove_missing=true, std::vector< int > seeds=std::vector< int >())
Definition: wdm.hpp:49