source: branches/HeuristicLab.RegressionSolutionGradientView/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.cs @ 14014

Last change on this file since 14014 was 14014, checked in by bburlacu, 6 years ago

#2597: Fixed small issue with layout update when the number of columns was changed. Improved layout of the configuration panel. Made GradientView invisible as an item in the Solution View.

File size: 14.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 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.Drawing;
25using System.Linq;
26using System.Threading.Tasks;
27using System.Windows.Forms;
28using HeuristicLab.Common;
29using HeuristicLab.MainForm;
30using HeuristicLab.Visualization.ChartControlsExtensions;
31
32namespace HeuristicLab.Problems.DataAnalysis.Views {
33  [View("Target Response Gradients")]
34  [Content(typeof(IRegressionSolution))]
35  public partial class RegressionSolutionTargetResponseGradientView : DataAnalysisSolutionEvaluationView {
36    private readonly Dictionary<string, GradientChart> gradientCharts;
37    private readonly Dictionary<string, DensityChart> densityCharts;
38    private readonly Dictionary<string, Panel> groupingPanels;
39
40    private const int Points = 200;
41    private int MaxColumns = 4;
42
43    private IEnumerable<string> VisibleVariables {
44      get {
45        foreach (ListViewItem item in variableListView.CheckedItems)
46          yield return item.Text;
47      }
48    }
49    private IEnumerable<GradientChart> VisibleGradientCharts {
50      get { return VisibleVariables.Select(v => gradientCharts[v]); }
51    }
52    private IEnumerable<DensityChart> VisibleDensityCharts {
53      get { return VisibleVariables.Select(v => densityCharts[v]); }
54    }
55    private IEnumerable<Panel> VisibleChartsPanels {
56      get { return VisibleVariables.Select(v => groupingPanels[v]); }
57    }
58
59    public RegressionSolutionTargetResponseGradientView() {
60      InitializeComponent();
61      gradientCharts = new Dictionary<string, GradientChart>();
62      densityCharts = new Dictionary<string, DensityChart>();
63      groupingPanels = new Dictionary<string, Panel>();
64
65      limitView.Content = new DoubleLimit(0, 1);
66      densityComboBox.SelectedIndex = 0; // select None
67
68      columnsTextBox.Text = "4";
69
70      // Avoid additional horizontal scrollbar
71      var vertScrollWidth = SystemInformation.VerticalScrollBarWidth;
72      scrollPanel.Padding = new Padding(0, 0, vertScrollWidth, 0);
73      scrollPanel.AutoScroll = true;
74    }
75
76    public new IRegressionSolution Content {
77      get { return (IRegressionSolution)base.Content; }
78      set { base.Content = value; }
79    }
80
81    protected override void RegisterContentEvents() {
82      base.RegisterContentEvents();
83      variableListView.ItemChecked += variableListView_ItemChecked;
84      limitView.Content.ValueChanged += limit_ValueChanged;
85      automaticYAxisCheckBox.CheckedChanged += automaticYAxisCheckBox_CheckedChanged;
86      Content.ModelChanged += solution_ModelChanged;
87    }
88
89    protected override void DeregisterContentEvents() {
90      variableListView.ItemChecked -= variableListView_ItemChecked;
91      limitView.Content.ValueChanged -= limit_ValueChanged;
92      automaticYAxisCheckBox.CheckedChanged -= automaticYAxisCheckBox_CheckedChanged;
93      Content.ModelChanged -= solution_ModelChanged;
94      base.DeregisterContentEvents();
95    }
96
97    protected override void OnContentChanged() {
98      base.OnContentChanged();
99      if (Content == null) return;
100      var problemData = Content.ProblemData;
101
102      // Init Y-axis range
103      double min = double.MaxValue, max = double.MinValue;
104      var trainingTarget = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TrainingIndices);
105      foreach (var t in trainingTarget) {
106        if (t < min) min = t;
107        if (t > max) max = t;
108      }
109      double range = max - min;
110      const double scale = 1.0 / 3.0;
111      double axisMin, axisMax, axisInterval;
112      ChartUtil.CalculateAxisInterval(min - scale * range, max + scale * range, 5, out axisMin, out axisMax, out axisInterval);
113      automaticYAxisCheckBox.Checked = false;
114      limitView.ReadOnly = false;
115      limitView.Content.Lower = axisMin;
116      limitView.Content.Upper = axisMax;
117
118      // create dataset
119      var allowedInputVariables = Content.ProblemData.AllowedInputVariables;
120      var variableValues = allowedInputVariables.Select(x => new List<double> { problemData.Dataset.GetDoubleValues(x, problemData.TrainingIndices).Median() });
121      var sharedFixedVariables = new ModifiableDataset(allowedInputVariables, variableValues);
122
123      // create controls
124      gradientCharts.Clear();
125      densityCharts.Clear();
126      groupingPanels.Clear();
127      foreach (var variableName in allowedInputVariables) {
128        var gradientChart = CreateGradientChart(variableName, sharedFixedVariables);
129        gradientCharts.Add(variableName, gradientChart);
130
131        var densityChart = new DensityChart() {
132          Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right,
133          Margin = Padding.Empty,
134          Height = 12,
135          Visible = false
136        };
137        densityCharts.Add(variableName, densityChart);
138
139        var panel = new Panel() {
140          Dock = DockStyle.Fill,
141          Margin = Padding.Empty,
142          BackColor = Color.White
143        };
144
145        panel.Controls.Add(densityChart);
146        panel.Controls.Add(gradientChart);
147        groupingPanels.Add(variableName, panel);
148      }
149
150      // update variable list
151      variableListView.Items.Clear();
152      foreach (var variable in allowedInputVariables)
153        variableListView.Items.Add(key: variable, text: variable, imageIndex: 0);
154
155      foreach (var variable in Content.Model.VariablesUsedForPrediction)
156        variableListView.Items[variable].Checked = true;
157    }
158    private GradientChart CreateGradientChart(string variableName, ModifiableDataset sharedFixedVariables) {
159      var gradientChart = new GradientChart {
160        Dock = DockStyle.Fill,
161        Margin = Padding.Empty,
162        ShowLegend = false,
163        ShowCursor = true,
164        ShowConfigButton = false,
165        YAxisTicks = 5,
166      };
167      gradientChart.VariableValueChanged += async (o, e) => {
168        var recalculations = VisibleGradientCharts.Except(new[] { (GradientChart)o }).Select(async chart => {
169          await chart.RecalculateAsync(updateOnFinish: false, resetYAxis: false);
170        }).ToList();
171        await Task.WhenAll(recalculations);
172
173        if (recalculations.All(t => t.IsCompleted))
174          SetupYAxis();
175      };
176      gradientChart.Configure(new[] { Content }, sharedFixedVariables, variableName, Points);
177      gradientChart.SolutionAdded += gradientChart_SolutionAdded;
178      gradientChart.SolutionRemoved += gradientChart_SolutionRemoved;
179      return gradientChart;
180    }
181
182    private void SetupYAxis() {
183      double axisMin, axisMax;
184      if (automaticYAxisCheckBox.Checked) {
185        double min = double.MaxValue, max = double.MinValue;
186        foreach (var chart in VisibleGradientCharts) {
187          if (chart.YMin < min) min = chart.YMin;
188          if (chart.YMax > max) max = chart.YMax;
189        }
190
191        double axisInterval;
192        ChartUtil.CalculateAxisInterval(min, max, 5, out axisMin, out axisMax, out axisInterval);
193      } else {
194        axisMin = limitView.Content.Lower;
195        axisMax = limitView.Content.Upper;
196      }
197
198      foreach (var chart in VisibleGradientCharts) {
199        chart.FixedYAxisMin = axisMin;
200        chart.FixedYAxisMax = axisMax;
201        //chart.Update();
202      }
203    }
204
205    // reorder chart controls so that they always appear in the same order as in the list view
206    // the table layout containing the controls should be suspended before calling this method
207    private void ReOrderControls() {
208      var tl = gradientChartTableLayout;
209      tl.Controls.Clear();
210      int row = 0, column = 0;
211      foreach (var v in VisibleVariables) {
212        var chartsPanel = groupingPanels[v];
213        tl.Controls.Add(chartsPanel, column, row);
214
215        if (column == 0) {
216          var chart = gradientCharts[v];
217          chart.YAxisTitle = Content.ProblemData.TargetVariable;
218        }
219
220        column++;
221
222        if (column == MaxColumns) {
223          row++;
224          column = 0;
225        }
226      }
227    }
228
229    private void SetStyles() {
230      var tl = gradientChartTableLayout;
231      tl.RowStyles.Clear();
232      tl.ColumnStyles.Clear();
233      int numVariables = VisibleVariables.Count();
234      if (numVariables == 0)
235        return;
236
237      // set column styles
238      tl.ColumnCount = Math.Min(numVariables, MaxColumns);
239      for (int c = 0; c < tl.ColumnCount; c++)
240        tl.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100.0f / tl.ColumnCount));
241
242      // set row styles
243      tl.RowCount = (int)Math.Ceiling((double)numVariables / tl.ColumnCount);
244      var columnWidth = tl.Width / tl.ColumnCount; // assume all columns have the same width
245      var rowHeight = (int)(0.8 * columnWidth);
246      for (int r = 0; r < tl.RowCount; r++)
247        tl.RowStyles.Add(new RowStyle(SizeType.Absolute, rowHeight));
248    }
249
250    private async void gradientChart_SolutionAdded(object sender, EventArgs<IRegressionSolution> e) {
251      var solution = e.Value;
252      foreach (var chart in gradientCharts.Values) {
253        if (sender == chart) continue;
254        await chart.AddSolutionAsync(solution);
255      }
256    }
257
258    private async void gradientChart_SolutionRemoved(object sender, EventArgs<IRegressionSolution> e) {
259      var solution = e.Value;
260      foreach (var chart in gradientCharts.Values) {
261        if (sender == chart) continue;
262        await chart.RemoveSolutionAsync(solution);
263      }
264    }
265
266    private async void variableListView_ItemChecked(object sender, ItemCheckedEventArgs e) {
267      var item = e.Item;
268      var variable = item.Text;
269      var gradientChart = gradientCharts[variable];
270      var densityChart = densityCharts[variable];
271      var chartsPanel = groupingPanels[variable];
272      var tl = gradientChartTableLayout;
273
274      tl.SuspendLayout();
275      if (item.Checked) {
276        tl.Controls.Add(chartsPanel);
277        await gradientChart.RecalculateAsync();
278        UpdateDensityChart(densityChart, variable);
279      } else {
280        tl.Controls.Remove(chartsPanel);
281      }
282
283      if (tl.Controls.Count > 0) {
284        SetupYAxis();
285        ReOrderControls();
286        SetStyles();
287      }
288      tl.ResumeLayout();
289    }
290
291    private void automaticYAxisCheckBox_CheckedChanged(object sender, EventArgs e) {
292      limitView.ReadOnly = automaticYAxisCheckBox.Checked;
293      SetupYAxis();
294    }
295
296    private void limit_ValueChanged(object sender, EventArgs e) {
297      if (automaticYAxisCheckBox.Checked)
298        return;
299      SetupYAxis();
300    }
301
302    private void densityComboBox_SelectedIndexChanged(object sender, EventArgs e) {
303      int si = densityComboBox.SelectedIndex;
304      if (si == 0) {
305        foreach (var densityChart in densityCharts.Values)
306          densityChart.Visible = false;
307      } else {
308        var indices = GetDensityIndices(si).ToList();
309
310        foreach (var entry in densityCharts) {
311          var variableName = entry.Key;
312          var densityChart = entry.Value;
313          if (!VisibleVariables.Contains(variableName))
314            continue;
315          UpdateDensityChart(densityChart, variableName, indices);
316
317        }
318      }
319    }
320    private IEnumerable<int> GetDensityIndices(int selectedIndex) {
321      var problemData = Content.ProblemData;
322      return
323        selectedIndex == 1 ? problemData.TrainingIndices :
324        selectedIndex == 2 ? problemData.TestIndices :
325        problemData.AllIndices;
326    }
327    private void UpdateDensityChart(DensityChart densityChart, string variable, IList<int> indices = null) {
328      if (densityComboBox.SelectedIndex == 0)
329        return;
330      if (indices == null) {
331        indices = GetDensityIndices(densityComboBox.SelectedIndex).ToList();
332      }
333      var data = Content.ProblemData.Dataset.GetDoubleValues(variable, indices).ToList();
334      var gradientChart = gradientCharts[variable];
335      var min = gradientChart.FixedXAxisMin;
336      var max = gradientChart.FixedXAxisMax;
337      var buckets = gradientChart.DrawingSteps;
338      if (min.HasValue && max.HasValue) {
339        densityChart.UpdateChart(data, min.Value, max.Value, buckets);
340        densityChart.Width = gradientChart.Width;
341
342        var gcPlotPosition = gradientChart.InnerPlotPosition;
343        densityChart.Left = (int)(gcPlotPosition.X / 100.0 * gradientChart.Width);
344        densityChart.Width = (int)(gcPlotPosition.Width / 100.0 * gradientChart.Width);
345
346        densityChart.Visible = true;
347      }
348    }
349
350    private void columnsTextBox_Validating(object sender, System.ComponentModel.CancelEventArgs e) {
351      int columns;
352      if (!int.TryParse(columnsTextBox.Text, out columns)) {
353        e.Cancel = true;
354        columnsTextBox.Select();
355        var textBox = (TextBox)sender;
356        errorProvider.SetError(columnsTextBox, "Columns number must be a positive integer.");
357        errorProvider.SetIconPadding(textBox, -20);
358      }
359    }
360
361    private void columnsTextBox_Validated(object sender, EventArgs e) {
362      errorProvider.SetError(columnsTextBox, "");
363      MaxColumns = int.Parse(columnsTextBox.Text);
364      int columns = Math.Min(VisibleVariables.Count(), MaxColumns);
365      if (columns > 0) {
366        var tl = gradientChartTableLayout;
367        MaxColumns = columns;
368        tl.SuspendLayout();
369        ReOrderControls();
370        SetStyles();
371        tl.ResumeLayout(true);
372      }
373    }
374
375    private void solution_ModelChanged(object sender, EventArgs e) {
376      foreach (var chart in gradientCharts.Values) {
377        chart.RecalculateAsync();
378      }
379    }
380  }
381}
Note: See TracBrowser for help on using the repository browser.