source: branches/2971_named_intervals/HeuristicLab.Problems.DataAnalysis.Views/3.4/Classification/ClassificationSolutionVariableImpactsView.cs @ 17304

Last change on this file since 17304 was 17304, checked in by chaider, 3 years ago

#2971 merged trunk into branch

File size: 10.4 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 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;
26using System.Threading.Tasks;
27using HeuristicLab.Common;
28using HeuristicLab.Data;
29using HeuristicLab.MainForm;
30
31namespace HeuristicLab.Problems.DataAnalysis.Views {
32  [View("Variable Impacts")]
33  [Content(typeof(IClassificationSolution))]
34  public partial class ClassificationSolutionVariableImpactsView : DataAnalysisSolutionEvaluationView {
35    private enum SortingCriteria {
36      ImpactValue,
37      Occurrence,
38      VariableName
39    }
40    private CancellationTokenSource cancellationToken = new CancellationTokenSource();
41    private List<Tuple<string, double>> rawVariableImpacts = new List<Tuple<string, double>>();
42    private bool attachedToProgress = false;
43
44    public new IClassificationSolution Content {
45      get { return (IClassificationSolution)base.Content; }
46      set {
47        base.Content = value;
48      }
49    }
50
51    public ClassificationSolutionVariableImpactsView()
52      : base() {
53      InitializeComponent();
54
55      //Set the default values
56      this.dataPartitionComboBox.SelectedIndex = 0;
57      this.replacementComboBox.SelectedIndex = 3;
58      this.factorVarReplComboBox.SelectedIndex = 0;
59      this.sortByComboBox.SelectedItem = SortingCriteria.ImpactValue;
60    }
61
62    protected override void RegisterContentEvents() {
63      base.RegisterContentEvents();
64      Content.ModelChanged += new EventHandler(Content_ModelChanged);
65      Content.ProblemDataChanged += new EventHandler(Content_ProblemDataChanged);
66    }
67    protected override void DeregisterContentEvents() {
68      base.DeregisterContentEvents();
69      Content.ModelChanged -= new EventHandler(Content_ModelChanged);
70      Content.ProblemDataChanged -= new EventHandler(Content_ProblemDataChanged);
71    }
72
73    protected virtual void Content_ProblemDataChanged(object sender, EventArgs e) {
74      OnContentChanged();
75    }
76    protected virtual void Content_ModelChanged(object sender, EventArgs e) {
77      OnContentChanged();
78    }
79    protected override void OnContentChanged() {
80      base.OnContentChanged();
81      rawVariableImpacts.Clear();
82
83      if (Content == null) {
84        variableImpactsArrayView.Content = null;
85      } else {
86        UpdateVariableImpact();
87      }
88    }
89    protected override void OnHidden(EventArgs e) {
90      base.OnHidden(e);
91      cancellationToken.Cancel();
92
93      if (attachedToProgress) {
94        Progress.Hide(this);
95        attachedToProgress = false;
96      }
97    }
98
99    private void dataPartitionComboBox_SelectedIndexChanged(object sender, EventArgs e) {
100      rawVariableImpacts.Clear();
101      UpdateVariableImpact();
102    }
103    private void replacementComboBox_SelectedIndexChanged(object sender, EventArgs e) {
104      rawVariableImpacts.Clear();
105      UpdateVariableImpact();
106    }
107    private void sortByComboBox_SelectedIndexChanged(object sender, EventArgs e) {
108      //Update the default ordering (asc,desc), but remove the eventHandler beforehand (otherwise the data would be ordered twice)
109      ascendingCheckBox.CheckedChanged -= ascendingCheckBox_CheckedChanged;
110      ascendingCheckBox.Checked = (SortingCriteria)sortByComboBox.SelectedItem != SortingCriteria.ImpactValue;
111      ascendingCheckBox.CheckedChanged += ascendingCheckBox_CheckedChanged;
112
113      UpdateOrdering();
114    }
115    private void ascendingCheckBox_CheckedChanged(object sender, EventArgs e) {
116      UpdateOrdering();
117    }
118
119    private async void UpdateVariableImpact() {
120      IProgress progress;
121
122      //Check if the selection is valid
123      if (Content == null) { return; }
124      if (replacementComboBox.SelectedIndex < 0) { return; }
125      if (dataPartitionComboBox.SelectedIndex < 0) { return; }
126      if (factorVarReplComboBox.SelectedIndex < 0) { return; }
127
128      //Prepare arguments
129      var replMethod = (ClassificationSolutionVariableImpactsCalculator.ReplacementMethodEnum)replacementComboBox.Items[replacementComboBox.SelectedIndex];
130      var factorReplMethod = (ClassificationSolutionVariableImpactsCalculator.FactorReplacementMethodEnum)factorVarReplComboBox.Items[factorVarReplComboBox.SelectedIndex];
131      var dataPartition = (ClassificationSolutionVariableImpactsCalculator.DataPartitionEnum)dataPartitionComboBox.SelectedItem;
132
133      variableImpactsArrayView.Caption = Content.Name + " Variable Impacts";
134      progress = Progress.Show(this, "Calculating variable impacts for " + Content.Name);
135      attachedToProgress = true;
136      cancellationToken = new CancellationTokenSource();
137
138      try {
139        var problemData = Content.ProblemData;
140        var inputvariables = new HashSet<string>(problemData.AllowedInputVariables.Union(Content.Model.VariablesUsedForPrediction));
141        //Remember the original ordering of the variables
142        var originalVariableOrdering = problemData.Dataset.VariableNames
143          .Where(v => inputvariables.Contains(v))
144          .Where(v => problemData.Dataset.VariableHasType<double>(v) || problemData.Dataset.VariableHasType<string>(v))
145          .ToList();
146
147        List<Tuple<string, double>> impacts = null;
148        await Task.Run(() => { impacts = CalculateVariableImpacts(originalVariableOrdering, Content.Model, problemData, Content.EstimatedClassValues, dataPartition, replMethod, factorReplMethod, cancellationToken.Token, progress); });
149        if (impacts == null) { return; }
150
151        rawVariableImpacts.AddRange(impacts);
152        UpdateOrdering();
153      } finally {
154        if (attachedToProgress) {
155          Progress.Hide(this);
156          attachedToProgress = false;
157        }
158      }
159    }
160    private List<Tuple<string, double>> CalculateVariableImpacts(List<string> originalVariableOrdering,
161      IClassificationModel model,
162      IClassificationProblemData problemData,
163      IEnumerable<double> estimatedClassValues,
164      ClassificationSolutionVariableImpactsCalculator.DataPartitionEnum dataPartition,
165      ClassificationSolutionVariableImpactsCalculator.ReplacementMethodEnum replMethod,
166      ClassificationSolutionVariableImpactsCalculator.FactorReplacementMethodEnum factorReplMethod,
167      CancellationToken token,
168      IProgress progress) {
169      List<Tuple<string, double>> impacts = new List<Tuple<string, double>>();
170      int count = originalVariableOrdering.Count;
171      int i = 0;
172      var modifiableDataset = ((Dataset)(problemData.Dataset).Clone()).ToModifiable();
173      IEnumerable<int> rows = ClassificationSolutionVariableImpactsCalculator.GetPartitionRows(dataPartition, problemData);
174
175      //Calculate original quality-values (via calculator, default is R²)
176      IEnumerable<double> targetValuesPartition = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, rows);
177      IEnumerable<double> estimatedClassValuesPartition = Content.GetEstimatedClassValues(rows);
178
179      var originalCalculatorValue = ClassificationSolutionVariableImpactsCalculator.CalculateQuality(targetValuesPartition, estimatedClassValuesPartition);
180      var clonedModel = (IClassificationModel)model.Clone();
181      foreach (var variableName in originalVariableOrdering) {
182        if (cancellationToken.Token.IsCancellationRequested) { return null; }
183        progress.ProgressValue = (double)++i / count;
184        progress.Message = string.Format("Calculating impact for variable {0} ({1} of {2})", variableName, i, count);
185
186        double impact = 0;
187        //If the variable isn't used for prediction, it has zero impact.
188        if (model.VariablesUsedForPrediction.Contains(variableName)) {
189          impact = ClassificationSolutionVariableImpactsCalculator.CalculateImpact(variableName, clonedModel, problemData, modifiableDataset, rows, replMethod, factorReplMethod, targetValuesPartition, originalCalculatorValue);
190        }
191        impacts.Add(new Tuple<string, double>(variableName, impact));
192      }
193
194      return impacts;
195    }
196
197    /// <summary>
198    /// Updates the <see cref="variableImpactsArrayView"/> according to the selected ordering <see cref="ascendingCheckBox"/> of the selected Column <see cref="sortByComboBox"/>
199    /// The default is "Descending" by "VariableImpact" (as in previous versions)
200    /// </summary>
201    private void UpdateOrdering() {
202      //Check if valid sortingCriteria is selected and data exists
203      if (sortByComboBox.SelectedIndex == -1) { return; }
204      if (rawVariableImpacts == null) { return; }
205      if (!rawVariableImpacts.Any()) { return; }
206
207      var selectedItem = (SortingCriteria)sortByComboBox.SelectedItem;
208      bool ascending = ascendingCheckBox.Checked;
209
210      IEnumerable<Tuple<string, double>> orderedEntries = null;
211
212      //Sort accordingly
213      switch (selectedItem) {
214        case SortingCriteria.ImpactValue:
215          orderedEntries = rawVariableImpacts.OrderBy(v => v.Item2);
216          break;
217        case SortingCriteria.Occurrence:
218          orderedEntries = rawVariableImpacts;
219          break;
220        case SortingCriteria.VariableName:
221          orderedEntries = rawVariableImpacts.OrderBy(v => v.Item1, new NaturalStringComparer());
222          break;
223        default:
224          throw new NotImplementedException("Ordering for selected SortingCriteria not implemented");
225      }
226
227      if (!ascending) { orderedEntries = orderedEntries.Reverse(); }
228
229      //Write the data back
230      var impactArray = new DoubleArray(orderedEntries.Select(i => i.Item2).ToArray()) {
231        ElementNames = orderedEntries.Select(i => i.Item1)
232      };
233
234      //Could be, if the View was closed
235      if (!variableImpactsArrayView.IsDisposed) {
236        variableImpactsArrayView.Content = (DoubleArray)impactArray.AsReadOnly();
237      }
238    }
239  }
240}
Note: See TracBrowser for help on using the repository browser.