Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionPartialDependencePlotView.cs @ 18079

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

#3134 Added RunCollectionPartialDependencePlotView and changed UpdateAllSeriesDataAsync method in PartialDependencePlot to await all updates from the given enumeration

File size: 27.2 KB
RevLine 
[13817]1#region License Information
2/* HeuristicLab
[17180]3 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[13817]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
[13828]22using System;
[14826]23using System.Collections;
[13817]24using System.Collections.Generic;
[13850]25using System.Drawing;
[14267]26using System.Globalization;
[13808]27using System.Linq;
[13843]28using System.Threading.Tasks;
[13808]29using System.Windows.Forms;
30using HeuristicLab.Common;
31using HeuristicLab.MainForm;
[13843]32using HeuristicLab.Visualization.ChartControlsExtensions;
[13808]33
34namespace HeuristicLab.Problems.DataAnalysis.Views {
[14852]35  [View("Partial Dependence Plots")]
[13808]36  [Content(typeof(IRegressionSolution))]
[14852]37  public partial class RegressionSolutionPartialDependencePlotView : DataAnalysisSolutionEvaluationView {
38    private readonly Dictionary<string, IPartialDependencePlot> partialDependencePlots;
[13850]39    private readonly Dictionary<string, DensityChart> densityCharts;
40    private readonly Dictionary<string, Panel> groupingPanels;
[14267]41    private ModifiableDataset sharedFixedVariables;
[13850]42
[13831]43    private const int Points = 200;
[13995]44    private int MaxColumns = 4;
[13808]45
[13850]46    private IEnumerable<string> VisibleVariables {
47      get {
48        foreach (ListViewItem item in variableListView.CheckedItems)
49          yield return item.Text;
50      }
[13843]51    }
[14852]52    private IEnumerable<IPartialDependencePlot> VisiblePartialDependencePlots {
53      get { return VisibleVariables.Select(v => partialDependencePlots[v]); }
[13850]54    }
55    private IEnumerable<DensityChart> VisibleDensityCharts {
56      get { return VisibleVariables.Select(v => densityCharts[v]); }
57    }
58    private IEnumerable<Panel> VisibleChartsPanels {
59      get { return VisibleVariables.Select(v => groupingPanels[v]); }
60    }
[13843]61
[14852]62    public RegressionSolutionPartialDependencePlotView() {
[13808]63      InitializeComponent();
[14852]64      partialDependencePlots = new Dictionary<string, IPartialDependencePlot>();
[13850]65      densityCharts = new Dictionary<string, DensityChart>();
66      groupingPanels = new Dictionary<string, Panel>();
[13846]67
68      limitView.Content = new DoubleLimit(0, 1);
[14021]69      limitView.Content.ValueChanged += limit_ValueChanged;
[13850]70
[14021]71      densityComboBox.SelectedIndex = 1; // select Training
72
[13850]73      // Avoid additional horizontal scrollbar
74      var vertScrollWidth = SystemInformation.VerticalScrollBarWidth;
75      scrollPanel.Padding = new Padding(0, 0, vertScrollWidth, 0);
76      scrollPanel.AutoScroll = true;
[13808]77    }
78
79    public new IRegressionSolution Content {
80      get { return (IRegressionSolution)base.Content; }
[13831]81      set { base.Content = value; }
[13808]82    }
83
84    protected override void RegisterContentEvents() {
85      base.RegisterContentEvents();
[13995]86      Content.ModelChanged += solution_ModelChanged;
[13808]87    }
88
89    protected override void DeregisterContentEvents() {
[13995]90      Content.ModelChanged -= solution_ModelChanged;
[13808]91      base.DeregisterContentEvents();
92    }
93
94    protected override void OnContentChanged() {
95      base.OnContentChanged();
[13817]96      if (Content == null) return;
[13831]97      var problemData = Content.ProblemData;
[13846]98
[16519]99      if (sharedFixedVariables != null) {
100        sharedFixedVariables.ItemChanged -= SharedFixedVariables_ItemChanged;
101        sharedFixedVariables.Reset -= SharedFixedVariables_Reset;
102      }
103
[13846]104      // Init Y-axis range
105      double min = double.MaxValue, max = double.MinValue;
106      var trainingTarget = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TrainingIndices);
107      foreach (var t in trainingTarget) {
108        if (t < min) min = t;
109        if (t > max) max = t;
110      }
111      double range = max - min;
112      const double scale = 1.0 / 3.0;
113      double axisMin, axisMax, axisInterval;
114      ChartUtil.CalculateAxisInterval(min - scale * range, max + scale * range, 5, out axisMin, out axisMax, out axisInterval);
115      automaticYAxisCheckBox.Checked = false;
116      limitView.ReadOnly = false;
117      limitView.Content.Lower = axisMin;
118      limitView.Content.Upper = axisMax;
119
[14464]120      // create dataset of problemData input variables and model input variables
[17938]121      // necessary workaround to have the variables in the occurring order
[14464]122      var inputvariables =
123        new HashSet<string>(Content.ProblemData.AllowedInputVariables.Union(Content.Model.VariablesUsedForPrediction));
124      var allowedInputVariables =
125        Content.ProblemData.Dataset.VariableNames.Where(v => inputvariables.Contains(v)).ToList();
126
[14826]127      var doubleVariables = allowedInputVariables.Where(problemData.Dataset.VariableHasType<double>);
[16519]128      var doubleVariableValues = (IEnumerable<IList>)doubleVariables.Select(x => new List<double> {
129        problemData.Dataset.GetDoubleValue(x, 0)
130      });
[14826]131
132      var factorVariables = allowedInputVariables.Where(problemData.Dataset.VariableHasType<string>);
133      var factorVariableValues = (IEnumerable<IList>)factorVariables.Select(x => new List<string> {
[16519]134        problemData.Dataset.GetStringValue(x, 0)
[14826]135      });
136
137      sharedFixedVariables = new ModifiableDataset(doubleVariables.Concat(factorVariables), doubleVariableValues.Concat(factorVariableValues));
[16519]138      variableValuesModeComboBox.SelectedItem = "Median"; // triggers UpdateVariableValue and changes shardFixedVariables
[14826]139
[13850]140      // create controls
[14852]141      partialDependencePlots.Clear();
[13850]142      densityCharts.Clear();
143      groupingPanels.Clear();
[14826]144      foreach (var variableName in doubleVariables) {
[14852]145        var plot = CreatePartialDependencePlot(variableName, sharedFixedVariables);
146        partialDependencePlots.Add(variableName, plot);
[13850]147
148        var densityChart = new DensityChart() {
149          Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right,
150          Margin = Padding.Empty,
151          Height = 12,
[14131]152          Visible = false,
[14852]153          Top = (int)(plot.Height * 0.1),
[13850]154        };
155        densityCharts.Add(variableName, densityChart);
156
[14852]157        plot.ZoomChanged += (o, e) => {
158          var pdp = (PartialDependencePlot)o;
159          var density = densityCharts[pdp.FreeVariable];
160          density.Visible = densityComboBox.SelectedIndex != 0 && !pdp.IsZoomed;
[14089]161          if (density.Visible)
[14852]162            UpdateDensityChart(density, pdp.FreeVariable);
[14089]163        };
[14852]164        plot.SizeChanged += (o, e) => {
165          var pdp = (PartialDependencePlot)o;
166          var density = densityCharts[pdp.FreeVariable];
167          density.Top = (int)(pdp.Height * 0.1);
[14131]168        };
[14089]169
[16519]170        // Initially, the inner plot areas are not initialized for hidden charts (scrollpanel, ...)
[14158]171        // This event handler listens for the paint event once (where everything is already initialized) to do some manual layouting.
[14852]172        plot.ChartPostPaint += OnPartialDependencePlotPostPaint;
[14158]173
[13850]174        var panel = new Panel() {
[13820]175          Dock = DockStyle.Fill,
176          Margin = Padding.Empty,
[13850]177          BackColor = Color.White
[13820]178        };
[13843]179
[13850]180        panel.Controls.Add(densityChart);
[14852]181        panel.Controls.Add(plot);
[13850]182        groupingPanels.Add(variableName, panel);
[13808]183      }
[14826]184      foreach (var variableName in factorVariables) {
[14852]185        var plot = CreateFactorPartialDependencePlot(variableName, sharedFixedVariables);
186        partialDependencePlots.Add(variableName, plot);
[13850]187
[14826]188        var densityChart = new DensityChart() {
189          Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right,
190          Margin = Padding.Empty,
191          Height = 12,
192          Visible = false,
[14852]193          Top = (int)(plot.Height * 0.1),
[14826]194        };
195        densityCharts.Add(variableName, densityChart);
[14852]196        plot.ZoomChanged += (o, e) => {
197          var pdp = (FactorPartialDependencePlot)o;
198          var density = densityCharts[pdp.FreeVariable];
199          density.Visible = densityComboBox.SelectedIndex != 0 && !pdp.IsZoomed;
[14826]200          if (density.Visible)
[14852]201            UpdateDensityChart(density, pdp.FreeVariable);
[14826]202        };
[14852]203        plot.SizeChanged += (o, e) => {
204          var pdp = (FactorPartialDependencePlot)o;
205          var density = densityCharts[pdp.FreeVariable];
206          density.Top = (int)(pdp.Height * 0.1);
[14826]207        };
208
[16519]209        // Initially, the inner plot areas are not initialized for hidden charts (scrollpanel, ...)
[14826]210        // This event handler listens for the paint event once (where everything is already initialized) to do some manual layouting.
[14852]211        plot.ChartPostPaint += OnFactorPartialDependencePlotPostPaint;
[14826]212
213        var panel = new Panel() {
214          Dock = DockStyle.Fill,
215          Margin = Padding.Empty,
216          BackColor = Color.White
217        };
218
219        panel.Controls.Add(densityChart);
[14852]220        panel.Controls.Add(plot);
[14826]221        groupingPanels.Add(variableName, panel);
222      }
[13828]223      // update variable list
[14021]224      variableListView.ItemChecked -= variableListView_ItemChecked;
[13828]225      variableListView.Items.Clear();
[13845]226      foreach (var variable in allowedInputVariables)
227        variableListView.Items.Add(key: variable, text: variable, imageIndex: 0);
228
[13948]229      foreach (var variable in Content.Model.VariablesUsedForPrediction)
[13845]230        variableListView.Items[variable].Checked = true;
[14021]231      variableListView.ItemChecked += variableListView_ItemChecked;
232
[14267]233      sharedFixedVariables.ItemChanged += SharedFixedVariables_ItemChanged;
[16519]234      sharedFixedVariables.Reset += SharedFixedVariables_Reset;
[14267]235
[16519]236      rowNrNumericUpDown.Maximum = Content.ProblemData.Dataset.Rows - 1;
237
[14021]238      RecalculateAndRelayoutCharts();
[13808]239    }
[14021]240
[18051]241    public async Task AddSolution(IRegressionSolution solution) {
242      foreach (var chart in partialDependencePlots.Values) {
243        await chart.AddSolutionAsync(solution);
244      }
245    }
246
[14267]247    private void SharedFixedVariables_ItemChanged(object sender, EventArgs<int, int> e) {
[16519]248      SharedFixedVariablesChanged();
249    }
250    private void SharedFixedVariables_Reset(object sender, EventArgs e) {
251      SharedFixedVariablesChanged();
252    }
253    private void SharedFixedVariablesChanged() {
254      if (!setVariableValues) // set mode to "nothing" if change was not initiated from a "mode change"
255        variableValuesModeComboBox.SelectedIndex = -1;
256
[14267]257      double yValue = Content.Model.GetEstimatedValues(sharedFixedVariables, new[] { 0 }).Single();
258      string title = Content.ProblemData.TargetVariable + ": " + yValue.ToString("G5", CultureInfo.CurrentCulture);
[14852]259      foreach (var chart in partialDependencePlots.Values) {
[14267]260        if (!string.IsNullOrEmpty(chart.YAxisTitle)) { // only show title for first column in grid
261          chart.YAxisTitle = title;
262        }
263      }
264    }
265
[14852]266    private void OnPartialDependencePlotPostPaint(object o, EventArgs e) {
267      var plot = (PartialDependencePlot)o;
268      var density = densityCharts[plot.FreeVariable];
[14158]269
[14852]270      density.Width = plot.Width;
[14158]271
[14852]272      var gcPlotPosition = plot.InnerPlotPosition;
273      density.Left = (int)(gcPlotPosition.X / 100.0 * plot.Width);
274      density.Width = (int)(gcPlotPosition.Width / 100.0 * plot.Width);
275      plot.UpdateTitlePosition();
[14158]276
277      // removed after succesful layouting due to performance reasons
278      if (gcPlotPosition.Width != 0)
[14852]279        plot.ChartPostPaint -= OnPartialDependencePlotPostPaint;
[14158]280    }
281
[14852]282    private void OnFactorPartialDependencePlotPostPaint(object o, EventArgs e) {
283      var plot = (FactorPartialDependencePlot)o;
284      var density = densityCharts[plot.FreeVariable];
[14826]285
[14852]286      density.Width = plot.Width;
[14826]287
[14852]288      var gcPlotPosition = plot.InnerPlotPosition;
289      density.Left = (int)(gcPlotPosition.X / 100.0 * plot.Width);
290      density.Width = (int)(gcPlotPosition.Width / 100.0 * plot.Width);
291      plot.UpdateTitlePosition();
[14826]292
293      // removed after succesful layouting due to performance reasons
294      if (gcPlotPosition.Width != 0)
[14852]295        plot.ChartPostPaint -= OnFactorPartialDependencePlotPostPaint;
[14826]296    }
297
[14021]298    private async void RecalculateAndRelayoutCharts() {
299      foreach (var variable in VisibleVariables) {
[14852]300        var plot = partialDependencePlots[variable];
301        await plot.RecalculateAsync(false, false);
[14021]302      }
[14852]303      partialDependencePlotTableLayout.SuspendLayout();
[14021]304      SetupYAxis();
305      ReOrderControls();
306      SetStyles();
[14852]307      partialDependencePlotTableLayout.ResumeLayout();
308      partialDependencePlotTableLayout.Refresh();
[14021]309      foreach (var variable in VisibleVariables) {
[14826]310        DensityChart densityChart;
311        if (densityCharts.TryGetValue(variable, out densityChart)) {
312          UpdateDensityChart(densityChart, variable);
313        }
[14021]314      }
315    }
[14852]316    private PartialDependencePlot CreatePartialDependencePlot(string variableName, ModifiableDataset sharedFixedVariables) {
317      var plot = new PartialDependencePlot {
[13850]318        Dock = DockStyle.Fill,
319        Margin = Padding.Empty,
320        ShowLegend = false,
321        ShowCursor = true,
[13855]322        ShowConfigButton = false,
[13850]323        YAxisTicks = 5,
324      };
[14852]325      plot.VariableValueChanged += async (o, e) => {
326        var recalculations = VisiblePartialDependencePlots
327          .Except(new[] { (IPartialDependencePlot)o })
[14826]328          .Select(async chart => {
329            await chart.RecalculateAsync(updateOnFinish: false, resetYAxis: false);
330          }).ToList();
[13850]331        await Task.WhenAll(recalculations);
[13843]332
[13850]333        if (recalculations.All(t => t.IsCompleted))
334          SetupYAxis();
335      };
[14852]336      plot.Configure(new[] { Content }, sharedFixedVariables, variableName, Points);
337      plot.SolutionAdded += partialDependencePlot_SolutionAdded;
338      plot.SolutionRemoved += partialDependencePlot_SolutionRemoved;
339      return plot;
[13850]340    }
[14852]341    private FactorPartialDependencePlot CreateFactorPartialDependencePlot(string variableName, ModifiableDataset sharedFixedVariables) {
342      var plot = new FactorPartialDependencePlot {
[14826]343        Dock = DockStyle.Fill,
344        Margin = Padding.Empty,
345        ShowLegend = false,
346        ShowCursor = true,
347        YAxisTicks = 5,
348      };
[14852]349      plot.VariableValueChanged += async (o, e) => {
350        var recalculations = VisiblePartialDependencePlots
351          .Except(new[] { (FactorPartialDependencePlot)o })
[14826]352          .Select(async chart => {
353            await chart.RecalculateAsync(updateOnFinish: false, resetYAxis: false);
354          }).ToList();
355        await Task.WhenAll(recalculations);
[13850]356
[14826]357        if (recalculations.All(t => t.IsCompleted))
358          SetupYAxis();
359      };
360      var variableValues = Content.ProblemData.Dataset.GetStringValues(variableName).Distinct().OrderBy(n => n).ToList();
[14852]361      plot.Configure(new[] { Content }, sharedFixedVariables, variableName, variableValues);
362      plot.SolutionAdded += partialDependencePlot_SolutionAdded;
363      plot.SolutionRemoved += partialDependencePlot_SolutionRemoved;
364      return plot;
[14826]365    }
[13846]366    private void SetupYAxis() {
367      double axisMin, axisMax;
368      if (automaticYAxisCheckBox.Checked) {
369        double min = double.MaxValue, max = double.MinValue;
[14852]370        foreach (var chart in VisiblePartialDependencePlots) {
[13846]371          if (chart.YMin < min) min = chart.YMin;
372          if (chart.YMax > max) max = chart.YMax;
373        }
374
375        double axisInterval;
376        ChartUtil.CalculateAxisInterval(min, max, 5, out axisMin, out axisMax, out axisInterval);
377      } else {
378        axisMin = limitView.Content.Lower;
379        axisMax = limitView.Content.Upper;
[13843]380      }
381
[14852]382      foreach (var chart in VisiblePartialDependencePlots) {
[13843]383        chart.FixedYAxisMin = axisMin;
384        chart.FixedYAxisMax = axisMax;
385      }
386    }
387
[13850]388    // reorder chart controls so that they always appear in the same order as in the list view
389    // the table layout containing the controls should be suspended before calling this method
390    private void ReOrderControls() {
[14852]391      var tl = partialDependencePlotTableLayout;
[13828]392      tl.Controls.Clear();
[13850]393      int row = 0, column = 0;
[14267]394      double yValue = Content.Model.GetEstimatedValues(sharedFixedVariables, new[] { 0 }).Single();
395      string title = Content.ProblemData.TargetVariable + ": " + yValue.ToString("G5", CultureInfo.CurrentCulture);
396
[14014]397      foreach (var v in VisibleVariables) {
398        var chartsPanel = groupingPanels[v];
[13850]399        tl.Controls.Add(chartsPanel, column, row);
[14014]400
[14852]401        var chart = partialDependencePlots[v];
[14267]402        chart.YAxisTitle = column == 0 ? title : string.Empty;
[13850]403        column++;
[14014]404
[13850]405        if (column == MaxColumns) {
406          row++;
407          column = 0;
408        }
409      }
[13828]410    }
[13808]411
[13995]412    private void SetStyles() {
[14852]413      var tl = partialDependencePlotTableLayout;
[13995]414      tl.RowStyles.Clear();
415      tl.ColumnStyles.Clear();
416      int numVariables = VisibleVariables.Count();
417      if (numVariables == 0)
418        return;
419
420      // set column styles
421      tl.ColumnCount = Math.Min(numVariables, MaxColumns);
422      for (int c = 0; c < tl.ColumnCount; c++)
423        tl.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100.0f / tl.ColumnCount));
424
425      // set row styles
426      tl.RowCount = (int)Math.Ceiling((double)numVariables / tl.ColumnCount);
427      var columnWidth = tl.Width / tl.ColumnCount; // assume all columns have the same width
428      var rowHeight = (int)(0.8 * columnWidth);
429      for (int r = 0; r < tl.RowCount; r++)
430        tl.RowStyles.Add(new RowStyle(SizeType.Absolute, rowHeight));
431    }
432
[14852]433    private async void partialDependencePlot_SolutionAdded(object sender, EventArgs<IRegressionSolution> e) {
[13995]434      var solution = e.Value;
[14852]435      foreach (var chart in partialDependencePlots.Values) {
[13995]436        if (sender == chart) continue;
437        await chart.AddSolutionAsync(solution);
438      }
439    }
440
[14852]441    private async void partialDependencePlot_SolutionRemoved(object sender, EventArgs<IRegressionSolution> e) {
[13995]442      var solution = e.Value;
[14852]443      foreach (var chart in partialDependencePlots.Values) {
[13995]444        if (sender == chart) continue;
445        await chart.RemoveSolutionAsync(solution);
446      }
447    }
448
[13842]449    private async void variableListView_ItemChecked(object sender, ItemCheckedEventArgs e) {
[13808]450      var item = e.Item;
451      var variable = item.Text;
[14852]452      var plot = partialDependencePlots[variable];
[13850]453      var chartsPanel = groupingPanels[variable];
[14852]454      var tl = partialDependencePlotTableLayout;
[13850]455
[13817]456      tl.SuspendLayout();
[13808]457      if (item.Checked) {
[13850]458        tl.Controls.Add(chartsPanel);
[14852]459        await plot.RecalculateAsync(false, false);
[13808]460      } else {
[13850]461        tl.Controls.Remove(chartsPanel);
[13808]462      }
[13843]463
[13850]464      if (tl.Controls.Count > 0) {
465        SetupYAxis();
466        ReOrderControls();
[13995]467        SetStyles();
[13828]468      }
[13817]469      tl.ResumeLayout();
[14021]470      tl.Refresh();
471      densityComboBox_SelectedIndexChanged(this, EventArgs.Empty);
[13808]472    }
[13846]473
474    private void automaticYAxisCheckBox_CheckedChanged(object sender, EventArgs e) {
475      limitView.ReadOnly = automaticYAxisCheckBox.Checked;
476      SetupYAxis();
[14852]477      partialDependencePlotTableLayout.Refresh();
[14021]478      densityComboBox_SelectedIndexChanged(this, EventArgs.Empty); // necessary to realign the density plots
[13846]479    }
480
481    private void limit_ValueChanged(object sender, EventArgs e) {
482      if (automaticYAxisCheckBox.Checked)
483        return;
484      SetupYAxis();
[14852]485      partialDependencePlotTableLayout.Refresh();
[14021]486      densityComboBox_SelectedIndexChanged(this, EventArgs.Empty); // necessary to realign the density plots
[13846]487    }
[13850]488
489    private void densityComboBox_SelectedIndexChanged(object sender, EventArgs e) {
[14021]490      if (Content == null)
491        return;
492
[13850]493      int si = densityComboBox.SelectedIndex;
494      if (si == 0) {
495        foreach (var densityChart in densityCharts.Values)
496          densityChart.Visible = false;
497      } else {
498        var indices = GetDensityIndices(si).ToList();
499
500        foreach (var entry in densityCharts) {
501          var variableName = entry.Key;
502          var densityChart = entry.Value;
[14852]503          if (!VisibleVariables.Contains(variableName) || partialDependencePlots[variableName].IsZoomed)
[13850]504            continue;
[14089]505
[13850]506          UpdateDensityChart(densityChart, variableName, indices);
507        }
508      }
509    }
510    private IEnumerable<int> GetDensityIndices(int selectedIndex) {
511      var problemData = Content.ProblemData;
512      return
513        selectedIndex == 1 ? problemData.TrainingIndices :
514        selectedIndex == 2 ? problemData.TestIndices :
515        problemData.AllIndices;
516    }
517    private void UpdateDensityChart(DensityChart densityChart, string variable, IList<int> indices = null) {
518      if (densityComboBox.SelectedIndex == 0)
519        return;
520      if (indices == null) {
521        indices = GetDensityIndices(densityComboBox.SelectedIndex).ToList();
522      }
[14826]523      if (Content.ProblemData.Dataset.VariableHasType<double>(variable)) {
524        var data = Content.ProblemData.Dataset.GetDoubleValues(variable, indices).ToList();
[14852]525        var plot = partialDependencePlots[variable] as PartialDependencePlot;
526        if (plot != null) {
527          var min = plot.FixedXAxisMin;
528          var max = plot.FixedXAxisMax;
529          var buckets = plot.DrawingSteps;
[14826]530          if (min.HasValue && max.HasValue) {
531            densityChart.UpdateChart(data, min.Value, max.Value, buckets);
[14852]532            densityChart.Width = plot.Width;
[13850]533
[14852]534            var gcPlotPosition = plot.InnerPlotPosition;
535            densityChart.Left = (int)(gcPlotPosition.X / 100.0 * plot.Width);
536            densityChart.Width = (int)(gcPlotPosition.Width / 100.0 * plot.Width);
[13850]537
[14826]538            densityChart.Visible = true;
539          }
[14852]540          plot.UpdateTitlePosition();
[14826]541        }
542      } else if (Content.ProblemData.Dataset.VariableHasType<string>(variable)) {
543        var data = Content.ProblemData.Dataset.GetStringValues(variable).ToList();
[14852]544        var plot = partialDependencePlots[variable] as FactorPartialDependencePlot;
545        if (plot != null) {
[14826]546          densityChart.UpdateChart(data);
[14852]547          densityChart.Width = plot.Width;
[14826]548
[14852]549          var gcPlotPosition = plot.InnerPlotPosition;
550          densityChart.Left = (int)(gcPlotPosition.X / 100.0 * plot.Width);
551          densityChart.Width = (int)(gcPlotPosition.Width / 100.0 * plot.Width);
[14826]552
553          densityChart.Visible = true;
554
[14852]555          plot.UpdateTitlePosition();
[14826]556        }
[13850]557      }
558    }
[13995]559
[14089]560    private void columnsNumericUpDown_ValueChanged(object sender, EventArgs e) {
561      MaxColumns = (int)columnsNumericUpDown.Value;
[13995]562      int columns = Math.Min(VisibleVariables.Count(), MaxColumns);
563      if (columns > 0) {
[14852]564        var tl = partialDependencePlotTableLayout;
[13995]565        MaxColumns = columns;
566        tl.SuspendLayout();
567        ReOrderControls();
568        SetStyles();
[14021]569        tl.ResumeLayout();
570        tl.Refresh();
571        densityComboBox_SelectedIndexChanged(this, EventArgs.Empty);
[13995]572      }
573    }
574
[14021]575    private async void solution_ModelChanged(object sender, EventArgs e) {
576      foreach (var variable in VisibleVariables) {
[14852]577        var pdp = partialDependencePlots[variable];
[14021]578        var densityChart = densityCharts[variable];
579        // recalculate and refresh
[14852]580        await pdp.RecalculateAsync(false, false);
581        pdp.Refresh();
[14021]582        UpdateDensityChart(densityChart, variable);
[13996]583      }
[13995]584    }
[16519]585
586    // flag that the current change is not triggered by a manual change from within a single plot
587    private bool setVariableValues = false;
588    private void variableValuesComboBox_SelectedValueChanged(object sender, EventArgs e) {
589      if (variableValuesModeComboBox.SelectedIndex == -1)
590        return; // changed to "manual" due to manual change of a variable
591      setVariableValues = true;
592      UpdateVariableValues();
593      setVariableValues = false;
594    }
595    private void rowNrNumericUpDown_ValueChanged(object sender, EventArgs e) {
596      if ((string)variableValuesModeComboBox.SelectedItem != "Row") {
597        variableValuesModeComboBox.SelectedItem = "Row"; // triggers UpdateVariableValues
598      } else {
599        setVariableValues = true;
600        UpdateVariableValues();
601        setVariableValues = false;
602      }
603    }
604    private void UpdateVariableValues() {
605      string mode = (string)variableValuesModeComboBox.SelectedItem;
606
607      var dataset = Content.ProblemData.Dataset;
608      object[] newRow;
609
610      if (mode == "Row") {
611        int rowNumber = (int)rowNrNumericUpDown.Value;
612        newRow = sharedFixedVariables.VariableNames
613          .Select<string, object>(variableName => {
614            if (dataset.DoubleVariables.Contains(variableName)) {
615              return dataset.GetDoubleValue(variableName, rowNumber);
616            } else if (dataset.StringVariables.Contains(variableName)) {
617              return dataset.GetStringValue(variableName, rowNumber);
618            } else {
619              throw new NotSupportedException("Only double and string(factor) columns are currently supported.");
620            }
621          }).ToArray();
622      } else {
623        newRow = sharedFixedVariables.VariableNames
624          .Select<string, object>(variableName => {
625            if (dataset.DoubleVariables.Contains(variableName)) {
626              var values = dataset.GetDoubleValues(variableName);
627              return
628                mode == "Mean" ? values.Average() :
629                mode == "Median" ? values.Median() :
630                mode == "Most Common" ? MostCommon(values) :
631                throw new NotSupportedException();
632            } else if (dataset.StringVariables.Contains(variableName)) {
633              var values = dataset.GetStringValues(variableName);
634              return
635                mode == "Mean" ? MostCommon(values) :
636                mode == "Median" ? MostCommon(values) :
637                mode == "Most Common" ? MostCommon(values) :
638                throw new NotSupportedException();
639            } else {
640              throw new NotSupportedException("Only double and string(factor) columns are currently supported.");
641            }
642          }).ToArray();
643      }
644
645      sharedFixedVariables.ReplaceRow(0, newRow);
646    }
647
648    private static T MostCommon<T>(IEnumerable<T> values) {
649      return values.GroupBy(x => x).OrderByDescending(g => g.Count()).Select(g => g.Key).First();
650    }
651
652    // ToolTips cannot be shown longer than 5000ms, only by using ToolTip.Show manually
653    // See: https://stackoverflow.com/questions/8225807/c-sharp-tooltip-doesnt-display-long-enough
654    private void variableValuesModeComboBox_MouseHover(object sender, EventArgs e) {
655      string tooltipText = @"Sets each variable to a specific value:
656    Row - Selects the value based on a specified row of the dataset.
657    Mean - Sets the value to the arithmetic mean of the variable.
658    Median - Sets the value to the median of the variable.
659    Most Common - Sets the value to the most common value of the variable (first if multiple).
660
661Note: For categorical values, the most common value is used when selecting Mean, Median or Most Common.";
662      toolTip.Show(tooltipText, variableValuesModeComboBox, 30000);
663      toolTip.Active = true;
664    }
665    private void variableValuesModeComboBox_MouseLeave(object sender, EventArgs e) {
666      toolTip.Active = false;
667    }
[13808]668  }
669}
Note: See TracBrowser for help on using the repository browser.