Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationEnsembleModel.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationEnsembleModel.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationEnsembleModel.cs	(revision 15280)
@@ -32,5 +32,8 @@
   [StorableClass]
   [Item("ClassificationEnsembleModel", "A classification model that contains an ensemble of multiple classification models")]
-  public class ClassificationEnsembleModel : NamedItem, IClassificationEnsembleModel {
+  public class ClassificationEnsembleModel : ClassificationModel, IClassificationEnsembleModel {
+    public override IEnumerable<string> VariablesUsedForPrediction {
+      get { return models.SelectMany(x => x.VariablesUsedForPrediction).Distinct().OrderBy(x => x); }
+    }
 
     [Storable]
@@ -49,8 +52,10 @@
     public ClassificationEnsembleModel() : this(Enumerable.Empty<IClassificationModel>()) { }
     public ClassificationEnsembleModel(IEnumerable<IClassificationModel> models)
-      : base() {
+      : base(string.Empty) {
       this.name = ItemName;
       this.description = ItemDescription;
       this.models = new List<IClassificationModel>(models);
+
+      if (this.models.Any()) this.TargetVariable = this.models.First().TargetVariable;
     }
 
@@ -59,10 +64,11 @@
     }
 
-    #region IClassificationEnsembleModel Members
     public void Add(IClassificationModel model) {
+      if (string.IsNullOrEmpty(TargetVariable)) TargetVariable = model.TargetVariable;
       models.Add(model);
     }
     public void Remove(IClassificationModel model) {
       models.Remove(model);
+      if (!models.Any()) TargetVariable = string.Empty;
     }
 
@@ -78,9 +84,6 @@
     }
 
-    #endregion
 
-    #region IClassificationModel Members
-
-    public IEnumerable<double> GetEstimatedClassValues(IDataset dataset, IEnumerable<int> rows) {
+    public override IEnumerable<double> GetEstimatedClassValues(IDataset dataset, IEnumerable<int> rows) {
       foreach (var estimatedValuesVector in GetEstimatedClassValueVectors(dataset, rows)) {
         // return the class which is most often occuring
@@ -94,8 +97,9 @@
     }
 
-    IClassificationSolution IClassificationModel.CreateClassificationSolution(IClassificationProblemData problemData) {
+    public override IClassificationSolution CreateClassificationSolution(IClassificationProblemData problemData) {
       return new ClassificationEnsembleSolution(models, new ClassificationEnsembleProblemData(problemData));
     }
-    #endregion
+
+
   }
 }
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationModel.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationModel.cs	(revision 15280)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationModel.cs	(revision 15280)
@@ -0,0 +1,68 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Collections.Generic;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Problems.DataAnalysis {
+  [StorableClass]
+  [Item("Classification Model", "Base class for all classification models.")]
+  public abstract class ClassificationModel : DataAnalysisModel, IClassificationModel {
+    [Storable]
+    private string targetVariable;
+    public string TargetVariable {
+      get { return targetVariable; }
+      protected set { targetVariable = value; }
+    }
+
+    protected ClassificationModel(bool deserializing) : base(deserializing) { }
+    protected ClassificationModel(ClassificationModel original, Cloner cloner)
+      : base(original, cloner) {
+      this.targetVariable = original.targetVariable;
+    }
+
+    protected ClassificationModel(string targetVariable)
+      : base("Classification Model") {
+      this.targetVariable = targetVariable;
+    }
+    protected ClassificationModel(string targetVariable, string name)
+      : base(name) {
+      this.targetVariable = targetVariable;
+    }
+    protected ClassificationModel(string targetVariable, string name, string description)
+      : base(name, description) {
+      this.targetVariable = targetVariable;
+    }
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      // BackwardsCompatibility3.3
+      #region Backwards compatible code, remove with 3.4
+      targetVariable = string.Empty;
+      #endregion
+    }
+
+    public abstract IEnumerable<double> GetEstimatedClassValues(IDataset dataset, IEnumerable<int> rows);
+    public abstract IClassificationSolution CreateClassificationSolution(IClassificationProblemData problemData);
+  }
+}
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationPerformanceMeasures.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationPerformanceMeasures.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationPerformanceMeasures.cs	(revision 15280)
@@ -153,4 +153,10 @@
       Add(new Result(TestF1ScoreResultName, "The F1 score of the model on the test partition.", new DoubleValue()));
       Add(new Result(TestMatthewsCorrelationResultName, "The Matthews correlation value of the model on the test partition.", new DoubleValue()));
+
+      Reset();
+    }
+
+
+    public void Reset() {
       TrainingTruePositiveRate = double.NaN;
       TrainingTrueNegativeRate = double.NaN;
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationProblem.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationProblem.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationProblem.cs	(revision 15280)
@@ -35,8 +35,5 @@
     public override IDeepCloneable Clone(Cloner cloner) { return new ClassificationProblem(this, cloner); }
 
-    public ClassificationProblem()
-      : base() {
-      ProblemData = new ClassificationProblemData();
-    }
+    public ClassificationProblem() : base(new ClassificationProblemData()) { }
   }
 }
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationProblemData.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationProblemData.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationProblemData.cs	(revision 15280)
@@ -283,4 +283,9 @@
     private void AfterDeserialization() {
       RegisterParameterEvents();
+
+      classNamesCache = new List<string>();
+      for (int i = 0; i < ClassNamesParameter.Value.Rows; i++)
+        classNamesCache.Add(ClassNamesParameter.Value[i, 0]);
+
       // BackwardsCompatibility3.4
       #region Backwards compatible code, remove with 3.5
@@ -297,4 +302,7 @@
       : base(original, cloner) {
       RegisterParameterEvents();
+      classNamesCache = new List<string>();
+      for (int i = 0; i < ClassNamesParameter.Value.Rows; i++)
+        classNamesCache.Add(ClassNamesParameter.Value[i, 0]);
     }
     public override IDeepCloneable Clone(Cloner cloner) {
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationSolutionBase.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationSolutionBase.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationSolutionBase.cs	(revision 15280)
@@ -129,4 +129,6 @@
       TestNormalizedGiniCoefficient = testNormalizedGini;
 
+      ClassificationPerformanceMeasures.Reset();
+
       trainingPerformanceCalculator.Calculate(originalTrainingClassValues, estimatedTrainingClassValues);
       if (trainingPerformanceCalculator.ErrorState == OnlineCalculatorError.None)
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/DiscriminantFunctionClassificationModel.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/DiscriminantFunctionClassificationModel.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/DiscriminantFunctionClassificationModel.cs	(revision 15280)
@@ -33,5 +33,9 @@
   [StorableClass]
   [Item("DiscriminantFunctionClassificationModel", "Represents a classification model that uses a discriminant function and classification thresholds.")]
-  public class DiscriminantFunctionClassificationModel : NamedItem, IDiscriminantFunctionClassificationModel {
+  public class DiscriminantFunctionClassificationModel : ClassificationModel, IDiscriminantFunctionClassificationModel {
+    public override IEnumerable<string> VariablesUsedForPrediction {
+      get { return model.VariablesUsedForPrediction; }
+    }
+
     [Storable]
     private IRegressionModel model;
@@ -73,7 +77,8 @@
 
     public DiscriminantFunctionClassificationModel(IRegressionModel model, IDiscriminantFunctionThresholdCalculator thresholdCalculator)
-      : base() {
+      : base(model.TargetVariable) {
       this.name = ItemName;
       this.description = ItemDescription;
+
       this.model = model;
       this.classValues = new double[0];
@@ -115,5 +120,5 @@
     }
 
-    public IEnumerable<double> GetEstimatedClassValues(IDataset dataset, IEnumerable<int> rows) {
+    public override IEnumerable<double> GetEstimatedClassValues(IDataset dataset, IEnumerable<int> rows) {
       if (!Thresholds.Any() && !ClassValues.Any()) throw new ArgumentException("No thresholds and class values were set for the current classification model.");
       foreach (var x in GetEstimatedValues(dataset, rows)) {
@@ -135,10 +140,10 @@
     #endregion
 
-    public virtual IDiscriminantFunctionClassificationSolution CreateDiscriminantFunctionClassificationSolution(IClassificationProblemData problemData) {
+    public override IClassificationSolution CreateClassificationSolution(IClassificationProblemData problemData) {
+      return CreateDiscriminantFunctionClassificationSolution(problemData);
+    }
+    public virtual IDiscriminantFunctionClassificationSolution CreateDiscriminantFunctionClassificationSolution(
+      IClassificationProblemData problemData) {
       return new DiscriminantFunctionClassificationSolution(this, new ClassificationProblemData(problemData));
-    }
-
-    public virtual IClassificationSolution CreateClassificationSolution(IClassificationProblemData problemData) {
-      return CreateDiscriminantFunctionClassificationSolution(problemData);
     }
   }
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Clustering/ClusteringProblem.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Clustering/ClusteringProblem.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Clustering/ClusteringProblem.cs	(revision 15280)
@@ -33,8 +33,5 @@
     public override IDeepCloneable Clone(Cloner cloner) { return new ClusteringProblem(this, cloner); }
 
-    public ClusteringProblem()
-      : base() {
-      ProblemData = new ClusteringProblemData();
-    }
+    public ClusteringProblem() : base(new ClusteringProblemData()) { }
   }
 }
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/ConstantModel.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/ConstantModel.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/ConstantModel.cs	(revision 15280)
@@ -31,7 +31,10 @@
   [StorableClass]
   [Item("Constant Model", "A model that always returns the same constant value regardless of the presented input data.")]
-  public class ConstantModel : NamedItem, IRegressionModel, IClassificationModel, ITimeSeriesPrognosisModel, IStringConvertibleValue {
+  public class ConstantModel : RegressionModel, IClassificationModel, ITimeSeriesPrognosisModel, IStringConvertibleValue {
+    public override IEnumerable<string> VariablesUsedForPrediction { get { return Enumerable.Empty<string>(); } }
+
+
     [Storable]
-    private double constant;
+    private readonly double constant;
     public double Constant {
       get { return constant; }
@@ -45,8 +48,9 @@
       this.constant = original.constant;
     }
+
     public override IDeepCloneable Clone(Cloner cloner) { return new ConstantModel(this, cloner); }
 
-    public ConstantModel(double constant)
-      : base() {
+    public ConstantModel(double constant, string targetVariable)
+      : base(targetVariable) {
       this.name = ItemName;
       this.description = ItemDescription;
@@ -55,5 +59,5 @@
     }
 
-    public IEnumerable<double> GetEstimatedValues(IDataset dataset, IEnumerable<int> rows) {
+    public override IEnumerable<double> GetEstimatedValues(IDataset dataset, IEnumerable<int> rows) {
       return rows.Select(row => Constant);
     }
@@ -65,5 +69,5 @@
     }
 
-    public IRegressionSolution CreateRegressionSolution(IRegressionProblemData problemData) {
+    public override IRegressionSolution CreateRegressionSolution(IRegressionProblemData problemData) {
       return new ConstantRegressionSolution(this, new RegressionProblemData(problemData));
     }
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisModel.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisModel.cs	(revision 15280)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisModel.cs	(revision 15280)
@@ -0,0 +1,41 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Collections.Generic;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Problems.DataAnalysis {
+  [StorableClass]
+  [Item("DataAnalysisModel", "Base class for data analysis models.")]
+  public abstract class DataAnalysisModel : NamedItem, IDataAnalysisModel {
+    [StorableConstructor]
+    protected DataAnalysisModel(bool deserializing) : base(deserializing) { }
+    protected DataAnalysisModel(DataAnalysisModel original, Cloner cloner)
+      : base(original, cloner) { }
+    protected DataAnalysisModel() { }
+    protected DataAnalysisModel(string name) : base(name) { }
+    protected DataAnalysisModel(string name, string description) : base(name, description) { }
+
+    public abstract IEnumerable<string> VariablesUsedForPrediction { get; }
+  }
+}
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisProblem.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisProblem.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisProblem.cs	(revision 15280)
@@ -49,7 +49,5 @@
     public T ProblemData {
       get { return ProblemDataParameter.Value; }
-      protected set {
-        ProblemDataParameter.Value = value;
-      }
+      set { ProblemDataParameter.Value = value; }
     }
     #endregion
@@ -60,13 +58,9 @@
     [StorableConstructor]
     protected DataAnalysisProblem(bool deserializing) : base(deserializing) { }
-    public DataAnalysisProblem()
-      : base() {
-      Parameters.Add(new ValueParameter<T>(ProblemDataParameterName, ProblemDataParameterDescription));
-      RegisterEventHandlers();
-    }
 
     protected DataAnalysisProblem(T problemData)
-      : this() {
-      ProblemData = problemData;
+      : base() {
+      Parameters.Add(new ValueParameter<T>(ProblemDataParameterName, ProblemDataParameterDescription, problemData));
+      RegisterEventHandlers();
     }
 
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisProblemData.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisProblemData.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisProblemData.cs	(revision 15280)
@@ -80,4 +80,7 @@
     }
 
+    public virtual IEnumerable<int> AllIndices {
+      get { return Enumerable.Range(0, Dataset.Rows); }
+    }
     public virtual IEnumerable<int> TrainingIndices {
       get {
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/ConstantRegressionModel.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/ConstantRegressionModel.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/ConstantRegressionModel.cs	(revision 15280)
@@ -32,5 +32,7 @@
   [Item("Constant Regression Model", "A model that always returns the same constant value regardless of the presented input data.")]
   [Obsolete]
-  public class ConstantRegressionModel : NamedItem, IRegressionModel, IStringConvertibleValue {
+  public class ConstantRegressionModel : RegressionModel, IStringConvertibleValue {
+    public override IEnumerable<string> VariablesUsedForPrediction { get { return Enumerable.Empty<string>(); } }
+
     [Storable]
     private double constant;
@@ -46,8 +48,9 @@
       this.constant = original.constant;
     }
+
     public override IDeepCloneable Clone(Cloner cloner) { return new ConstantRegressionModel(this, cloner); }
 
-    public ConstantRegressionModel(double constant)
-      : base() {
+    public ConstantRegressionModel(double constant, string targetVariable)
+      : base(targetVariable) {
       this.name = ItemName;
       this.description = ItemDescription;
@@ -56,10 +59,10 @@
     }
 
-    public IEnumerable<double> GetEstimatedValues(IDataset dataset, IEnumerable<int> rows) {
+    public override IEnumerable<double> GetEstimatedValues(IDataset dataset, IEnumerable<int> rows) {
       return rows.Select(row => Constant);
     }
 
-    public IRegressionSolution CreateRegressionSolution(IRegressionProblemData problemData) {
-      return new ConstantRegressionSolution(new ConstantModel(constant), new RegressionProblemData(problemData));
+    public override IRegressionSolution CreateRegressionSolution(IRegressionProblemData problemData) {
+      return new ConstantRegressionSolution(new ConstantModel(constant, TargetVariable), new RegressionProblemData(problemData));
     }
 
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionEnsembleModel.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionEnsembleModel.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionEnsembleModel.cs	(revision 15280)
@@ -20,4 +20,5 @@
 #endregion
 
+using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -32,5 +33,8 @@
   [StorableClass]
   [Item("RegressionEnsembleModel", "A regression model that contains an ensemble of multiple regression models")]
-  public class RegressionEnsembleModel : NamedItem, IRegressionEnsembleModel {
+  public sealed class RegressionEnsembleModel : RegressionModel, IRegressionEnsembleModel {
+    public override IEnumerable<string> VariablesUsedForPrediction {
+      get { return models.SelectMany(x => x.VariablesUsedForPrediction).Distinct().OrderBy(x => x); }
+    }
 
     private List<IRegressionModel> models;
@@ -45,4 +49,27 @@
     }
 
+    private List<double> modelWeights;
+    public IEnumerable<double> ModelWeights {
+      get { return modelWeights; }
+    }
+
+    [Storable(Name = "ModelWeights")]
+    private IEnumerable<double> StorableModelWeights {
+      get { return modelWeights; }
+      set { modelWeights = value.ToList(); }
+    }
+
+    [Storable]
+    private bool averageModelEstimates = true;
+    public bool AverageModelEstimates {
+      get { return averageModelEstimates; }
+      set {
+        if (averageModelEstimates != value) {
+          averageModelEstimates = value;
+          OnChanged();
+        }
+      }
+    }
+
     #region backwards compatiblity 3.3.5
     [Storable(Name = "models", AllowOneWay = true)]
@@ -52,36 +79,96 @@
     #endregion
 
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      // BackwardsCompatibility 3.3.14
+      #region Backwards compatible code, remove with 3.4
+      if (modelWeights == null || !modelWeights.Any())
+        modelWeights = new List<double>(models.Select(m => 1.0));
+      #endregion
+    }
+
     [StorableConstructor]
-    protected RegressionEnsembleModel(bool deserializing) : base(deserializing) { }
-    protected RegressionEnsembleModel(RegressionEnsembleModel original, Cloner cloner)
+    private RegressionEnsembleModel(bool deserializing) : base(deserializing) { }
+    private RegressionEnsembleModel(RegressionEnsembleModel original, Cloner cloner)
       : base(original, cloner) {
-      this.models = original.Models.Select(m => cloner.Clone(m)).ToList();
+      this.models = original.Models.Select(cloner.Clone).ToList();
+      this.modelWeights = new List<double>(original.ModelWeights);
+      this.averageModelEstimates = original.averageModelEstimates;
+    }
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new RegressionEnsembleModel(this, cloner);
     }
 
     public RegressionEnsembleModel() : this(Enumerable.Empty<IRegressionModel>()) { }
-    public RegressionEnsembleModel(IEnumerable<IRegressionModel> models)
-      : base() {
+    public RegressionEnsembleModel(IEnumerable<IRegressionModel> models) : this(models, models.Select(m => 1.0)) { }
+    public RegressionEnsembleModel(IEnumerable<IRegressionModel> models, IEnumerable<double> modelWeights)
+      : base(string.Empty) {
       this.name = ItemName;
       this.description = ItemDescription;
+
       this.models = new List<IRegressionModel>(models);
-    }
-
-    public override IDeepCloneable Clone(Cloner cloner) {
-      return new RegressionEnsembleModel(this, cloner);
-    }
-
-    #region IRegressionEnsembleModel Members
+      this.modelWeights = new List<double>(modelWeights);
+
+      if (this.models.Any()) this.TargetVariable = this.models.First().TargetVariable;
+    }
 
     public void Add(IRegressionModel model) {
+      if (string.IsNullOrEmpty(TargetVariable)) TargetVariable = model.TargetVariable;
+      Add(model, 1.0);
+    }
+    public void Add(IRegressionModel model, double weight) {
+      if (string.IsNullOrEmpty(TargetVariable)) TargetVariable = model.TargetVariable;
+
       models.Add(model);
-    }
+      modelWeights.Add(weight);
+      OnChanged();
+    }
+
+    public void AddRange(IEnumerable<IRegressionModel> models) {
+      AddRange(models, models.Select(m => 1.0));
+    }
+    public void AddRange(IEnumerable<IRegressionModel> models, IEnumerable<double> weights) {
+      if (string.IsNullOrEmpty(TargetVariable)) TargetVariable = models.First().TargetVariable;
+
+      this.models.AddRange(models);
+      modelWeights.AddRange(weights);
+      OnChanged();
+    }
+
     public void Remove(IRegressionModel model) {
-      models.Remove(model);
-    }
-
+      var index = models.IndexOf(model);
+      models.RemoveAt(index);
+      modelWeights.RemoveAt(index);
+
+      if (!models.Any()) TargetVariable = string.Empty;
+      OnChanged();
+    }
+    public void RemoveRange(IEnumerable<IRegressionModel> models) {
+      foreach (var model in models) {
+        var index = this.models.IndexOf(model);
+        this.models.RemoveAt(index);
+        modelWeights.RemoveAt(index);
+      }
+
+      if (!models.Any()) TargetVariable = string.Empty;
+      OnChanged();
+    }
+
+    public double GetModelWeight(IRegressionModel model) {
+      var index = models.IndexOf(model);
+      return modelWeights[index];
+    }
+    public void SetModelWeight(IRegressionModel model, double weight) {
+      var index = models.IndexOf(model);
+      modelWeights[index] = weight;
+      OnChanged();
+    }
+
+    #region evaluation
     public IEnumerable<IEnumerable<double>> GetEstimatedValueVectors(IDataset dataset, IEnumerable<int> rows) {
       var estimatedValuesEnumerators = (from model in models
-                                        select model.GetEstimatedValues(dataset, rows).GetEnumerator())
-                                       .ToList();
+                                        let weight = GetModelWeight(model)
+                                        select model.GetEstimatedValues(dataset, rows).Select(e => weight * e)
+                                        .GetEnumerator()).ToList();
 
       while (estimatedValuesEnumerators.All(en => en.MoveNext())) {
@@ -91,22 +178,55 @@
     }
 
+    public override IEnumerable<double> GetEstimatedValues(IDataset dataset, IEnumerable<int> rows) {
+      double weightsSum = modelWeights.Sum();
+      var summedEstimates = from estimatedValuesVector in GetEstimatedValueVectors(dataset, rows)
+                            select estimatedValuesVector.DefaultIfEmpty(double.NaN).Sum();
+
+      if (AverageModelEstimates)
+        return summedEstimates.Select(v => v / weightsSum);
+      else
+        return summedEstimates;
+
+    }
+
+    public IEnumerable<double> GetEstimatedValues(IDataset dataset, IEnumerable<int> rows, Func<int, IRegressionModel, bool> modelSelectionPredicate) {
+      var estimatedValuesEnumerators = GetEstimatedValueVectors(dataset, rows).GetEnumerator();
+      var rowsEnumerator = rows.GetEnumerator();
+
+      while (rowsEnumerator.MoveNext() & estimatedValuesEnumerators.MoveNext()) {
+        var estimatedValueEnumerator = estimatedValuesEnumerators.Current.GetEnumerator();
+        int currentRow = rowsEnumerator.Current;
+        double weightsSum = 0.0;
+        double filteredEstimatesSum = 0.0;
+
+        for (int m = 0; m < models.Count; m++) {
+          estimatedValueEnumerator.MoveNext();
+          var model = models[m];
+          if (!modelSelectionPredicate(currentRow, model)) continue;
+
+          filteredEstimatesSum += estimatedValueEnumerator.Current;
+          weightsSum += modelWeights[m];
+        }
+
+        if (AverageModelEstimates)
+          yield return filteredEstimatesSum / weightsSum;
+        else
+          yield return filteredEstimatesSum;
+      }
+    }
+
     #endregion
 
-    #region IRegressionModel Members
-
-    public IEnumerable<double> GetEstimatedValues(IDataset dataset, IEnumerable<int> rows) {
-      foreach (var estimatedValuesVector in GetEstimatedValueVectors(dataset, rows)) {
-        yield return estimatedValuesVector.Average();
-      }
-    }
-
-    public RegressionEnsembleSolution CreateRegressionSolution(IRegressionProblemData problemData) {
-      return new RegressionEnsembleSolution(this.Models, new RegressionEnsembleProblemData(problemData));
-    }
-    IRegressionSolution IRegressionModel.CreateRegressionSolution(IRegressionProblemData problemData) {
-      return CreateRegressionSolution(problemData);
-    }
-
-    #endregion
+    public event EventHandler Changed;
+    private void OnChanged() {
+      var handler = Changed;
+      if (handler != null)
+        handler(this, EventArgs.Empty);
+    }
+
+
+    public override IRegressionSolution CreateRegressionSolution(IRegressionProblemData problemData) {
+      return new RegressionEnsembleSolution(this, new RegressionEnsembleProblemData(problemData));
+    }
   }
 }
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionEnsembleSolution.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionEnsembleSolution.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionEnsembleSolution.cs	(revision 15280)
@@ -79,4 +79,6 @@
         }
       }
+
+      RegisterModelEvents();
       RegisterRegressionSolutionsEventHandler();
     }
@@ -93,8 +95,10 @@
       }
 
+      evaluationCache = new Dictionary<int, double>(original.ProblemData.Dataset.Rows);
       trainingEvaluationCache = new Dictionary<int, double>(original.ProblemData.TrainingIndices.Count());
       testEvaluationCache = new Dictionary<int, double>(original.ProblemData.TestIndices.Count());
 
       regressionSolutions = cloner.Clone(original.regressionSolutions);
+      RegisterModelEvents();
       RegisterRegressionSolutionsEventHandler();
     }
@@ -106,50 +110,42 @@
       regressionSolutions = new ItemCollection<IRegressionSolution>();
 
+      RegisterModelEvents();
       RegisterRegressionSolutionsEventHandler();
     }
 
     public RegressionEnsembleSolution(IRegressionProblemData problemData)
-      : this(Enumerable.Empty<IRegressionModel>(), problemData) {
-    }
-
-    public RegressionEnsembleSolution(IEnumerable<IRegressionModel> models, IRegressionProblemData problemData)
-      : this(models, problemData,
-             models.Select(m => (IntRange)problemData.TrainingPartition.Clone()),
-             models.Select(m => (IntRange)problemData.TestPartition.Clone())
-      ) { }
-
-    public RegressionEnsembleSolution(IEnumerable<IRegressionModel> models, IRegressionProblemData problemData, IEnumerable<IntRange> trainingPartitions, IEnumerable<IntRange> testPartitions)
-      : base(new RegressionEnsembleModel(Enumerable.Empty<IRegressionModel>()), new RegressionEnsembleProblemData(problemData)) {
-      this.trainingPartitions = new Dictionary<IRegressionModel, IntRange>();
-      this.testPartitions = new Dictionary<IRegressionModel, IntRange>();
-      this.regressionSolutions = new ItemCollection<IRegressionSolution>();
-
-      List<IRegressionSolution> solutions = new List<IRegressionSolution>();
-      var modelEnumerator = models.GetEnumerator();
-      var trainingPartitionEnumerator = trainingPartitions.GetEnumerator();
-      var testPartitionEnumerator = testPartitions.GetEnumerator();
-
-      while (modelEnumerator.MoveNext() & trainingPartitionEnumerator.MoveNext() & testPartitionEnumerator.MoveNext()) {
-        var p = (IRegressionProblemData)problemData.Clone();
-        p.TrainingPartition.Start = trainingPartitionEnumerator.Current.Start;
-        p.TrainingPartition.End = trainingPartitionEnumerator.Current.End;
-        p.TestPartition.Start = testPartitionEnumerator.Current.Start;
-        p.TestPartition.End = testPartitionEnumerator.Current.End;
-
-        solutions.Add(modelEnumerator.Current.CreateRegressionSolution(p));
-      }
-      if (modelEnumerator.MoveNext() | trainingPartitionEnumerator.MoveNext() | testPartitionEnumerator.MoveNext()) {
-        throw new ArgumentException();
-      }
-
+      : this(new RegressionEnsembleModel(), problemData) {
+    }
+
+    public RegressionEnsembleSolution(IRegressionEnsembleModel model, IRegressionProblemData problemData)
+      : base(model, new RegressionEnsembleProblemData(problemData)) {
+      trainingPartitions = new Dictionary<IRegressionModel, IntRange>();
+      testPartitions = new Dictionary<IRegressionModel, IntRange>();
+      regressionSolutions = new ItemCollection<IRegressionSolution>();
+
+      evaluationCache = new Dictionary<int, double>(problemData.Dataset.Rows);
       trainingEvaluationCache = new Dictionary<int, double>(problemData.TrainingIndices.Count());
       testEvaluationCache = new Dictionary<int, double>(problemData.TestIndices.Count());
 
+
+      var solutions = model.Models.Select(m => m.CreateRegressionSolution((IRegressionProblemData)problemData.Clone()));
+      foreach (var solution in solutions) {
+        regressionSolutions.Add(solution);
+        trainingPartitions.Add(solution.Model, solution.ProblemData.TrainingPartition);
+        testPartitions.Add(solution.Model, solution.ProblemData.TestPartition);
+      }
+
+      RecalculateResults();
+      RegisterModelEvents();
       RegisterRegressionSolutionsEventHandler();
-      regressionSolutions.AddRange(solutions);
-    }
+    }
+
 
     public override IDeepCloneable Clone(Cloner cloner) {
       return new RegressionEnsembleSolution(this, cloner);
+    }
+
+    private void RegisterModelEvents() {
+      Model.Changed += Model_Changed;
     }
     private void RegisterRegressionSolutionsEventHandler() {
@@ -168,6 +164,7 @@
         var rows = ProblemData.TrainingIndices;
         var rowsToEvaluate = rows.Except(trainingEvaluationCache.Keys);
+
         var rowsEnumerator = rowsToEvaluate.GetEnumerator();
-        var valuesEnumerator = GetEstimatedValues(rowsToEvaluate, (r, m) => RowIsTrainingForModel(r, m) && !RowIsTestForModel(r, m)).GetEnumerator();
+        var valuesEnumerator = Model.GetEstimatedValues(ProblemData.Dataset, rowsToEvaluate, (r, m) => RowIsTrainingForModel(r, m) && !RowIsTestForModel(r, m)).GetEnumerator();
 
         while (rowsEnumerator.MoveNext() & valuesEnumerator.MoveNext()) {
@@ -184,5 +181,5 @@
         var rowsToEvaluate = rows.Except(testEvaluationCache.Keys);
         var rowsEnumerator = rowsToEvaluate.GetEnumerator();
-        var valuesEnumerator = GetEstimatedValues(rowsToEvaluate, RowIsTestForModel).GetEnumerator();
+        var valuesEnumerator = Model.GetEstimatedValues(ProblemData.Dataset, rowsToEvaluate, RowIsTestForModel).GetEnumerator();
 
         while (rowsEnumerator.MoveNext() & valuesEnumerator.MoveNext()) {
@@ -193,27 +190,8 @@
       }
     }
-
-    private IEnumerable<double> GetEstimatedValues(IEnumerable<int> rows, Func<int, IRegressionModel, bool> modelSelectionPredicate) {
-      var estimatedValuesEnumerators = (from model in Model.Models
-                                        select new { Model = model, EstimatedValuesEnumerator = model.GetEstimatedValues(ProblemData.Dataset, rows).GetEnumerator() })
-                                       .ToList();
-      var rowsEnumerator = rows.GetEnumerator();
-      // aggregate to make sure that MoveNext is called for all enumerators 
-      while (rowsEnumerator.MoveNext() & estimatedValuesEnumerators.Select(en => en.EstimatedValuesEnumerator.MoveNext()).Aggregate(true, (acc, b) => acc & b)) {
-        int currentRow = rowsEnumerator.Current;
-
-        var selectedEnumerators = from pair in estimatedValuesEnumerators
-                                  where modelSelectionPredicate(currentRow, pair.Model)
-                                  select pair.EstimatedValuesEnumerator;
-
-        yield return AggregateEstimatedValues(selectedEnumerators.Select(x => x.Current));
-      }
-    }
-
     private bool RowIsTrainingForModel(int currentRow, IRegressionModel model) {
       return trainingPartitions == null || !trainingPartitions.ContainsKey(model) ||
               (trainingPartitions[model].Start <= currentRow && currentRow < trainingPartitions[model].End);
     }
-
     private bool RowIsTestForModel(int currentRow, IRegressionModel model) {
       return testPartitions == null || !testPartitions.ContainsKey(model) ||
@@ -224,7 +202,5 @@
       var rowsToEvaluate = rows.Except(evaluationCache.Keys);
       var rowsEnumerator = rowsToEvaluate.GetEnumerator();
-      var valuesEnumerator = (from xs in GetEstimatedValueVectors(ProblemData.Dataset, rowsToEvaluate)
-                              select AggregateEstimatedValues(xs))
-                             .GetEnumerator();
+      var valuesEnumerator = Model.GetEstimatedValues(ProblemData.Dataset, rowsToEvaluate).GetEnumerator();
 
       while (rowsEnumerator.MoveNext() & valuesEnumerator.MoveNext()) {
@@ -235,18 +211,6 @@
     }
 
-    public IEnumerable<IEnumerable<double>> GetEstimatedValueVectors(IDataset dataset, IEnumerable<int> rows) {
-      if (!Model.Models.Any()) yield break;
-      var estimatedValuesEnumerators = (from model in Model.Models
-                                        select model.GetEstimatedValues(dataset, rows).GetEnumerator())
-                                       .ToList();
-
-      while (estimatedValuesEnumerators.All(en => en.MoveNext())) {
-        yield return from enumerator in estimatedValuesEnumerators
-                     select enumerator.Current;
-      }
-    }
-
-    private double AggregateEstimatedValues(IEnumerable<double> estimatedValues) {
-      return estimatedValues.DefaultIfEmpty(double.NaN).Average();
+    public IEnumerable<IEnumerable<double>> GetEstimatedValueVectors(IEnumerable<int> rows) {
+      return Model.GetEstimatedValueVectors(ProblemData.Dataset, rows);
     }
     #endregion
@@ -282,53 +246,56 @@
     }
 
-    public void AddRegressionSolutions(IEnumerable<IRegressionSolution> solutions) {
-      regressionSolutions.AddRange(solutions);
+    private void Model_Changed(object sender, EventArgs e) {
+      var modelSet = new HashSet<IRegressionModel>(Model.Models);
+      foreach (var model in Model.Models) {
+        if (!trainingPartitions.ContainsKey(model)) trainingPartitions.Add(model, ProblemData.TrainingPartition);
+        if (!testPartitions.ContainsKey(model)) testPartitions.Add(model, ProblemData.TrainingPartition);
+      }
+      foreach (var model in trainingPartitions.Keys) {
+        if (modelSet.Contains(model)) continue;
+        trainingPartitions.Remove(model);
+        testPartitions.Remove(model);
+      }
 
       trainingEvaluationCache.Clear();
       testEvaluationCache.Clear();
       evaluationCache.Clear();
+
+      OnModelChanged();
+    }
+
+    public void AddRegressionSolutions(IEnumerable<IRegressionSolution> solutions) {
+      regressionSolutions.AddRange(solutions);
     }
     public void RemoveRegressionSolutions(IEnumerable<IRegressionSolution> solutions) {
       regressionSolutions.RemoveRange(solutions);
-
-      trainingEvaluationCache.Clear();
-      testEvaluationCache.Clear();
-      evaluationCache.Clear();
     }
 
     private void regressionSolutions_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRegressionSolution> e) {
-      foreach (var solution in e.Items) AddRegressionSolution(solution);
-      RecalculateResults();
+      foreach (var solution in e.Items) {
+        trainingPartitions.Add(solution.Model, solution.ProblemData.TrainingPartition);
+        testPartitions.Add(solution.Model, solution.ProblemData.TestPartition);
+      }
+      Model.AddRange(e.Items.Select(s => s.Model));
     }
     private void regressionSolutions_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRegressionSolution> e) {
-      foreach (var solution in e.Items) RemoveRegressionSolution(solution);
-      RecalculateResults();
+      foreach (var solution in e.Items) {
+        trainingPartitions.Remove(solution.Model);
+        testPartitions.Remove(solution.Model);
+      }
+      Model.RemoveRange(e.Items.Select(s => s.Model));
     }
     private void regressionSolutions_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRegressionSolution> e) {
-      foreach (var solution in e.OldItems) RemoveRegressionSolution(solution);
-      foreach (var solution in e.Items) AddRegressionSolution(solution);
-      RecalculateResults();
-    }
-
-    private void AddRegressionSolution(IRegressionSolution solution) {
-      if (Model.Models.Contains(solution.Model)) throw new ArgumentException();
-      Model.Add(solution.Model);
-      trainingPartitions[solution.Model] = solution.ProblemData.TrainingPartition;
-      testPartitions[solution.Model] = solution.ProblemData.TestPartition;
-
-      trainingEvaluationCache.Clear();
-      testEvaluationCache.Clear();
-      evaluationCache.Clear();
-    }
-
-    private void RemoveRegressionSolution(IRegressionSolution solution) {
-      if (!Model.Models.Contains(solution.Model)) throw new ArgumentException();
-      Model.Remove(solution.Model);
-      trainingPartitions.Remove(solution.Model);
-      testPartitions.Remove(solution.Model);
-
-      trainingEvaluationCache.Clear();
-      testEvaluationCache.Clear();
-      evaluationCache.Clear();
+      foreach (var solution in e.OldItems) {
+        trainingPartitions.Remove(solution.Model);
+        testPartitions.Remove(solution.Model);
+      }
+      Model.RemoveRange(e.OldItems.Select(s => s.Model));
+
+      foreach (var solution in e.Items) {
+        trainingPartitions.Add(solution.Model, solution.ProblemData.TrainingPartition);
+        testPartitions.Add(solution.Model, solution.ProblemData.TestPartition);
+      }
+      Model.AddRange(e.Items.Select(s => s.Model));
     }
   }
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionModel.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionModel.cs	(revision 15280)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionModel.cs	(revision 15280)
@@ -0,0 +1,68 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Collections.Generic;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Problems.DataAnalysis {
+  [StorableClass]
+  [Item("Regression Model", "Base class for all regression models.")]
+  public abstract class RegressionModel : DataAnalysisModel, IRegressionModel {
+    [Storable]
+    private string targetVariable;
+    public string TargetVariable {
+      get { return targetVariable; }
+      protected set { targetVariable = value; }
+    }
+
+    protected RegressionModel(bool deserializing) : base(deserializing) { }
+
+    protected RegressionModel(RegressionModel original, Cloner cloner)
+      : base(original, cloner) {
+      this.targetVariable = original.targetVariable;
+    }
+
+    protected RegressionModel(string targetVariable)
+      : base("Regression Model") {
+      this.targetVariable = targetVariable;
+    }
+    protected RegressionModel(string targetVariable, string name)
+      : base(name) {
+      this.targetVariable = targetVariable;
+    }
+    protected RegressionModel(string targetVariable, string name, string description)
+      : base(name, description) {
+      this.targetVariable = targetVariable;
+    }
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      // BackwardsCompatibility3.3
+      #region Backwards compatible code, remove with 3.4
+      targetVariable = string.Empty;
+      #endregion
+    }
+    public abstract IEnumerable<double> GetEstimatedValues(IDataset dataset, IEnumerable<int> rows);
+    public abstract IRegressionSolution CreateRegressionSolution(IRegressionProblemData problemData);
+  }
+}
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionProblemData.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionProblemData.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionProblemData.cs	(revision 15280)
@@ -110,4 +110,15 @@
     }
 
+    public IEnumerable<double> TargetVariableValues {
+      get { return Dataset.GetDoubleValues(TargetVariable); }
+    }
+    public IEnumerable<double> TargetVariableTrainingValues {
+      get { return Dataset.GetDoubleValues(TargetVariable, TrainingIndices); }
+    }
+    public IEnumerable<double> TargetVariableTestValues {
+      get { return Dataset.GetDoubleValues(TargetVariable, TestIndices); }
+    }
+
+
     [StorableConstructor]
     protected RegressionProblemData(bool deserializing) : base(deserializing) { }
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionSolutionVariableImpactsCalculator.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionSolutionVariableImpactsCalculator.cs	(revision 15280)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionSolutionVariableImpactsCalculator.cs	(revision 15280)
@@ -0,0 +1,179 @@
+﻿#region License Information
+
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Problems.DataAnalysis {
+  [StorableClass]
+  [Item("RegressionSolution Impacts Calculator", "Calculation of the impacts of input variables for any regression solution")]
+  public sealed class RegressionSolutionVariableImpactsCalculator : ParameterizedNamedItem {
+    public enum ReplacementMethodEnum {
+      Median,
+      Average,
+      Shuffle,
+      Noise
+    }
+
+    public enum DataPartitionEnum {
+      Training,
+      Test,
+      All
+    }
+
+    private const string ReplacementParameterName = "Replacement Method";
+    private const string DataPartitionParameterName = "DataPartition";
+
+    public IFixedValueParameter<EnumValue<ReplacementMethodEnum>> ReplacementParameter {
+      get { return (IFixedValueParameter<EnumValue<ReplacementMethodEnum>>)Parameters[ReplacementParameterName]; }
+    }
+    public IFixedValueParameter<EnumValue<DataPartitionEnum>> DataPartitionParameter {
+      get { return (IFixedValueParameter<EnumValue<DataPartitionEnum>>)Parameters[DataPartitionParameterName]; }
+    }
+
+    public ReplacementMethodEnum ReplacementMethod {
+      get { return ReplacementParameter.Value.Value; }
+      set { ReplacementParameter.Value.Value = value; }
+    }
+    public DataPartitionEnum DataPartition {
+      get { return DataPartitionParameter.Value.Value; }
+      set { DataPartitionParameter.Value.Value = value; }
+    }
+
+
+    [StorableConstructor]
+    private RegressionSolutionVariableImpactsCalculator(bool deserializing) : base(deserializing) { }
+    private RegressionSolutionVariableImpactsCalculator(RegressionSolutionVariableImpactsCalculator original, Cloner cloner)
+      : base(original, cloner) { }
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new RegressionSolutionVariableImpactsCalculator(this, cloner);
+    }
+
+    public RegressionSolutionVariableImpactsCalculator()
+      : base() {
+      Parameters.Add(new FixedValueParameter<EnumValue<ReplacementMethodEnum>>(ReplacementParameterName, "The replacement method for variables during impact calculation.", new EnumValue<ReplacementMethodEnum>(ReplacementMethodEnum.Median)));
+      Parameters.Add(new FixedValueParameter<EnumValue<DataPartitionEnum>>(DataPartitionParameterName, "The data partition on which the impacts are calculated.", new EnumValue<DataPartitionEnum>(DataPartitionEnum.Training)));
+    }
+
+    //mkommend: annoying name clash with static method, open to better naming suggestions
+    public IEnumerable<Tuple<string, double>> Calculate(IRegressionSolution solution) {
+      return CalculateImpacts(solution, DataPartition, ReplacementMethod);
+    }
+
+    public static IEnumerable<Tuple<string, double>> CalculateImpacts(IRegressionSolution solution,
+      DataPartitionEnum data = DataPartitionEnum.Training,
+      ReplacementMethodEnum replacement = ReplacementMethodEnum.Median) {
+
+      var problemData = solution.ProblemData;
+      var dataset = problemData.Dataset;
+
+      IEnumerable<int> rows;
+      IEnumerable<double> targetValues;
+      double originalR2 = -1;
+
+      OnlineCalculatorError error;
+
+      switch (data) {
+        case DataPartitionEnum.All:
+          rows = solution.ProblemData.AllIndices;
+          targetValues = problemData.TargetVariableValues.ToList();
+          originalR2 = OnlinePearsonsRCalculator.Calculate(problemData.TargetVariableValues, solution.EstimatedValues, out error);
+          if (error != OnlineCalculatorError.None) throw new InvalidOperationException("Error during R² calculation.");
+          originalR2 = originalR2 * originalR2;
+          break;
+        case DataPartitionEnum.Training:
+          rows = problemData.TrainingIndices;
+          targetValues = problemData.TargetVariableTrainingValues.ToList();
+          originalR2 = solution.TrainingRSquared;
+          break;
+        case DataPartitionEnum.Test:
+          rows = problemData.TestIndices;
+          targetValues = problemData.TargetVariableTestValues.ToList();
+          originalR2 = solution.TestRSquared;
+          break;
+        default: throw new ArgumentException(string.Format("DataPartition {0} cannot be handled.", data));
+      }
+
+
+      var impacts = new Dictionary<string, double>();
+      var modifiableDataset = ((Dataset)dataset).ToModifiable();
+
+      foreach (var inputVariable in problemData.AllowedInputVariables) {
+        var newEstimates = EvaluateModelWithReplacedVariable(solution.Model, inputVariable, modifiableDataset, rows, replacement);
+        var newR2 = OnlinePearsonsRCalculator.Calculate(targetValues, newEstimates, out error);
+        if (error != OnlineCalculatorError.None) throw new InvalidOperationException("Error during R² calculation with replaced inputs.");
+
+        newR2 = newR2 * newR2;
+        var impact = originalR2 - newR2;
+        impacts[inputVariable] = impact;
+      }
+      return impacts.OrderByDescending(i => i.Value).Select(i => Tuple.Create(i.Key, i.Value));
+    }
+
+    private static IEnumerable<double> EvaluateModelWithReplacedVariable(IRegressionModel model, string variable, ModifiableDataset dataset, IEnumerable<int> rows, ReplacementMethodEnum replacement = ReplacementMethodEnum.Median) {
+      var originalValues = dataset.GetReadOnlyDoubleValues(variable).ToList();
+      double replacementValue;
+      List<double> replacementValues;
+      IRandom rand;
+
+      switch (replacement) {
+        case ReplacementMethodEnum.Median:
+          replacementValue = rows.Select(r => originalValues[r]).Median();
+          replacementValues = Enumerable.Repeat(replacementValue, dataset.Rows).ToList();
+          break;
+        case ReplacementMethodEnum.Average:
+          replacementValue = rows.Select(r => originalValues[r]).Average();
+          replacementValues = Enumerable.Repeat(replacementValue, dataset.Rows).ToList();
+          break;
+        case ReplacementMethodEnum.Shuffle:
+          // new var has same empirical distribution but the relation to y is broken
+          rand = new FastRandom(31415);
+          replacementValues = rows.Select(r => originalValues[r]).Shuffle(rand).ToList();
+          break;
+        case ReplacementMethodEnum.Noise:
+          var avg = rows.Select(r => originalValues[r]).Average();
+          var stdDev = rows.Select(r => originalValues[r]).StandardDeviation();
+          rand = new FastRandom(31415);
+          replacementValues = rows.Select(_ => NormalDistributedRandom.NextDouble(rand, avg, stdDev)).ToList();
+          break;
+
+        default:
+          throw new ArgumentException(string.Format("ReplacementMethod {0} cannot be handled.", replacement));
+      }
+
+      dataset.ReplaceVariable(variable, replacementValues);
+      //mkommend: ToList is used on purpose to avoid lazy evaluation that could result in wrong estimates due to variable replacements
+      var estimates = model.GetEstimatedValues(dataset, rows).ToList();
+      dataset.ReplaceVariable(variable, originalValues);
+
+      return estimates;
+    }
+  }
+}
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/TimeSeriesPrognosis/Models/ConstantTimeSeriesPrognosisModel.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/TimeSeriesPrognosis/Models/ConstantTimeSeriesPrognosisModel.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/TimeSeriesPrognosis/Models/ConstantTimeSeriesPrognosisModel.cs	(revision 15280)
@@ -39,5 +39,5 @@
     }
 
-    public ConstantTimeSeriesPrognosisModel(double constant) : base(constant) { }
+    public ConstantTimeSeriesPrognosisModel(double constant, string targetVariable) : base(constant, targetVariable) { }
 
     public IEnumerable<IEnumerable<double>> GetPrognosedValues(IDataset dataset, IEnumerable<int> rows, IEnumerable<int> horizons) {
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/TimeSeriesPrognosis/Models/TimeSeriesPrognosisAutoRegressiveModel.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/TimeSeriesPrognosis/Models/TimeSeriesPrognosisAutoRegressiveModel.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/TimeSeriesPrognosis/Models/TimeSeriesPrognosisAutoRegressiveModel.cs	(revision 15280)
@@ -30,11 +30,13 @@
   [StorableClass]
   [Item("Autoregressive TimeSeries Model", "A linear autoregressive time series model used to predict future values.")]
-  public class TimeSeriesPrognosisAutoRegressiveModel : NamedItem, ITimeSeriesPrognosisModel {
+  public class TimeSeriesPrognosisAutoRegressiveModel : RegressionModel, ITimeSeriesPrognosisModel {
+    public override IEnumerable<string> VariablesUsedForPrediction {
+      get { return new[] { TargetVariable }; } 
+    }
+
     [Storable]
     public double[] Phi { get; private set; }
     [Storable]
     public double Constant { get; private set; }
-    [Storable]
-    public string TargetVariable { get; private set; }
 
     public int TimeOffset { get { return Phi.Length; } }
@@ -46,5 +48,4 @@
       this.Phi = (double[])original.Phi.Clone();
       this.Constant = original.Constant;
-      this.TargetVariable = original.TargetVariable;
     }
     public override IDeepCloneable Clone(Cloner cloner) {
@@ -52,8 +53,7 @@
     }
     public TimeSeriesPrognosisAutoRegressiveModel(string targetVariable, double[] phi, double constant)
-      : base("AR(1) Model") {
+      : base(targetVariable, "AR(1) Model") {
       Phi = (double[])phi.Clone();
       Constant = constant;
-      TargetVariable = targetVariable;
     }
 
@@ -91,5 +91,5 @@
     }
 
-    public IEnumerable<double> GetEstimatedValues(IDataset dataset, IEnumerable<int> rows) {
+    public override IEnumerable<double> GetEstimatedValues(IDataset dataset, IEnumerable<int> rows) {
       var targetVariables = dataset.GetReadOnlyDoubleValues(TargetVariable);
       foreach (int row in rows) {
@@ -111,5 +111,5 @@
       return new TimeSeriesPrognosisSolution(this, new TimeSeriesPrognosisProblemData(problemData));
     }
-    public IRegressionSolution CreateRegressionSolution(IRegressionProblemData problemData) {
+    public override IRegressionSolution CreateRegressionSolution(IRegressionProblemData problemData) {
       throw new NotSupportedException();
     }
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/TimeSeriesPrognosis/TimeSeriesPrognosisResults.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/TimeSeriesPrognosis/TimeSeriesPrognosisResults.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/TimeSeriesPrognosis/TimeSeriesPrognosisResults.cs	(revision 15280)
@@ -373,5 +373,5 @@
       //mean model
       double trainingMean = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TrainingIndices).Average();
-      var meanModel = new ConstantModel(trainingMean);
+      var meanModel = new ConstantModel(trainingMean, problemData.TargetVariable);
 
       //AR1 model
@@ -395,5 +395,5 @@
       PrognosisTrainingMeanAbsoluteError = errorState == OnlineCalculatorError.None ? trainingMAE : double.NaN;
       double trainingR = OnlinePearsonsRCalculator.Calculate(originalTrainingValues, estimatedTrainingValues, out errorState);
-      PrognosisTrainingRSquared = errorState == OnlineCalculatorError.None ? trainingR*trainingR : double.NaN;
+      PrognosisTrainingRSquared = errorState == OnlineCalculatorError.None ? trainingR * trainingR : double.NaN;
       double trainingRelError = OnlineMeanAbsolutePercentageErrorCalculator.Calculate(originalTrainingValues, estimatedTrainingValues, out errorState);
       PrognosisTrainingRelativeError = errorState == OnlineCalculatorError.None ? trainingRelError : double.NaN;
@@ -431,5 +431,5 @@
       PrognosisTestMeanAbsoluteError = errorState == OnlineCalculatorError.None ? testMAE : double.NaN;
       double testR = OnlinePearsonsRCalculator.Calculate(originalTestValues, estimatedTestValues, out errorState);
-      PrognosisTestRSquared = errorState == OnlineCalculatorError.None ? testR*testR : double.NaN;
+      PrognosisTestRSquared = errorState == OnlineCalculatorError.None ? testR * testR : double.NaN;
       double testRelError = OnlineMeanAbsolutePercentageErrorCalculator.Calculate(originalTestValues, estimatedTestValues, out errorState);
       PrognosisTestRelativeError = errorState == OnlineCalculatorError.None ? testRelError : double.NaN;
@@ -448,5 +448,5 @@
         //mean model
         double trainingMean = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TrainingIndices).Average();
-        var meanModel = new ConstantModel(trainingMean);
+        var meanModel = new ConstantModel(trainingMean, problemData.TargetVariable);
 
         //AR1 model
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/TimeSeriesPrognosis/TimeSeriesPrognosisSolutionBase.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/TimeSeriesPrognosis/TimeSeriesPrognosisSolutionBase.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/TimeSeriesPrognosis/TimeSeriesPrognosisSolutionBase.cs	(revision 15280)
@@ -150,5 +150,5 @@
       OnlineCalculatorError errorState;
       double trainingMean = ProblemData.TrainingIndices.Any() ? ProblemData.Dataset.GetDoubleValues(ProblemData.TargetVariable, ProblemData.TrainingIndices).Average() : double.NaN;
-      var meanModel = new ConstantModel(trainingMean);
+      var meanModel = new ConstantModel(trainingMean,ProblemData.TargetVariable);
 
       double alpha, beta;
Index: branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Transformations/Transformation.cs
===================================================================
--- branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Transformations/Transformation.cs	(revision 13401)
+++ branches/Async/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Transformations/Transformation.cs	(revision 15280)
@@ -31,4 +31,5 @@
 
   [Item("Transformation", "Represents the base class for a transformation.")]
+  [StorableClass]
   public abstract class Transformation : ParameterizedNamedItem, ITransformation {
     protected const string ColumnParameterName = "Column";
