Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2972_PDPRowSelect/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionPartialDependencePlotView.cs @ 16445

Last change on this file since 16445 was 16445, checked in by pfleck, 5 years ago

#2972 Implemented 1st version (UI is subject to change)

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