using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace BotSharp.Algorithm.HiddenMarkovModel.MathUtils.Statistics { /// /// A version of ANCOVA with multiple dependent variables and indepedent variables /// Status: waiting to be tested /// public class MANCOVA : MANOVA { public double[] GrandMeansX; //grand means for X at each k (where k is the number of columns in X) public double[] SSTy; //sum of squares total for y public double[] SSTx; //sum of squares total for x public double[] SSBGy; //sum of squares between groups for y public double[] SSBGx; //sum of squares between groups for x public double[] SSWGy; //sum of squares within group for y public double[] SSWGx; // sum of squares within group for x public double[][] SCT; //covariance total between x and y public double[][] SCWG; //sum of coveriance within groups between x and y public double[][] rT; //correlation total between x and y public double[][] rWG; //correlation within group between x and y public Dictionary MeanWithinGroups_x; public Dictionary MeanWithinGroups_y; /// /// The values of the intercept in each regression model /// y[k] = b * x[k] + intercept[j][k], where j is the group id, and k is the Y dimension (i.e. number of columns in Y) /// public Dictionary Intercepts = new Dictionary(); /// /// The values of the slope, b[k][i], in each regression model /// y[k] = sum_i (b[k][i] * x[i]) + intercept[j][k], where j is the group id, i is the number of columns in X, k is the number of columns in Y /// public double[][] Slope; public static string ToString(double[] x) { StringBuilder sb = new StringBuilder(); sb.Append("["); for (int i = 0; i < x.Length; ++i) { if (i != 0) { sb.Append(" "); } sb.AppendFormat("{0:0.00}", x[i]); } sb.Append("]"); return sb.ToString(); } public static string ToString(double[][] x) { StringBuilder sb = new StringBuilder(); sb.Append("["); for (int i = 0; i < x.Length; ++i) { if (i != 0) { sb.Append(", "); } sb.AppendFormat("{0:0.00}", x[i]); } sb.Append("]"); return sb.ToString(); } public string Summary { get { StringBuilder sb = new StringBuilder(); sb.AppendLine("Means Within Group:"); sb.AppendLine("X\tY\tGroupId"); foreach (int groupId in MeanWithinGroups_x.Keys) { double[] mean_x = MeanWithinGroups_x[groupId]; double[] mean_y = MeanWithinGroups_y[groupId]; sb.AppendFormat("{0:0.00}\t{1:0.00}\t{2}\n", ToString(mean_x), ToString(mean_y), groupId); } sb.AppendLine(); sb.AppendLine("Y:"); sb.AppendFormat("SST(y) = {0:0.00}\n", ToString(SSTy)); sb.AppendFormat("SSwg(y) = {0:0.00}\n", ToString(SSWGy)); sb.AppendFormat("SSbg(y) = {0:0.00}\n", ToString(SSBGy)); sb.AppendLine(); sb.AppendLine("X:"); sb.AppendFormat("SST(x) = {0:0.00}\n", ToString(SSTx)); sb.AppendFormat("SSwg(x) = {0:0.00}\n", ToString(SSWGx)); sb.AppendLine(); sb.AppendLine("Convariance:"); sb.AppendFormat("SCT[y, x] = {0:0.00}\n", ToString(SCT)); sb.AppendFormat("SCwg[y, x] = {0:0.00}\n", ToString(SCWG)); sb.AppendLine(); sb.AppendLine("Correlation:"); sb.AppendFormat("r_T[y, x] = {0:0.00}\n", ToString(rT)); sb.AppendFormat("r_wg[y, x] = {0:0.00}\n", ToString(rWG)); sb.AppendLine(); sb.AppendLine("Regression Model"); sb.AppendLine("Intercept(GroupId)\tX\tY_j\tGroupId"); foreach (int groupId in Intercepts.Keys) { double[] intercepts_y = Intercepts[groupId]; int Dy = intercepts_y.Length; int Dx = Slope.Length; for (int dy = 0; dy < Dy; ++dy) { sb.AppendFormat("{0:0.00}\t\t\t{1:0.00}\t{2}\n", intercepts_y[dy], ToString(Slope[dy]), groupId); } } sb.AppendLine(); return sb.ToString(); } } public static void RunMANCOVA(double[][] X, double[][] Y, int[] grpCat, out MANCOVA output, double significance_level = 0.05) { output = new MANCOVA(); int Dx = X[0].Length; int Dy = Y[0].Length; int N = Y.Length; Dictionary>[] groupped_x_at_dim = new Dictionary>[Dx]; Dictionary>[] groupped_y_at_dim = new Dictionary>[Dy]; for (int l = 0; l < Dx; ++l) { groupped_x_at_dim[l] = new Dictionary>(); } for (int l = 0; l < Dy; ++l) { groupped_y_at_dim[l] = new Dictionary>(); } double[][] X_transpose = new double[Dx][]; for (int i = 0; i < N; ++i) { double[] X_i = X[i]; X_transpose[i] = new double[Dx]; for (int d = 0; d < Dx; ++d) { X_transpose[d][i] = X_i[d]; } } double[][] Y_transpose = new double[Dy][]; for (int i = 0; i < N; ++i) { double[] Y_i = Y[i]; Y_transpose[i] = new double[Dy]; for (int d = 0; d < Dx; ++d) { Y_transpose[d][i] = Y_i[d]; } } for (int i = 0; i < N; ++i) { int grpId = grpCat[i]; for (int d = 0; d < Dx; ++d) { List group_x = null; if (groupped_x_at_dim[d].ContainsKey(grpId)) { group_x = groupped_x_at_dim[d][grpId]; } else { group_x = new List(); groupped_x_at_dim[d][grpId] = group_x; } group_x.Add(X_transpose[d][i]); } for (int d = 0; d < Dy; ++d) { List group_y = null; if (groupped_y_at_dim[d].ContainsKey(grpId)) { group_y = groupped_y_at_dim[d][grpId]; } else { group_y = new List(); groupped_y_at_dim[d][grpId] = group_y; } group_y.Add(Y_transpose[d][i]); } } int k = groupped_x_at_dim[0].Count; output.GrandMeansX = new double[Dx]; output.GrandMeansY = new double[Dy]; output.SSTx = new double[Dx]; for (int d = 0; d < Dx; ++d) { output.SSTx[d] = GetSST(X_transpose[d], out output.GrandMeansX[d]); } output.SSTy = new double[Dy]; for (int d = 0; d < Dy; ++d) { output.SSTy[d] = GetSST(Y_transpose[d], out output.GrandMeansY[d]); } output.SSBGx = new double[Dx]; for (int d = 0; d < Dx; ++d) { output.SSBGx[d] = GetSSG(groupped_x_at_dim[d], output.GrandMeansX[d]); } output.SSBGy = new double[Dy]; for (int d = 0; d < Dy; ++d) { output.SSBGy[d] = GetSSG(groupped_y_at_dim[d], output.GrandMeansY[d]); } output.SSWGx = new double[Dx]; for (int d = 0; d < Dx; ++d) { output.SSWGx[d] = output.SSTx[d] - output.SSBGx[d]; } output.SSWGy = new double[Dy]; for (int d = 0; d < Dy; ++d) { output.SSWGy[d] = output.SSTy[d] - output.SSBGy[d]; } output.SCT = new double[Dy][]; for (int dy = 0; dy < Dy; ++dy) { output.SCT[dy] = new double[Dx]; for (int dx = 0; dx < Dx; ++dx) { output.SCT[dy][dx] = GetCovariance(X_transpose[dx], Y_transpose[dy]); } } output.SCWG = new double[Dy][]; for (int dy = 0; dy < Dy; ++dy) { output.SCWG[dy] = new double[Dx]; for (int dx = 0; dx < Dx; ++dx) { output.SCWG[dy][dx] = GetCovarianceWithinGroup(groupped_x_at_dim[dx], groupped_y_at_dim[dy]); } } output.rT = new double[Dy][]; for (int dy = 0; dy < Dy; ++dy) { output.rT[dy] = new double[Dx]; for (int dx = 0; dx < Dx; ++dx) { output.rT[dy][dx] = output.SCT[dy][dx] / System.Math.Sqrt(output.SSTx[dx] * output.SSTy[dy]); } } output.rWG = new double[Dy][]; for (int dy = 0; dy < Dy; ++dy) { output.rWG[dy] = new double[Dx]; for (int dx = 0; dx < Dx; ++dx) { output.rWG[dy][dx] = output.SCWG[dy][dx] / System.Math.Sqrt(output.SSWGx[dx] * output.SSWGy[dy]); } } output.Slope = new double[Dy][]; //b[i][k] where i is the number of columns in X and k is the number of columns in Y for (int dy = 0; dy < Dy; ++dy) { output.Slope[dy] = new double[Dx]; for (int dx = 0; dx < Dx; ++dx) { output.Slope[dy][dx] = output.SCWG[dx][dy] / output.SSWGx[dx]; } } output.MeanWithinGroups_x = GetMeanWithinGroup(groupped_x_at_dim); output.MeanWithinGroups_y = GetMeanWithinGroup(groupped_y_at_dim); output.Intercepts = GetIntercepts(output.MeanWithinGroups_x, output.MeanWithinGroups_y, output.GrandMeansX, output.Slope); double[][] Y_adj = new double[N][]; for (int i = 0; i < N; ++i) { Y_adj[i] = new double[Dy]; for (int dy = 0; dy < Dy; ++dy) { Y_adj[i][dy] = Y[i][dy] - DotProduct(output.Slope[dy], X[i]); } } MANOVA.RunMANOVA(Y_adj, grpCat, output, significance_level); } public static double DotProduct(double[] x1, double[] x2) { double sum = 0; for (int i = 0; i < x1.Length; ++i) { sum += x1[i] * x2[i]; } return sum; } public static Dictionary GetIntercepts(Dictionary mean_x_within_group, Dictionary mean_y_within_group, double[] grand_mean_x, double[][] b) { Dictionary intercepts = new Dictionary(); foreach (int grpId in mean_x_within_group.Keys) { double[] mean_x = mean_x_within_group[grpId]; double[] mean_y = mean_y_within_group[grpId]; int Dx = mean_x.Length; int Dy = mean_y.Length; intercepts[grpId] = new double[Dy]; for (int dy = 0; dy < Dy; ++dy) { double bx = 0; for (int dx = 0; dx < Dx; ++dx) { bx += b[dy][dx] * (mean_x[dx] - grand_mean_x[dx]); } intercepts[grpId][dy] = mean_y[dy] - bx; } } return intercepts; } /// /// Return the sum of squares total /// /// SST measures the total variability in the response variable /// /// all the data points in the sample containing all classes /// The mean of all the data points in the sample containing all classes /// The sum of squares total, which measures p=o--i9i9 public static double GetSST(double[] totalSample, out double grand_mean) { grand_mean = Mean.GetMean(totalSample); double SST = 0; int n = totalSample.Length; for (int i = 0; i < n; ++i) { double yd = totalSample[i] - grand_mean; SST += yd * yd; } return SST; } /// /// Return the sum of squares group (SSG) /// /// SSG measures the variability between groups /// This is also known as explained variablity: deviation of group mean from overral mean, weighted by sample size /// /// The sample groupped based on the classes /// public static double GetSSG(Dictionary> groupedSample, double grand_mean) { double SSG = 0; foreach (int grpId in groupedSample.Keys) { List group = groupedSample[grpId]; double group_mean = Mean.GetMean(group); double group_size = group.Count; SSG += group_size * (group_mean - grand_mean) * (group_mean - grand_mean); } return SSG; } public static double GetCovariance(double[] x, double[] y) { double sum_xy = 0; double sum_x = 0; double sum_y = 0; int N = x.Length; for (int i = 0; i < N; ++i) { sum_xy += x[i] * y[i]; sum_x += x[i]; sum_y += y[i]; } return sum_xy - (sum_x * sum_y) / N; } public static double GetCovarianceWithinGroup(Dictionary> groupped_x, Dictionary> groupped_y) { double SCG = 0; foreach (int grpId in groupped_x.Keys) { List group_x = groupped_x[grpId]; List group_y = groupped_y[grpId]; double SC_grp = GetCovariance(group_x, group_y); SCG += SC_grp; } return SCG; } public static double GetCovariance(List x, List y) { double sum_xy = 0; double sum_x = 0; double sum_y = 0; int N = x.Count; for (int i = 0; i < N; ++i) { sum_xy += x[i] * y[i]; sum_x += x[i]; sum_y += y[i]; } return sum_xy - (sum_x * sum_y) / N; } public static Dictionary GetMeanWithinGroup(Dictionary> groupSample) { Dictionary means = new Dictionary(); foreach (int grpId in groupSample.Keys) { means[grpId] = Mean.GetMean(groupSample[grpId]); } return means; } public static Dictionary GetMeanWithinGroup(Dictionary>[] groupSampleWithDim) { Dictionary means = new Dictionary(); int D = groupSampleWithDim.Length; foreach (int groupId in groupSampleWithDim[0].Keys) { means[groupId] = new double[D]; } for (int d = 0; d < D; ++d) { Dictionary> groupSample = groupSampleWithDim[d]; foreach (int grpId in groupSample.Keys) { means[grpId][d] = Mean.GetMean(groupSample[grpId]); } } return means; } } }