Free cookie consent management tool by TermsFeed Policy Generator

source: branches/StatisticalTesting/HeuristicLab.Analysis.Statistics/3.3/StatisticalTestingView.cs @ 11611

Last change on this file since 11611 was 11611, checked in by ascheibe, 9 years ago

#2031 adapted statistical testing view to new histogram

File size: 18.3 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Threading.Tasks;
26using System.Windows.Forms;
27using HeuristicLab.Collections;
28using HeuristicLab.Core.Views;
29using HeuristicLab.Data;
30using HeuristicLab.MainForm;
31using HeuristicLab.Optimization;
32using HeuristicLab.Optimization.Views;
33
34namespace HeuristicLab.Analysis.Statistics {
35  [View("Statistical Tests", "HeuristicLab.Analysis.Statistics.InfoResources.StatisticalTestsInfo.rtf")]
36  [Content(typeof(RunCollection), false)]
37  public sealed partial class StatisticalTestingView : ItemView {
38    private const double significanceLevel = 0.05;
39    private double[][] data;
40
41    public StatisticalTestingView() {
42      InitializeComponent();
43    }
44
45    public new RunCollection Content {
46      get { return (RunCollection)base.Content; }
47      set { base.Content = value; }
48    }
49
50    public override bool ReadOnly {
51      get { return true; }
52      set { /*not needed because results are always readonly */}
53    }
54
55    protected override void OnContentChanged() {
56      base.OnContentChanged();
57
58      if (Content != null) {
59        UpdateResultComboBox();
60        UpdateGroupsComboBox();
61        FillCompComboBox();
62        RebuildDataTable();
63      }
64      UpdateCaption();
65    }
66
67    private void UpdateCaption() {
68      Caption = Content != null ? Content.OptimizerName + " Statistical Tests" : ViewAttribute.GetViewName(GetType());
69    }
70
71    #region events
72    protected override void RegisterContentEvents() {
73      base.RegisterContentEvents();
74      Content.ItemsAdded += new CollectionItemsChangedEventHandler<IRun>(Content_ItemsAdded);
75      Content.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(Content_ItemsRemoved);
76      Content.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(Content_CollectionReset);
77      Content.UpdateOfRunsInProgressChanged += Content_UpdateOfRunsInProgressChanged;
78    }
79
80    protected override void DeregisterContentEvents() {
81      base.DeregisterContentEvents();
82      Content.ItemsAdded -= new CollectionItemsChangedEventHandler<IRun>(Content_ItemsAdded);
83      Content.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(Content_ItemsRemoved);
84      Content.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(Content_CollectionReset);
85      Content.UpdateOfRunsInProgressChanged -= Content_UpdateOfRunsInProgressChanged;
86    }
87
88    private void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
89      RebuildDataTable();
90    }
91
92    private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
93      RebuildDataTable();
94    }
95
96    private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
97      RebuildDataTable();
98    }
99
100    void Content_UpdateOfRunsInProgressChanged(object sender, EventArgs e) {
101      if (!Content.UpdateOfRunsInProgress) {
102        RebuildDataTable();
103      }
104    }
105    #endregion
106
107    private void UpdateGroupsComboBox() {
108      groupComboBox.Items.Clear();
109
110      var parameters = (from run in Content
111                        where run.Visible
112                        from param in run.Parameters
113                        select param.Key).Distinct().ToArray();
114
115      foreach (var p in parameters) {
116        var variations = (from run in Content
117                          where run.Visible && run.Parameters.ContainsKey(p) &&
118                          (run.Parameters[p] is IntValue || run.Parameters[p] is DoubleValue ||
119                          run.Parameters[p] is StringValue || run.Parameters[p] is BoolValue)
120                          select ((dynamic)run.Parameters[p]).Value).Distinct();
121
122        if (variations.Count() > 1) {
123          groupComboBox.Items.Add(p);
124        }
125      }
126
127      if (groupComboBox.Items.Count > 0) {
128        //try to select something different than "Seed" or "Algorithm Name" as this makes no sense
129        //and takes a long time to group
130        List<int> possibleIndizes = new List<int>();
131        for (int i = 0; i < groupComboBox.Items.Count; i++) {
132          if (groupComboBox.Items[i].ToString() != "Seed"
133            && groupComboBox.Items[i].ToString() != "Algorithm Name") {
134            possibleIndizes.Add(i);
135          }
136        }
137
138        if (possibleIndizes.Count > 0) {
139          groupComboBox.SelectedItem = groupComboBox.Items[possibleIndizes.First()];
140        } else {
141          groupComboBox.SelectedItem = groupComboBox.Items[0];
142        }
143      }
144    }
145
146    private string[] GetColumnNames(IEnumerable<IRun> runs) {
147      string parameterName = (string)groupComboBox.SelectedItem;
148      var r = runs.Where(x => x.Parameters.ContainsKey(parameterName));
149      return r.Select(x => ((dynamic)x.Parameters[parameterName]).Value).Distinct().Select(x => (string)x.ToString()).ToArray();
150    }
151
152    private void UpdateResultComboBox() {
153      resultComboBox.Items.Clear();
154      var results = (from run in Content
155                     where run.Visible
156                     from result in run.Results
157                     where result.Value is IntValue || result.Value is DoubleValue
158                     select result.Key).Distinct().ToArray();
159
160      resultComboBox.Items.AddRange(results);
161      if (resultComboBox.Items.Count > 0) resultComboBox.SelectedItem = resultComboBox.Items[0];
162    }
163
164    private void FillCompComboBox() {
165      string parameterName = (string)groupComboBox.SelectedItem;
166      if (parameterName != null) {
167        string resultName = (string)resultComboBox.SelectedItem;
168        if (resultName != null) {
169          var runs = Content.Where(x => x.Results.ContainsKey(resultName) && x.Visible);
170          var columnNames = GetColumnNames(runs).ToList();
171          groupCompComboBox.Items.Clear();
172          columnNames.ForEach(x => groupCompComboBox.Items.Add(x));
173          if (groupCompComboBox.Items.Count > 0) groupCompComboBox.SelectedItem = groupCompComboBox.Items[0];
174        }
175      }
176    }
177
178    private void RebuildDataTable() {
179      string parameterName = (string)groupComboBox.SelectedItem;
180      if (parameterName != null) {
181        string resultName = (string)resultComboBox.SelectedItem;
182
183        var runs = Content.Where(x => x.Results.ContainsKey(resultName) && x.Visible);
184        var columnNames = GetColumnNames(runs);
185        var groups = GetGroups(columnNames, runs);
186        data = new double[columnNames.Count()][];
187
188        DoubleMatrix dt = new DoubleMatrix(groups.Select(x => x.Count()).Max(), columnNames.Count());
189        dt.ColumnNames = columnNames;
190        DataTable histogramDataTable = new DataTable(resultName);
191
192        for (int i = 0; i < columnNames.Count(); i++) {
193          int j = 0;
194          data[i] = new double[groups[i].Count()];
195          DataRow row = new DataRow(columnNames[i]);
196          row.VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Histogram;
197          histogramDataTable.Rows.Add(row);
198
199          foreach (IRun run in groups[i]) {
200            dt[j, i] = (double)((dynamic)run.Results[resultName]).Value;
201            data[i][j] = dt[j, i];
202            row.Values.Add(dt[j, i]);
203            j++;
204          }
205        }
206
207        GenerateChart(histogramDataTable);
208        stringConvertibleMatrixView.Content = dt;
209      }
210    }
211
212    private void GenerateChart(DataTable histogramTable) {
213      foreach (var row in histogramTable.Rows) {
214        dataTableView.AddPoints(row.Name, row.Values, true);
215      }
216    }
217
218    private List<IEnumerable<IRun>> GetGroups(string[] columnNames, IEnumerable<IRun> runs) {
219      List<IEnumerable<IRun>> runCols = new List<IEnumerable<IRun>>();
220      string parameterName = (string)groupComboBox.SelectedItem;
221
222      foreach (string cn in columnNames) {
223        var tmpRuns = runs.Where(x => ((string)((dynamic)x.Parameters[parameterName]).Value.ToString()) == cn);
224        runCols.Add(tmpRuns);
225      }
226
227      return runCols;
228    }
229
230    private void ResetUI() {
231      normalityLabel.Image = null;
232      groupCompLabel.Image = null;
233      pairwiseLabel.Image = null;
234      pValTextBox.Text = string.Empty;
235      equalDistsTextBox.Text = string.Empty;
236    }
237
238    private void resultComboBox_SelectedValueChanged(object sender, EventArgs e) {
239      RebuildDataTable();
240      ResetUI();
241      CalculateValues();
242    }
243
244    private void groupComboBox_SelectedValueChanged(object sender, EventArgs e) {
245      FillCompComboBox();
246      RebuildDataTable();
247      ResetUI();
248      CalculateValues();
249    }
250
251    private bool VerifyDataLength(bool showMessage) {
252      if (data == null || data.Length == 0)
253        return false;
254
255      //alglib needs at least 5 samples for computation
256      if (data.Any(x => x.Length <= 5)) {
257        if (showMessage)
258          MessageBox.Show(this, "You need to choose samples with a size greater 5.", "HeuristicLab", MessageBoxButtons.OK,
259            MessageBoxIcon.Error);
260        return false;
261      }
262      return true;
263    }
264
265    private void CalculateValues() {
266      if (!VerifyDataLength(true))
267        return;
268
269      if (data != null) {
270        MainFormManager.GetMainForm<HeuristicLab.MainForm.WindowsForms.MainForm>()
271          .AddOperationProgressToView(this, "Calculating...");
272
273        string curItem = (string)groupCompComboBox.SelectedItem;
274        Task.Factory.StartNew(() => CalculateValuesAsync(curItem));
275      }
276    }
277
278    private void CalculateValuesAsync(string groupName) {
279      TestAllGroups();
280      CalculateNormality();
281      CalculateNormalityDetails();
282      CalculatePairwiseTest(groupName);
283      CalculatePairwiseTestDetails(groupName);
284
285      MainFormManager.GetMainForm<HeuristicLab.MainForm.WindowsForms.MainForm>().RemoveOperationProgressFromView(this);
286    }
287
288    private void CalculatePairwise(string groupName) {
289      if (!VerifyDataLength(false))
290        return;
291
292      MainFormManager.GetMainForm<HeuristicLab.MainForm.WindowsForms.MainForm>().AddOperationProgressToView(this, "Calculating...");
293      Task.Factory.StartNew(() => CalculatePairwiseAsync(groupName));
294    }
295
296    private void CalculatePairwiseAsync(string groupName) {
297      CalculatePairwiseTest(groupName);
298      CalculatePairwiseTestDetails(groupName);
299
300      MainFormManager.GetMainForm<HeuristicLab.MainForm.WindowsForms.MainForm>().RemoveOperationProgressFromView(this);
301    }
302
303    private void TestAllGroups() {
304      double pval = KruskalWallis.Test(data);
305      pValTextBox.Text = pval.ToString();
306      if (pval < significanceLevel) {
307        this.Invoke(new Action(() => { groupCompLabel.Image = HeuristicLab.Analysis.Statistics.Resources.Default; }));
308      } else {
309        this.Invoke(new Action(() => { groupCompLabel.Image = HeuristicLab.Common.Resources.VSImageLibrary.Warning; }));
310      }
311    }
312
313    private void CalculateNormality() {
314      double val;
315      List<double> res = new List<double>();
316
317      for (int i = 0; i < data.Length; i++) {
318        alglib.jarqueberatest(data[i], data[i].Length, out val);
319        res.Add(val);
320      }
321
322      // p-value is below significance level and thus the null hypothesis (data is normally distributed) is rejected.
323      if (res.Any(x => x < significanceLevel)) {
324        this.Invoke(new Action(() => { normalityLabel.Image = HeuristicLab.Common.Resources.VSImageLibrary.Warning; }));
325      } else {
326        this.Invoke(new Action(() => { normalityLabel.Image = HeuristicLab.Analysis.Statistics.Resources.Default; }));
327      }
328    }
329
330    private void CalculateNormalityDetails() {
331      DoubleMatrix pValsMatrix = new DoubleMatrix(1, stringConvertibleMatrixView.Content.Columns);
332      pValsMatrix.ColumnNames = stringConvertibleMatrixView.Content.ColumnNames;
333      pValsMatrix.RowNames = new string[] { "p-Value" };
334
335      double val;
336      for (int i = 0; i < data.Length; i++) {
337        alglib.jarqueberatest(data[i], data[i].Length, out val);
338        pValsMatrix[0, i] = val;
339      }
340
341      this.Invoke(new Action(() => {
342        normalityStringConvertibleMatrixView.Content = pValsMatrix;
343        normalityStringConvertibleMatrixView.DataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
344      }));
345    }
346
347    private void CalculatePairwiseTest(string groupName) {
348      int colIndex = 0;
349      IEnumerable<string> columnNames = null;
350      this.Invoke(new Action(() => { columnNames = stringConvertibleMatrixView.Content.ColumnNames; }));
351
352      foreach (string col in columnNames) {
353        if (col == groupName) {
354          break;
355        }
356        colIndex++;
357      }
358
359      double[][] newData = FilterDataForPairwiseTest(colIndex);
360
361      double mwuBothtails;
362      double mwuLefttail;
363      double mwuRighttail;
364      int cnt = 0;
365
366      for (int i = 0; i < newData.Length; i++) {
367        alglib.mannwhitneyutest(data[colIndex], data[colIndex].Length, newData[i], newData[i].Length, out mwuBothtails, out mwuLefttail, out mwuRighttail);
368        if (mwuBothtails > significanceLevel) {
369          cnt++;
370        }
371      }
372
373      double ratio = ((double)cnt) / (data.Length - 1) * 100.0;
374      equalDistsTextBox.Text = ratio.ToString() + " %";
375
376      if (cnt == 0) {
377        this.Invoke(new Action(() => { pairwiseLabel.Image = HeuristicLab.Analysis.Statistics.Resources.Default; }));
378      } else {
379        this.Invoke(new Action(() => { pairwiseLabel.Image = HeuristicLab.Common.Resources.VSImageLibrary.Warning; }));
380      }
381    }
382
383    private double[][] FilterDataForPairwiseTest(int columnToRemove) {
384      double[][] newData = new double[data.Length - 1][];
385
386      int i = 0;
387      int l = 0;
388      while (i < data.Length) {
389        if (i != columnToRemove) {
390          double[] row = new double[data[i].Length - 1];
391          newData[l] = row;
392
393          int j = 0, k = 0;
394          while (j < row.Length) {
395            if (i != columnToRemove) {
396              newData[l][j] = data[i][k];
397              j++;
398              k++;
399            } else {
400              k++;
401            }
402          }
403          i++;
404          l++;
405        } else {
406          i++;
407        }
408      }
409      return newData;
410    }
411
412    private void CalculatePairwiseTestDetails(string groupName) {
413      int colIndex = 0;
414      IEnumerable<string> columnNames = null;
415      this.Invoke(new Action(() => { columnNames = stringConvertibleMatrixView.Content.ColumnNames; }));
416
417      foreach (string col in columnNames) {
418        if (col == groupName) {
419          break;
420        }
421        colIndex++;
422      }
423
424      double[][] newData = FilterDataForPairwiseTest(colIndex);
425
426      columnNames = columnNames.Where(x => x != groupName).ToList();
427
428      var rowNames = new string[] { "p-Value of Mann-Whitney U", "Adjusted p-Value of Mann-Whitney U",
429            "p-Value of T-Test", "Adjusted p-Value of T-Test", "Necessary Sample Size for T-Test", "Cohen's d", "Hedges' g" };
430
431      DoubleMatrix pValsMatrix = new DoubleMatrix(rowNames.Length, columnNames.Count());
432      pValsMatrix.ColumnNames = columnNames;
433      pValsMatrix.RowNames = rowNames;
434
435      double mwuBothtails;
436      double mwuLefttail;
437      double mwuRighttail;
438      double tTestLefttail;
439      double[] mwuPValues = new double[newData.Length];
440      double[] tTestPValues = new double[newData.Length];
441      bool[] decision = null;
442      double[] adjustedMwuPValues = null;
443      double[] adjustedTtestPValues = null;
444
445      for (int i = 0; i < newData.Length; i++) {
446        if (i != colIndex) {
447          alglib.mannwhitneyutest(data[colIndex], data[colIndex].Length, newData[i], newData[i].Length, out mwuBothtails,
448            out mwuLefttail, out mwuRighttail);
449          tTestLefttail = TTest.Test(data[colIndex], newData[i]);
450          mwuPValues[i] = mwuBothtails;
451          tTestPValues[i] = tTestLefttail;
452        }
453      }
454
455      adjustedMwuPValues = BonferroniHolm.Calculate(significanceLevel, mwuPValues, out decision);
456      adjustedTtestPValues = BonferroniHolm.Calculate(significanceLevel, tTestPValues, out decision);
457
458      for (int i = 0; i < newData.Length; i++) {
459        if (i != colIndex) {
460          pValsMatrix[0, i] = mwuPValues[i];
461          pValsMatrix[1, i] = adjustedMwuPValues[i];
462          pValsMatrix[2, i] = tTestPValues[i];
463          pValsMatrix[3, i] = adjustedTtestPValues[i];
464          pValsMatrix[4, i] = TTest.GetOptimalSampleSize(data[colIndex], newData[i]);
465          pValsMatrix[5, i] = SampleSizeDetermination.CalculateCohensD(data[colIndex], newData[i]);
466          pValsMatrix[6, i] = SampleSizeDetermination.CalculateHedgesG(data[colIndex], newData[i]);
467        }
468      }
469
470      this.Invoke(new Action(() => {
471        pairwiseStringConvertibleMatrixView.Content = pValsMatrix;
472        pairwiseStringConvertibleMatrixView.DataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
473      }));
474    }
475
476    private void openBoxPlotToolStripMenuItem_Click(object sender, EventArgs e) {
477      RunCollectionBoxPlotView boxplotView = new RunCollectionBoxPlotView();
478      boxplotView.Content = Content;
479      // TODO: enable as soon as we move to HeuristicLab.Optimization.Views
480      // boxplotView.xAxisComboBox.SelectedItem = xAxisComboBox.SelectedItem;
481      // boxplotView.yAxisComboBox.SelectedItem = yAxisComboBox.SelectedItem;
482      boxplotView.Show();
483    }
484
485    private void groupCompComboBox_SelectedValueChanged(object sender, EventArgs e) {
486      string curItem = (string)groupCompComboBox.SelectedItem;
487      CalculatePairwise(curItem);
488    }
489  }
490}
Note: See TracBrowser for help on using the repository browser.