#region License Information /* HeuristicLab * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.Linq; using HeuristicLab.Common; namespace HeuristicLab.Problems.DataAnalysis { public class HoeffdingsDependenceCalculator { public static double Calculate(IEnumerable originalValues, IEnumerable estimatedValues, out OnlineCalculatorError errorState) { double d = HoeffD(originalValues, estimatedValues, out errorState); if (errorState != OnlineCalculatorError.None) return double.NaN; return d; } /// /// computes Hoeffding's dependence coefficient. /// Source: hoeffd.r from R package hmisc http://cran.r-project.org/web/packages/Hmisc/index.html /// private static double HoeffD(IEnumerable xs, IEnumerable ys, out OnlineCalculatorError errorState) { double[] rx = TiedRank(xs); double[] ry = TiedRank(ys); if (rx.Length != ry.Length) throw new ArgumentException("The number of elements in xs and ys does not match"); double[] rxy = TiedRank(xs, ys); int n = rx.Length; double q = 0, r = 0, s = 0; double scaling = 1.0 / (n * (n - 1)); for (int i = 0; i < n; i++) { q += (rx[i] - 1) * (rx[i] - 2) * (ry[i] - 1) * (ry[i] - 2) * scaling; r += (rx[i] - 2) * (ry[i] - 2) * rxy[i] * scaling; s += rxy[i] * (rxy[i] - 1) * scaling; } errorState = OnlineCalculatorError.None; // return 30.0 * (q - 2 * (n - 2) * r + (n - 2) * (n - 3) * s) / n / (n - 1) / (n - 2) / (n - 3) / (n - 4); double t0 = q / (n - 2) / (n - 3) / (n - 4); double t1 = 2 * r / (n - 3) / (n - 4); double t2 = s / (n - 4); return 30.0 * (t0 - t1 + t2); } private static double[] TiedRank(IEnumerable xs) { var xsArr = xs.ToArray(); var idx = Enumerable.Range(1, xsArr.Length).ToArray(); Array.Sort(xsArr, idx); CRank(xsArr); Array.Sort(idx, xsArr); return xsArr; } /// /// Calculates the joint rank with midranks for ties. Source: hoeffd.r from R package hmisc http://cran.r-project.org/web/packages/Hmisc/index.html /// /// /// /// private static double[] TiedRank(IEnumerable xs, IEnumerable ys) { var xsArr = xs.ToArray(); var ysArr = ys.ToArray(); var r = new double[xsArr.Length]; int n = r.Length; for (int i = 0; i < n; i++) { var xi = xsArr[i]; var yi = ysArr[i]; double ri = 0.0; for (int j = 0; j < n; j++) { if (i != j) { double cx; if (xsArr[j] < xi) cx = 1.0; else if (xsArr[j] > xi) cx = 0.0; else cx = 0.5; // eq double cy; if (ysArr[j] < yi) cy = 1.0; else if (ysArr[j] > yi) cy = 0.0; else cy = 0.5; // eq ri = ri + cx * cy; } } r[i] = ri; } return r; } /// /// Calculates midranks. Source: Numerical Recipes in C. /// /// Sorted array of elements, replaces the elements by their rank, including midranking of ties /// private static void CRank(double[] w) { int i = 0; int n = w.Length; while (i < n - 1) { if (w[i + 1] > w[i]) { // w[i+1] must be larger or equal w[i] as w must be sorted // not a tie w[i] = i + 1; i++; } else { int j; for (j = i + 1; j < n && w[j] <= w[i]; j++) ; // how far does it go (<= effectively means == as w must be sorted) double rank = 0.5 * (i + j - 1); int k; for (k = i; k < j - 1; k++) w[k] = rank; // set the rank for all tied entries i = j; } } if (i == n - 1) w[n - 1] = n - 1; // if the last element was not tied, this is its rank } } }