Index: trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.cs	(revision 14772)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.cs	(revision 14826)
@@ -21,4 +21,5 @@
 
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Drawing;
@@ -35,5 +36,5 @@
   [Content(typeof(IRegressionSolution))]
   public partial class RegressionSolutionTargetResponseGradientView : DataAnalysisSolutionEvaluationView {
-    private readonly Dictionary<string, GradientChart> gradientCharts;
+    private readonly Dictionary<string, IGradientChart> gradientCharts;
     private readonly Dictionary<string, DensityChart> densityCharts;
     private readonly Dictionary<string, Panel> groupingPanels;
@@ -49,5 +50,5 @@
       }
     }
-    private IEnumerable<GradientChart> VisibleGradientCharts {
+    private IEnumerable<IGradientChart> VisibleGradientCharts {
       get { return VisibleVariables.Select(v => gradientCharts[v]); }
     }
@@ -61,5 +62,5 @@
     public RegressionSolutionTargetResponseGradientView() {
       InitializeComponent();
-      gradientCharts = new Dictionary<string, GradientChart>();
+      gradientCharts = new Dictionary<string, IGradientChart>();
       densityCharts = new Dictionary<string, DensityChart>();
       groupingPanels = new Dictionary<string, Panel>();
@@ -120,9 +121,18 @@
 
 
-      var variableValues = allowedInputVariables.Select(x => new List<double> { problemData.Dataset.GetDoubleValues(x, problemData.TrainingIndices).Median() });
+      var doubleVariables = allowedInputVariables.Where(problemData.Dataset.VariableHasType<double>);
+      var doubleVariableValues = (IEnumerable<IList>)doubleVariables.Select(x => new List<double> { problemData.Dataset.GetDoubleValues(x, problemData.TrainingIndices).Median() });
+
+      var factorVariables = allowedInputVariables.Where(problemData.Dataset.VariableHasType<string>);
+      var factorVariableValues = (IEnumerable<IList>)factorVariables.Select(x => new List<string> {
+        problemData.Dataset.GetStringValues(x, problemData.TrainingIndices)
+        .GroupBy(val => val).OrderByDescending(g => g.Count()).First().Key // most frequent value
+      });
+
       if (sharedFixedVariables != null)
         sharedFixedVariables.ItemChanged -= SharedFixedVariables_ItemChanged;
-      sharedFixedVariables = new ModifiableDataset(allowedInputVariables, variableValues);
-      // ItemChanged eventhandler is registered later, after creating the gradient charts
+
+      sharedFixedVariables = new ModifiableDataset(doubleVariables.Concat(factorVariables), doubleVariableValues.Concat(factorVariableValues));
+
 
       // create controls
@@ -130,5 +140,5 @@
       densityCharts.Clear();
       groupingPanels.Clear();
-      foreach (var variableName in allowedInputVariables) {
+      foreach (var variableName in doubleVariables) {
         var gradientChart = CreateGradientChart(variableName, sharedFixedVariables);
         gradientCharts.Add(variableName, gradientChart);
@@ -158,5 +168,5 @@
         // Initially, the inner plot areas are not initialized for hidden charts (scollpanel, ...)
         // This event handler listens for the paint event once (where everything is already initialized) to do some manual layouting.
-        gradientChart.ChartPostPaint += OnGradientChartOnChartPostPaint;
+        gradientChart.ChartPostPaint += OnGradientChartPostPaint;
 
         var panel = new Panel() {
@@ -170,5 +180,43 @@
         groupingPanels.Add(variableName, panel);
       }
-
+      foreach (var variableName in factorVariables) {
+        var gradientChart = CreateFactorGradientChart(variableName, sharedFixedVariables);
+        gradientCharts.Add(variableName, gradientChart);
+
+        var densityChart = new DensityChart() {
+          Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right,
+          Margin = Padding.Empty,
+          Height = 12,
+          Visible = false,
+          Top = (int)(gradientChart.Height * 0.1),
+        };
+        densityCharts.Add(variableName, densityChart);
+        gradientChart.ZoomChanged += (o, e) => {
+          var gradient = (FactorGradientChart)o;
+          var density = densityCharts[gradient.FreeVariable];
+          density.Visible = densityComboBox.SelectedIndex != 0 && !gradient.IsZoomed;
+          if (density.Visible)
+            UpdateDensityChart(density, gradient.FreeVariable);
+        };
+        gradientChart.SizeChanged += (o, e) => {
+          var gradient = (FactorGradientChart)o;
+          var density = densityCharts[gradient.FreeVariable];
+          density.Top = (int)(gradient.Height * 0.1);
+        };
+
+        // Initially, the inner plot areas are not initialized for hidden charts (scollpanel, ...)
+        // This event handler listens for the paint event once (where everything is already initialized) to do some manual layouting.
+        gradientChart.ChartPostPaint += OnFactorGradientChartPostPaint;
+
+        var panel = new Panel() {
+          Dock = DockStyle.Fill,
+          Margin = Padding.Empty,
+          BackColor = Color.White
+        };
+
+        panel.Controls.Add(densityChart);
+        panel.Controls.Add(gradientChart);
+        groupingPanels.Add(variableName, panel);
+      }
       // update variable list
       variableListView.ItemChecked -= variableListView_ItemChecked;
@@ -196,5 +244,6 @@
     }
 
-    private void OnGradientChartOnChartPostPaint(object o, EventArgs e) {
+
+    private void OnGradientChartPostPaint(object o, EventArgs e) {
       var gradient = (GradientChart)o;
       var density = densityCharts[gradient.FreeVariable];
@@ -209,5 +258,21 @@
       // removed after succesful layouting due to performance reasons
       if (gcPlotPosition.Width != 0)
-        gradient.ChartPostPaint -= OnGradientChartOnChartPostPaint;
+        gradient.ChartPostPaint -= OnGradientChartPostPaint;
+    }
+
+    private void OnFactorGradientChartPostPaint(object o, EventArgs e) {
+      var gradient = (FactorGradientChart)o;
+      var density = densityCharts[gradient.FreeVariable];
+
+      density.Width = gradient.Width;
+
+      var gcPlotPosition = gradient.InnerPlotPosition;
+      density.Left = (int)(gcPlotPosition.X / 100.0 * gradient.Width);
+      density.Width = (int)(gcPlotPosition.Width / 100.0 * gradient.Width);
+      gradient.UpdateTitlePosition();
+
+      // removed after succesful layouting due to performance reasons
+      if (gcPlotPosition.Width != 0)
+        gradient.ChartPostPaint -= OnFactorGradientChartPostPaint;
     }
 
@@ -215,5 +280,5 @@
       foreach (var variable in VisibleVariables) {
         var gradientChart = gradientCharts[variable];
-        await gradientChart.RecalculateAsync();
+        await gradientChart.RecalculateAsync(false, false);
       }
       gradientChartTableLayout.SuspendLayout();
@@ -224,6 +289,8 @@
       gradientChartTableLayout.Refresh();
       foreach (var variable in VisibleVariables) {
-        var densityChart = densityCharts[variable];
-        UpdateDensityChart(densityChart, variable);
+        DensityChart densityChart;
+        if (densityCharts.TryGetValue(variable, out densityChart)) {
+          UpdateDensityChart(densityChart, variable);
+        }
       }
     }
@@ -238,7 +305,9 @@
       };
       gradientChart.VariableValueChanged += async (o, e) => {
-        var recalculations = VisibleGradientCharts.Except(new[] { (GradientChart)o }).Select(async chart => {
-          await chart.RecalculateAsync(updateOnFinish: false, resetYAxis: false);
-        }).ToList();
+        var recalculations = VisibleGradientCharts
+          .Except(new[] { (IGradientChart)o })
+          .Select(async chart => {
+            await chart.RecalculateAsync(updateOnFinish: false, resetYAxis: false);
+          }).ToList();
         await Task.WhenAll(recalculations);
 
@@ -251,5 +320,29 @@
       return gradientChart;
     }
-
+    private FactorGradientChart CreateFactorGradientChart(string variableName, ModifiableDataset sharedFixedVariables) {
+      var gradientChart = new FactorGradientChart {
+        Dock = DockStyle.Fill,
+        Margin = Padding.Empty,
+        ShowLegend = false,
+        ShowCursor = true,
+        YAxisTicks = 5,
+      };
+      gradientChart.VariableValueChanged += async (o, e) => {
+        var recalculations = VisibleGradientCharts
+          .Except(new[] { (FactorGradientChart)o })
+          .Select(async chart => {
+            await chart.RecalculateAsync(updateOnFinish: false, resetYAxis: false);
+          }).ToList();
+        await Task.WhenAll(recalculations);
+
+        if (recalculations.All(t => t.IsCompleted))
+          SetupYAxis();
+      };
+      var variableValues = Content.ProblemData.Dataset.GetStringValues(variableName).Distinct().OrderBy(n => n).ToList();
+      gradientChart.Configure(new[] { Content }, sharedFixedVariables, variableName, variableValues);
+      gradientChart.SolutionAdded += gradientChart_SolutionAdded;
+      gradientChart.SolutionRemoved += gradientChart_SolutionRemoved;
+      return gradientChart;
+    }
     private void SetupYAxis() {
       double axisMin, axisMax;
@@ -345,5 +438,5 @@
       if (item.Checked) {
         tl.Controls.Add(chartsPanel);
-        await gradientChart.RecalculateAsync();
+        await gradientChart.RecalculateAsync(false, false);
       } else {
         tl.Controls.Remove(chartsPanel);
@@ -409,21 +502,39 @@
         indices = GetDensityIndices(densityComboBox.SelectedIndex).ToList();
       }
-      var data = Content.ProblemData.Dataset.GetDoubleValues(variable, indices).ToList();
-      var gradientChart = gradientCharts[variable];
-      var min = gradientChart.FixedXAxisMin;
-      var max = gradientChart.FixedXAxisMax;
-      var buckets = gradientChart.DrawingSteps;
-      if (min.HasValue && max.HasValue) {
-        densityChart.UpdateChart(data, min.Value, max.Value, buckets);
-        densityChart.Width = gradientChart.Width;
-
-        var gcPlotPosition = gradientChart.InnerPlotPosition;
-        densityChart.Left = (int)(gcPlotPosition.X / 100.0 * gradientChart.Width);
-        densityChart.Width = (int)(gcPlotPosition.Width / 100.0 * gradientChart.Width);
-
-        densityChart.Visible = true;
-      }
-
-      gradientChart.UpdateTitlePosition();
+      if (Content.ProblemData.Dataset.VariableHasType<double>(variable)) {
+        var data = Content.ProblemData.Dataset.GetDoubleValues(variable, indices).ToList();
+        var gradientChart = gradientCharts[variable] as GradientChart;
+        if (gradientChart != null) {
+          var min = gradientChart.FixedXAxisMin;
+          var max = gradientChart.FixedXAxisMax;
+          var buckets = gradientChart.DrawingSteps;
+          if (min.HasValue && max.HasValue) {
+            densityChart.UpdateChart(data, min.Value, max.Value, buckets);
+            densityChart.Width = gradientChart.Width;
+
+            var gcPlotPosition = gradientChart.InnerPlotPosition;
+            densityChart.Left = (int)(gcPlotPosition.X / 100.0 * gradientChart.Width);
+            densityChart.Width = (int)(gcPlotPosition.Width / 100.0 * gradientChart.Width);
+
+            densityChart.Visible = true;
+          }
+          gradientChart.UpdateTitlePosition();
+        }
+      } else if (Content.ProblemData.Dataset.VariableHasType<string>(variable)) {
+        var data = Content.ProblemData.Dataset.GetStringValues(variable).ToList();
+        var gradientChart = gradientCharts[variable] as FactorGradientChart;
+        if (gradientChart != null) {
+          densityChart.UpdateChart(data);
+          densityChart.Width = gradientChart.Width;
+
+          var gcPlotPosition = gradientChart.InnerPlotPosition;
+          densityChart.Left = (int)(gcPlotPosition.X / 100.0 * gradientChart.Width);
+          densityChart.Width = (int)(gcPlotPosition.Width / 100.0 * gradientChart.Width);
+
+          densityChart.Visible = true;
+
+          gradientChart.UpdateTitlePosition();
+        }
+      }
     }
 
@@ -448,5 +559,5 @@
         var densityChart = densityCharts[variable];
         // recalculate and refresh
-        await gradientChart.RecalculateAsync();
+        await gradientChart.RecalculateAsync(false, false);
         gradientChart.Refresh();
         UpdateDensityChart(densityChart, variable);
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.Designer.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.Designer.cs	(revision 14772)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.Designer.cs	(revision 14826)
@@ -47,6 +47,8 @@
       this.dataPartitionComboBox = new System.Windows.Forms.ComboBox();
       this.dataPartitionLabel = new System.Windows.Forms.Label();
-      this.replacementLabel = new System.Windows.Forms.Label();
+      this.numericVarReplacementLabel = new System.Windows.Forms.Label();
       this.replacementComboBox = new System.Windows.Forms.ComboBox();
+      this.factorVarReplacementLabel = new System.Windows.Forms.Label();
+      this.factorVarReplComboBox = new System.Windows.Forms.ComboBox();
       this.SuspendLayout();
       // 
@@ -58,9 +60,9 @@
       this.variableImactsArrayView.Caption = "StringConvertibleArray View";
       this.variableImactsArrayView.Content = null;
-      this.variableImactsArrayView.Location = new System.Drawing.Point(3, 59);
+      this.variableImactsArrayView.Location = new System.Drawing.Point(3, 84);
       this.variableImactsArrayView.Name = "variableImactsArrayView";
       this.variableImactsArrayView.ReadOnly = true;
-      this.variableImactsArrayView.Size = new System.Drawing.Size(304, 223);
-      this.variableImactsArrayView.TabIndex = 4;
+      this.variableImactsArrayView.Size = new System.Drawing.Size(363, 278);
+      this.variableImactsArrayView.TabIndex = 2;
       // 
       // dataPartitionComboBox
@@ -71,5 +73,5 @@
             HeuristicLab.Problems.DataAnalysis.RegressionSolutionVariableImpactsCalculator.DataPartitionEnum.Test,
             HeuristicLab.Problems.DataAnalysis.RegressionSolutionVariableImpactsCalculator.DataPartitionEnum.All});
-      this.dataPartitionComboBox.Location = new System.Drawing.Point(82, 3);
+      this.dataPartitionComboBox.Location = new System.Drawing.Point(197, 3);
       this.dataPartitionComboBox.Name = "dataPartitionComboBox";
       this.dataPartitionComboBox.Size = new System.Drawing.Size(121, 21);
@@ -86,12 +88,12 @@
       this.dataPartitionLabel.Text = "Data partition:";
       // 
-      // replacementLabel
+      // numericVarReplacementLabel
       // 
-      this.replacementLabel.AutoSize = true;
-      this.replacementLabel.Location = new System.Drawing.Point(3, 35);
-      this.replacementLabel.Name = "replacementLabel";
-      this.replacementLabel.Size = new System.Drawing.Size(73, 13);
-      this.replacementLabel.TabIndex = 2;
-      this.replacementLabel.Text = "Replacement:";
+      this.numericVarReplacementLabel.AutoSize = true;
+      this.numericVarReplacementLabel.Location = new System.Drawing.Point(3, 33);
+      this.numericVarReplacementLabel.Name = "numericVarReplacementLabel";
+      this.numericVarReplacementLabel.Size = new System.Drawing.Size(173, 13);
+      this.numericVarReplacementLabel.TabIndex = 2;
+      this.numericVarReplacementLabel.Text = "Replacement for numeric variables:";
       // 
       // replacementComboBox
@@ -103,5 +105,5 @@
             HeuristicLab.Problems.DataAnalysis.RegressionSolutionVariableImpactsCalculator.ReplacementMethodEnum.Noise,
             HeuristicLab.Problems.DataAnalysis.RegressionSolutionVariableImpactsCalculator.ReplacementMethodEnum.Shuffle});
-      this.replacementComboBox.Location = new System.Drawing.Point(82, 32);
+      this.replacementComboBox.Location = new System.Drawing.Point(197, 30);
       this.replacementComboBox.Name = "replacementComboBox";
       this.replacementComboBox.Size = new System.Drawing.Size(121, 21);
@@ -109,15 +111,39 @@
       this.replacementComboBox.SelectedIndexChanged += new System.EventHandler(this.replacementComboBox_SelectedIndexChanged);
       // 
+      // factorVarReplacementLabel
+      // 
+      this.factorVarReplacementLabel.AutoSize = true;
+      this.factorVarReplacementLabel.Location = new System.Drawing.Point(3, 60);
+      this.factorVarReplacementLabel.Name = "factorVarReplacementLabel";
+      this.factorVarReplacementLabel.Size = new System.Drawing.Size(188, 13);
+      this.factorVarReplacementLabel.TabIndex = 0;
+      this.factorVarReplacementLabel.Text = "Replacement for categorical variables:";
+      // 
+      // factorVarReplComboBox
+      // 
+      this.factorVarReplComboBox.FormattingEnabled = true;
+      this.factorVarReplComboBox.Items.AddRange(new object[] {
+            HeuristicLab.Problems.DataAnalysis.RegressionSolutionVariableImpactsCalculator.FactorReplacementMethodEnum.Best,
+            HeuristicLab.Problems.DataAnalysis.RegressionSolutionVariableImpactsCalculator.FactorReplacementMethodEnum.Mode,
+            HeuristicLab.Problems.DataAnalysis.RegressionSolutionVariableImpactsCalculator.FactorReplacementMethodEnum.Shuffle});
+      this.factorVarReplComboBox.Location = new System.Drawing.Point(197, 57);
+      this.factorVarReplComboBox.Name = "factorVarReplComboBox";
+      this.factorVarReplComboBox.Size = new System.Drawing.Size(121, 21);
+      this.factorVarReplComboBox.TabIndex = 1;
+      this.factorVarReplComboBox.SelectedIndexChanged += new System.EventHandler(this.replacementComboBox_SelectedIndexChanged);
+      // 
       // RegressionSolutionVariableImpactsView
       // 
       this.AllowDrop = true;
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
+      this.Controls.Add(this.factorVarReplComboBox);
+      this.Controls.Add(this.factorVarReplacementLabel);
       this.Controls.Add(this.replacementComboBox);
-      this.Controls.Add(this.replacementLabel);
+      this.Controls.Add(this.numericVarReplacementLabel);
       this.Controls.Add(this.dataPartitionLabel);
       this.Controls.Add(this.dataPartitionComboBox);
       this.Controls.Add(this.variableImactsArrayView);
       this.Name = "RegressionSolutionVariableImpactsView";
-      this.Size = new System.Drawing.Size(310, 285);
+      this.Size = new System.Drawing.Size(369, 365);
       this.ResumeLayout(false);
       this.PerformLayout();
@@ -130,6 +156,8 @@
     private System.Windows.Forms.ComboBox dataPartitionComboBox;
     private System.Windows.Forms.Label dataPartitionLabel;
-    private System.Windows.Forms.Label replacementLabel;
+    private System.Windows.Forms.Label numericVarReplacementLabel;
     private System.Windows.Forms.ComboBox replacementComboBox;
+    private System.Windows.Forms.Label factorVarReplacementLabel;
+    private System.Windows.Forms.ComboBox factorVarReplComboBox;
   }
 }
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.cs	(revision 14772)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.cs	(revision 14826)
@@ -43,4 +43,5 @@
       this.dataPartitionComboBox.SelectedIndex = 0;
       this.replacementComboBox.SelectedIndex = 0;
+      this.factorVarReplComboBox.SelectedIndex = 0;
     }
 
@@ -76,9 +77,15 @@
 
     private void UpdateVariableImpacts() {
-      if (Content == null || replacementComboBox.SelectedIndex < 0 || dataPartitionComboBox.SelectedIndex < 0) return;
+      if (Content == null || replacementComboBox.SelectedIndex < 0
+        || factorVarReplComboBox.SelectedIndex < 0
+        || dataPartitionComboBox.SelectedIndex < 0) return;
       var mainForm = (MainForm.WindowsForms.MainForm)MainFormManager.MainForm;
       variableImactsArrayView.Caption = Content.Name + " Variable Impacts";
       var replMethod =
-         (RegressionSolutionVariableImpactsCalculator.ReplacementMethodEnum)replacementComboBox.Items[replacementComboBox.SelectedIndex];
+         (RegressionSolutionVariableImpactsCalculator.ReplacementMethodEnum)
+           replacementComboBox.Items[replacementComboBox.SelectedIndex];
+      var factorReplMethod =
+        (RegressionSolutionVariableImpactsCalculator.FactorReplacementMethodEnum)
+          factorVarReplComboBox.Items[factorVarReplComboBox.SelectedIndex];
       var dataPartition =
         (RegressionSolutionVariableImpactsCalculator.DataPartitionEnum)dataPartitionComboBox.SelectedItem;
@@ -88,5 +95,5 @@
           mainForm.AddOperationProgressToView(this, "Calculating variable impacts for " + Content.Name);
 
-          var impacts = RegressionSolutionVariableImpactsCalculator.CalculateImpacts(Content, dataPartition, replMethod);
+          var impacts = RegressionSolutionVariableImpactsCalculator.CalculateImpacts(Content, dataPartition, replMethod, factorReplMethod);
           var impactArray = new DoubleArray(impacts.Select(i => i.Item2).ToArray());
           impactArray.ElementNames = impacts.Select(i => i.Item1);
