source: trunk/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.cs @ 15727

Last change on this file since 15727 was 15727, checked in by fholzing, 3 years ago

#2871: Fixed crossthreading-bug

File size: 9.7 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2018 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 HeuristicLab.Common;
26using HeuristicLab.Data;
27using HeuristicLab.MainForm;
28
29namespace HeuristicLab.Problems.DataAnalysis.Views {
30  [View("Variable Impacts")]
31  [Content(typeof(IRegressionSolution))]
32  public partial class RegressionSolutionVariableImpactsView : DataAnalysisSolutionEvaluationView {
33    #region Nested Types
34    private class BackgroundWorkerArguments {
35      internal MainForm.WindowsForms.MainForm mainForm;
36      internal RegressionSolutionVariableImpactsCalculator.ReplacementMethodEnum replMethod;
37      internal RegressionSolutionVariableImpactsCalculator.FactorReplacementMethodEnum factorReplMethod;
38      internal RegressionSolutionVariableImpactsCalculator.DataPartitionEnum dataPartition;
39    }
40
41    private enum SortingCriteria {
42      ImpactValue,
43      Occurrence,
44      VariableName
45    }
46    #endregion
47
48    #region Fields
49    private Dictionary<string, double> rawVariableImpacts = new Dictionary<string, double>();
50    #endregion
51
52    #region Getter/Setter
53    public new IRegressionSolution Content {
54      get { return (IRegressionSolution)base.Content; }
55      set {
56        base.Content = value;
57      }
58    }
59    #endregion
60
61    #region Ctor
62    public RegressionSolutionVariableImpactsView()
63      : base() {
64      InitializeComponent();
65
66      //Little workaround. If you fill the ComboBox-Items in the other partial class, the UI-Designer will moan.
67      this.sortByComboBox.Items.AddRange(Enum.GetValues(typeof(SortingCriteria)).Cast<object>().ToArray());
68      this.sortByComboBox.SelectedItem = SortingCriteria.ImpactValue;
69
70      //Set the default values
71      this.dataPartitionComboBox.SelectedIndex = 0;
72      this.replacementComboBox.SelectedIndex = 0;
73      this.factorVarReplComboBox.SelectedIndex = 0;
74    }
75    #endregion
76
77    #region Events
78    protected override void RegisterContentEvents() {
79      base.RegisterContentEvents();
80      Content.ModelChanged += new EventHandler(Content_ModelChanged);
81      Content.ProblemDataChanged += new EventHandler(Content_ProblemDataChanged);
82    }
83
84    protected override void DeregisterContentEvents() {
85      base.DeregisterContentEvents();
86      Content.ModelChanged -= new EventHandler(Content_ModelChanged);
87      Content.ProblemDataChanged -= new EventHandler(Content_ProblemDataChanged);
88    }
89
90    protected virtual void Content_ProblemDataChanged(object sender, EventArgs e) {
91      OnContentChanged();
92    }
93
94    protected virtual void Content_ModelChanged(object sender, EventArgs e) {
95      OnContentChanged();
96    }
97
98    protected override void OnContentChanged() {
99      base.OnContentChanged();
100      if (Content == null) {
101        variableImactsArrayView.Content = null;
102      } else {
103        StartBackgroundWorker();
104      }
105    }
106
107
108    private void dataPartitionComboBox_SelectedIndexChanged(object sender, EventArgs e) {
109      StartBackgroundWorker();
110    }
111
112    private void replacementComboBox_SelectedIndexChanged(object sender, EventArgs e) {
113      StartBackgroundWorker();
114    }
115
116    private void sortByComboBox_SelectedIndexChanged(object sender, EventArgs e) {
117      //Update the default ordering (asc,desc), but remove the eventHandler beforehand (otherwise the data would be ordered twice)
118      ascendingCheckBox.CheckedChanged -= ascendingCheckBox_CheckedChanged;
119      switch ((SortingCriteria)sortByComboBox.SelectedItem) {
120        case SortingCriteria.ImpactValue:
121          ascendingCheckBox.Checked = false;
122          break;
123        case SortingCriteria.Occurrence:
124          ascendingCheckBox.Checked = true;
125          break;
126        case SortingCriteria.VariableName:
127          ascendingCheckBox.Checked = true;
128          break;
129        default:
130          throw new NotImplementedException("Ordering for selected SortingCriteria not implemented");
131      }
132      ascendingCheckBox.CheckedChanged += ascendingCheckBox_CheckedChanged;
133
134      UpdateDataOrdering();
135    }
136
137    private void ascendingCheckBox_CheckedChanged(object sender, EventArgs e) {
138      UpdateDataOrdering();
139    }
140
141
142    private void BackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) {
143      variableImactsArrayView.Caption = Content.Name + " Variable Impacts";
144
145      var argument = e.Argument as BackgroundWorkerArguments;
146      if (!(argument is BackgroundWorkerArguments)) {
147        throw new ArgumentException("Argument for Backgroundworker must be of type BackgroundworkerArguments");
148      }
149
150      argument.mainForm.AddOperationProgressToView(this, "Calculating variable impacts for " + Content.Name);
151      try {
152        //Remember the original ordering of the variables
153        var impacts = RegressionSolutionVariableImpactsCalculator.CalculateImpacts(Content, argument.dataPartition, argument.replMethod, argument.factorReplMethod);
154        var problemData = Content.ProblemData;
155        var inputvariables = new HashSet<string>(problemData.AllowedInputVariables.Union(Content.Model.VariablesUsedForPrediction));
156        var originalVariableOrdering = problemData.Dataset.VariableNames.Where(v => inputvariables.Contains(v)).Where(problemData.Dataset.VariableHasType<double>).ToList();
157
158        rawVariableImpacts.Clear();
159        originalVariableOrdering.ForEach(v => rawVariableImpacts.Add(v, impacts.First(vv => vv.Item1 == v).Item2));
160      } finally {
161        argument.mainForm.RemoveOperationProgressFromView(this);
162      }
163    }
164
165    private void BackgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) {
166      UpdateDataOrdering();
167    }
168    #endregion
169
170    #region Helper Methods   
171    private void StartBackgroundWorker() {
172      //Check if the selection is valid
173      if (Content == null) { return; }
174      if (replacementComboBox.SelectedIndex < 0) { return; }
175      if (dataPartitionComboBox.SelectedIndex < 0) { return; }
176      if (factorVarReplComboBox.SelectedIndex < 0) { return; }
177
178      //Prepare arguments
179      var mainForm = (MainForm.WindowsForms.MainForm)MainFormManager.MainForm;
180      var replMethod = (RegressionSolutionVariableImpactsCalculator.ReplacementMethodEnum)replacementComboBox.Items[replacementComboBox.SelectedIndex];
181      var factorReplMethod = (RegressionSolutionVariableImpactsCalculator.FactorReplacementMethodEnum)factorVarReplComboBox.Items[factorVarReplComboBox.SelectedIndex];
182      var dataPartition = (RegressionSolutionVariableImpactsCalculator.DataPartitionEnum)dataPartitionComboBox.SelectedItem;
183      var args = new BackgroundWorkerArguments() {
184        mainForm = mainForm,
185        replMethod = replMethod,
186        factorReplMethod = factorReplMethod,
187        dataPartition = dataPartition
188      };
189
190      //Let the backgroundWorker do his job (unless he's already running)
191      //fholzing: Possible bug -> A ContentChanged won't update the data if the backgroundworker is already running
192      if (!backgroundWorker.IsBusy) { backgroundWorker.RunWorkerAsync(args); }
193    }
194
195    /// <summary>
196    /// Updates the <see cref="variableImactsArrayView"/> according to the selected ordering <see cref="ascendingCheckBox"/> of the selected Column <see cref="sortByComboBox"/>
197    /// The default is "Descending" by "VariableImpact" (as in previous versions)
198    /// </summary>
199    private void UpdateDataOrdering() {
200      //Check if valid sortingCriteria is selected and data exists
201      if (sortByComboBox.SelectedIndex == -1) { return; }
202      if (rawVariableImpacts == null) { return; }
203      if (!rawVariableImpacts.Any()) { return; }
204
205      var selectedItem = (SortingCriteria)sortByComboBox.SelectedItem;
206      bool ascending = ascendingCheckBox.Checked;
207
208      IEnumerable<KeyValuePair<string, double>> orderedEntries = null;
209
210      //Sort accordingly
211      switch (selectedItem) {
212        case SortingCriteria.ImpactValue:
213          orderedEntries = rawVariableImpacts.OrderBy(v => v.Value);
214          break;
215        case SortingCriteria.Occurrence:
216          orderedEntries = rawVariableImpacts;
217          break;
218        case SortingCriteria.VariableName:
219          orderedEntries = rawVariableImpacts.OrderBy(v => v.Key, new NaturalStringComparer());
220          break;
221        default:
222          throw new NotImplementedException("Ordering for selected SortingCriteria not implemented");
223      }
224
225      if (!ascending) { orderedEntries = orderedEntries.Reverse(); }
226
227      //Write the data back
228      var impactArray = new DoubleArray(orderedEntries.Select(i => i.Value).ToArray()) {
229        ElementNames = orderedEntries.Select(i => i.Key)
230      };
231
232      //Could be, if the View was closed during the BackgroundWorker-run
233      if (!variableImactsArrayView.IsDisposed) {
234        variableImactsArrayView.Content = (DoubleArray)impactArray.AsReadOnly();
235      }
236    }
237    #endregion
238  }
239}
Note: See TracBrowser for help on using the repository browser.