Index: /branches/2904_CalculateImpacts/3.4/Dataset.cs
===================================================================
--- /branches/2904_CalculateImpacts/3.4/Dataset.cs	(revision 16187)
+++ /branches/2904_CalculateImpacts/3.4/Dataset.cs	(revision 16188)
@@ -38,8 +38,10 @@
     protected Dataset(Dataset original, Cloner cloner)
       : base(original, cloner) {
+      // no need to clone the variable values because these can't be modified
       variableValues = new Dictionary<string, IList>(original.variableValues);
       variableNames = new List<string>(original.variableNames);
       rows = original.rows;
     }
+
     public override IDeepCloneable Clone(Cloner cloner) { return new Dataset(this, cloner); }
 
@@ -58,32 +60,29 @@
     /// <param name="variableValues">The values for the variables (column-oriented storage). Values are not cloned!</param>
     public Dataset(IEnumerable<string> variableNames, IEnumerable<IList> variableValues)
-      : base() {
+      : this(variableNames, variableValues, cloneValues: true) {
+    }
+
+    protected Dataset(IEnumerable<string> variableNames, IEnumerable<IList> variableValues, bool cloneValues = false) {
       Name = "-";
-      if (!variableNames.Any()) {
+
+      if (variableNames.Any()) {
+        this.variableNames = new List<string>(variableNames);
+      } else {
         this.variableNames = Enumerable.Range(0, variableValues.Count()).Select(x => "Column " + x).ToList();
-      } else if (variableNames.Count() != variableValues.Count()) {
-        throw new ArgumentException("Number of variable names doesn't match the number of columns of variableValues");
-      } else if (!variableValues.All(list => list.Count == variableValues.First().Count)) {
-        throw new ArgumentException("The number of values must be equal for every variable");
-      } else if (variableNames.Distinct().Count() != variableNames.Count()) {
-        var duplicateVariableNames =
-          variableNames.GroupBy(v => v).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
-        string message = "The dataset cannot contain duplicate variables names: " + Environment.NewLine;
-        foreach (var duplicateVariableName in duplicateVariableNames)
-          message += duplicateVariableName + Environment.NewLine;
-        throw new ArgumentException(message);
-      }
+      }
+      // check if the arguments are consistent (no duplicate variables, same number of rows, correct data types, ...)
+      CheckArguments(this.variableNames, variableValues);
+
       rows = variableValues.First().Count;
-      this.variableNames = new List<string>(variableNames);
-      this.variableValues = new Dictionary<string, IList>(this.variableNames.Count);
-      for (int i = 0; i < this.variableNames.Count; i++) {
-        var variableName = this.variableNames[i];
-        var values = variableValues.ElementAt(i);
-
-        if (!IsAllowedType(values)) {
-          throw new ArgumentException(string.Format("Unsupported type {0} for variable {1}.", GetElementType(values), variableName));
+
+      if (cloneValues) {
+        this.variableValues = CloneValues(this.variableNames, variableValues);
+      } else {
+        this.variableValues = new Dictionary<string, IList>(this.variableNames.Count);
+        for (int i = 0; i < this.variableNames.Count; i++) {
+          var variableName = this.variableNames[i];
+          var values = variableValues.ElementAt(i);
+          this.variableValues.Add(variableName, values);
         }
-
-        this.variableValues.Add(variableName, values);
       }
     }
@@ -117,17 +116,5 @@
 
     public ModifiableDataset ToModifiable() {
-      var values = new List<IList>();
-      foreach (var v in variableNames) {
-        if (VariableHasType<double>(v)) {
-          values.Add(new List<double>((IList<double>)variableValues[v]));
-        } else if (VariableHasType<string>(v)) {
-          values.Add(new List<string>((IList<string>)variableValues[v]));
-        } else if (VariableHasType<DateTime>(v)) {
-          values.Add(new List<DateTime>((IList<DateTime>)variableValues[v]));
-        } else {
-          throw new ArgumentException("Unknown variable type.");
-        }
-      }
-      return new ModifiableDataset(variableNames, values);
+      return new ModifiableDataset(variableNames, variableNames.Select(v => variableValues[v]), true);
     }
 
@@ -142,5 +129,5 @@
     }
 
-    protected Dataset(Dataset dataset) : this(dataset.variableNames, dataset.variableValues.Values) { }
+
 
     #region Backwards compatible code, remove with 3.5
@@ -238,6 +225,4 @@
       return new ReadOnlyCollection<DateTime>(values);
     }
-
-
     private IEnumerable<T> GetValues<T>(string variableName, IEnumerable<int> rows) {
       var values = GetValues<T>(variableName);
@@ -255,5 +240,4 @@
       return variableValues[variableName] is IList<T>;
     }
-
     protected Type GetVariableType(string variableName) {
       IList list;
@@ -263,24 +247,67 @@
       return GetElementType(list);
     }
-
-    protected Type GetElementType(IList list) {
+    protected static Type GetElementType(IList list) {
       var type = list.GetType();
       return type.IsGenericType ? type.GetGenericArguments()[0] : type.GetElementType();
     }
-
-    protected bool IsAllowedType(IList list) {
+    protected static bool IsAllowedType(IList list) {
       var type = GetElementType(list);
       return IsAllowedType(type);
     }
-
-    protected bool IsAllowedType(Type type) {
+    protected static bool IsAllowedType(Type type) {
       return type == typeof(double) || type == typeof(string) || type == typeof(DateTime);
+    }
+
+    protected static void CheckArguments(IEnumerable<string> variableNames, IEnumerable<IList> variableValues) {
+      if (variableNames.Count() != variableValues.Count()) {
+        throw new ArgumentException("Number of variable names doesn't match the number of columns of variableValues");
+      } else if (!variableValues.All(list => list.Count == variableValues.First().Count)) {
+        throw new ArgumentException("The number of values must be equal for every variable");
+      } else if (variableNames.Distinct().Count() != variableNames.Count()) {
+        var duplicateVariableNames =
+          variableNames.GroupBy(v => v).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
+        string message = "The dataset cannot contain duplicate variables names: " + Environment.NewLine;
+        foreach (var duplicateVariableName in duplicateVariableNames)
+          message += duplicateVariableName + Environment.NewLine;
+        throw new ArgumentException(message);
+      }
+      // check if all the variables are supported
+      foreach (var t in variableNames.Zip(variableValues, Tuple.Create)) {
+        var variableName = t.Item1;
+        var values = t.Item2;
+
+        if (!IsAllowedType(values)) {
+          throw new ArgumentException(string.Format("Unsupported type {0} for variable {1}.", GetElementType(values), variableName));
+        }
+      }
+    }
+
+    protected static Dictionary<string, IList> CloneValues(Dictionary<string, IList> variableValues) {
+      return variableValues.ToDictionary(x => x.Key, x => CloneValues(x.Value));
+    }
+
+    protected static Dictionary<string, IList> CloneValues(IEnumerable<string> variableNames, IEnumerable<IList> variableValues) {
+      return variableNames.Zip(variableValues, Tuple.Create).ToDictionary(x => x.Item1, x => CloneValues(x.Item2));
+    }
+
+    protected static IList CloneValues(IList values) {
+      var doubleValues = values as IList<double>;
+      if (doubleValues != null) return new List<double>(doubleValues);
+
+      var stringValues = values as IList<string>;
+      if (stringValues != null) return new List<string>(stringValues);
+
+      var dateTimeValues = values as IList<DateTime>;
+      if (dateTimeValues != null) return new List<DateTime>(dateTimeValues);
+
+      throw new ArgumentException(string.Format("Unsupported variable type {0}.", GetElementType(values)));
     }
 
     #region IStringConvertibleMatrix Members
     [Storable]
-    protected int rows;
+    private int rows;
     public int Rows {
       get { return rows; }
+      protected set { rows = value; }
     }
     int IStringConvertibleMatrix.Rows {
Index: /branches/2904_CalculateImpacts/3.4/Implementation/Classification/ClassificationSolutionVariableImpactsCalculator.cs
===================================================================
--- /branches/2904_CalculateImpacts/3.4/Implementation/Classification/ClassificationSolutionVariableImpactsCalculator.cs	(revision 16187)
+++ /branches/2904_CalculateImpacts/3.4/Implementation/Classification/ClassificationSolutionVariableImpactsCalculator.cs	(revision 16188)
@@ -23,5 +23,4 @@
 
 using System;
-using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
@@ -37,5 +36,4 @@
   [Item("ClassificationSolution Impacts Calculator", "Calculation of the impacts of input variables for any classification solution")]
   public sealed class ClassificationSolutionVariableImpactsCalculator : ParameterizedNamedItem {
-    #region Parameters/Properties
     public enum ReplacementMethodEnum {
       Median,
@@ -56,12 +54,8 @@
 
     private const string ReplacementParameterName = "Replacement Method";
-    private const string FactorReplacementParameterName = "Factor Replacement Method";
     private const string DataPartitionParameterName = "DataPartition";
 
     public IFixedValueParameter<EnumValue<ReplacementMethodEnum>> ReplacementParameter {
       get { return (IFixedValueParameter<EnumValue<ReplacementMethodEnum>>)Parameters[ReplacementParameterName]; }
-    }
-    public IFixedValueParameter<EnumValue<FactorReplacementMethodEnum>> FactorReplacementParameter {
-      get { return (IFixedValueParameter<EnumValue<FactorReplacementMethodEnum>>)Parameters[FactorReplacementParameterName]; }
     }
     public IFixedValueParameter<EnumValue<DataPartitionEnum>> DataPartitionParameter {
@@ -73,152 +67,135 @@
       set { ReplacementParameter.Value.Value = value; }
     }
-    public FactorReplacementMethodEnum FactorReplacementMethod {
-      get { return FactorReplacementParameter.Value.Value; }
-      set { FactorReplacementParameter.Value.Value = value; }
-    }
     public DataPartitionEnum DataPartition {
       get { return DataPartitionParameter.Value.Value; }
       set { DataPartitionParameter.Value.Value = value; }
     }
-    #endregion
-
-    #region Ctor/Cloner
+
+
     [StorableConstructor]
     private ClassificationSolutionVariableImpactsCalculator(bool deserializing) : base(deserializing) { }
     private ClassificationSolutionVariableImpactsCalculator(ClassificationSolutionVariableImpactsCalculator original, Cloner cloner)
       : base(original, cloner) { }
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new ClassificationSolutionVariableImpactsCalculator(this, cloner);
+    }
+
     public ClassificationSolutionVariableImpactsCalculator()
       : base() {
-      Parameters.Add(new FixedValueParameter<EnumValue<ReplacementMethodEnum>>(ReplacementParameterName, "The replacement method for variables during impact calculation.", new EnumValue<ReplacementMethodEnum>(ReplacementMethodEnum.Shuffle)));
-      Parameters.Add(new FixedValueParameter<EnumValue<FactorReplacementMethodEnum>>(FactorReplacementParameterName, "The replacement method for factor variables during impact calculation.", new EnumValue<FactorReplacementMethodEnum>(FactorReplacementMethodEnum.Best)));
+      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)));
     }
-
-    public override IDeepCloneable Clone(Cloner cloner) {
-      return new ClassificationSolutionVariableImpactsCalculator(this, cloner);
-    }
-    #endregion
 
     //mkommend: annoying name clash with static method, open to better naming suggestions
     public IEnumerable<Tuple<string, double>> Calculate(IClassificationSolution solution) {
-      return CalculateImpacts(solution, ReplacementMethod, FactorReplacementMethod, DataPartition);
+      return CalculateImpacts(solution, DataPartition, ReplacementMethod);
     }
 
     public static IEnumerable<Tuple<string, double>> CalculateImpacts(
       IClassificationSolution solution,
-      ReplacementMethodEnum replacementMethod = ReplacementMethodEnum.Shuffle,
-      FactorReplacementMethodEnum factorReplacementMethod = FactorReplacementMethodEnum.Best,
-      DataPartitionEnum dataPartition = DataPartitionEnum.Training) {
-
-      IEnumerable<int> rows = GetPartitionRows(dataPartition, solution.ProblemData);
-      IEnumerable<double> estimatedClassValues = solution.GetEstimatedClassValues(rows);
-      return CalculateImpacts(solution.Model, solution.ProblemData, estimatedClassValues, rows, replacementMethod, factorReplacementMethod);
-    }
-
-    public static IEnumerable<Tuple<string, double>> CalculateImpacts(
-     IClassificationModel model,
-     IClassificationProblemData problemData,
-     IEnumerable<double> estimatedClassValues,
-     IEnumerable<int> rows,
-     ReplacementMethodEnum replacementMethod = ReplacementMethodEnum.Shuffle,
-     FactorReplacementMethodEnum factorReplacementMethod = FactorReplacementMethodEnum.Best) {
-
-      //fholzing: try and catch in case a different dataset is loaded, otherwise statement is neglectable
-      var missingVariables = model.VariablesUsedForPrediction.Except(problemData.Dataset.VariableNames);
-      if (missingVariables.Any()) {
-        throw new InvalidOperationException(string.Format("Can not calculate variable impacts, because the model uses inputs missing in the dataset ({0})", string.Join(", ", missingVariables)));
-      }
-      IEnumerable<double> targetValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, rows);
-      var originalQuality = CalculateQuality(targetValues, estimatedClassValues);
+      DataPartitionEnum data = DataPartitionEnum.Training,
+      ReplacementMethodEnum replacementMethod = ReplacementMethodEnum.Median,
+      FactorReplacementMethodEnum factorReplacementMethod = FactorReplacementMethodEnum.Best) {
+
+      var problemData = solution.ProblemData;
+      var dataset = problemData.Dataset;
+
+      IEnumerable<int> rows;
+      IEnumerable<double> targetValues;
+      double originalAccuracy;
+
+      OnlineCalculatorError error;
+
+      switch (data) {
+        case DataPartitionEnum.All:
+          rows = problemData.AllIndices;
+          targetValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.AllIndices).ToList();
+          originalAccuracy = OnlineAccuracyCalculator.Calculate(targetValues, solution.EstimatedClassValues, out error);
+          if (error != OnlineCalculatorError.None) throw new InvalidOperationException("Error during accuracy calculation.");
+          break;
+        case DataPartitionEnum.Training:
+          rows = problemData.TrainingIndices;
+          targetValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TrainingIndices).ToList();
+          originalAccuracy = OnlineAccuracyCalculator.Calculate(targetValues, solution.EstimatedTrainingClassValues, out error);
+          if (error != OnlineCalculatorError.None) throw new InvalidOperationException("Error during accuracy calculation.");
+          break;
+        case DataPartitionEnum.Test:
+          rows = problemData.TestIndices;
+          targetValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TestIndices).ToList();
+          originalAccuracy = OnlineAccuracyCalculator.Calculate(targetValues, solution.EstimatedTestClassValues, out error);
+          if (error != OnlineCalculatorError.None) throw new InvalidOperationException("Error during accuracy calculation.");
+          break;
+        default: throw new ArgumentException(string.Format("DataPartition {0} cannot be handled.", data));
+      }
 
       var impacts = new Dictionary<string, double>();
-      var inputvariables = new HashSet<string>(problemData.AllowedInputVariables.Union(model.VariablesUsedForPrediction));
-      var modifiableDataset = ((Dataset)(problemData.Dataset).Clone()).ToModifiable();
-
-      foreach (var inputVariable in inputvariables) {
-        impacts[inputVariable] = CalculateImpact(inputVariable, model, problemData, modifiableDataset, rows, replacementMethod, factorReplacementMethod, targetValues, originalQuality);
-      }
-
-      return impacts.Select(i => Tuple.Create(i.Key, i.Value));
-    }
-
-    public static double CalculateImpact(string variableName,
-      IClassificationModel model,
-      IClassificationProblemData problemData,
-      ModifiableDataset modifiableDataset,
-      IEnumerable<int> rows,
-      ReplacementMethodEnum replacementMethod = ReplacementMethodEnum.Shuffle,
-      FactorReplacementMethodEnum factorReplacementMethod = FactorReplacementMethodEnum.Best,
-      IEnumerable<double> targetValues = null,
-      double quality = double.NaN) {
-
-      if (!model.VariablesUsedForPrediction.Contains(variableName)) { return 0.0; }
-      if (!problemData.Dataset.VariableNames.Contains(variableName)) {
-        throw new InvalidOperationException(string.Format("Can not calculate variable impact, because the model uses inputs missing in the dataset ({0})", variableName));
-      }
-
-      if (targetValues == null) {
-        targetValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, rows);
-      }
-      if (quality == double.NaN) {
-        quality = CalculateQuality(model.GetEstimatedClassValues(modifiableDataset, rows), targetValues);
-      }
-
-      IList originalValues = null;
-      IList replacementValues = GetReplacementValues(modifiableDataset, variableName, model, rows, targetValues, out originalValues, replacementMethod, factorReplacementMethod);
-
-      double newValue = CalculateQualityForReplacement(model, modifiableDataset, variableName, originalValues, rows, replacementValues, targetValues);
-      double impact = quality - newValue;
-
-      return impact;
-    }
-
-    private static IList GetReplacementValues(ModifiableDataset modifiableDataset,
-      string variableName,
-      IClassificationModel model,
-      IEnumerable<int> rows,
-      IEnumerable<double> targetValues,
-      out IList originalValues,
-      ReplacementMethodEnum replacementMethod = ReplacementMethodEnum.Shuffle,
-      FactorReplacementMethodEnum factorReplacementMethod = FactorReplacementMethodEnum.Best) {
-
-      IList replacementValues = null;
-      if (modifiableDataset.VariableHasType<double>(variableName)) {
-        originalValues = modifiableDataset.GetReadOnlyDoubleValues(variableName).ToList();
-        replacementValues = GetReplacementValuesForDouble(modifiableDataset, rows, (List<double>)originalValues, replacementMethod);
-      } else if (modifiableDataset.VariableHasType<string>(variableName)) {
-        originalValues = modifiableDataset.GetReadOnlyStringValues(variableName).ToList();
-        replacementValues = GetReplacementValuesForString(model, modifiableDataset, variableName, rows, (List<string>)originalValues, targetValues, factorReplacementMethod);
-      } else {
-        throw new NotSupportedException("Variable not supported");
-      }
-
-      return replacementValues;
-    }
-
-    private static IList GetReplacementValuesForDouble(ModifiableDataset modifiableDataset,
-      IEnumerable<int> rows,
-      List<double> originalValues,
-      ReplacementMethodEnum replacementMethod = ReplacementMethodEnum.Shuffle) {
-
-      IRandom random = new FastRandom(31415);
+      var modifiableDataset = ((Dataset)dataset).ToModifiable();
+
+      var inputvariables = new HashSet<string>(problemData.AllowedInputVariables.Union(solution.Model.VariablesUsedForPrediction));
+      var allowedInputVariables = dataset.VariableNames.Where(v => inputvariables.Contains(v)).ToList();
+
+      // calculate impacts for double variables
+      foreach (var inputVariable in allowedInputVariables.Where(problemData.Dataset.VariableHasType<double>)) {
+        var newEstimates = EvaluateModelWithReplacedVariable(solution.Model, inputVariable, modifiableDataset, rows, replacementMethod);
+        var newAccuracy = OnlineAccuracyCalculator.Calculate(targetValues, newEstimates, out error);
+        if (error != OnlineCalculatorError.None) throw new InvalidOperationException("Error during R² calculation with replaced inputs.");
+
+        impacts[inputVariable] = originalAccuracy - newAccuracy;
+      }
+
+      // calculate impacts for string variables
+      foreach (var inputVariable in allowedInputVariables.Where(problemData.Dataset.VariableHasType<string>)) {
+        if (factorReplacementMethod == FactorReplacementMethodEnum.Best) {
+          // try replacing with all possible values and find the best replacement value
+          var smallestImpact = double.PositiveInfinity;
+          foreach (var repl in problemData.Dataset.GetStringValues(inputVariable, rows).Distinct()) {
+            var newEstimates = EvaluateModelWithReplacedVariable(solution.Model, inputVariable, modifiableDataset, rows,
+              Enumerable.Repeat(repl, dataset.Rows));
+            var newAccuracy = OnlineAccuracyCalculator.Calculate(targetValues, newEstimates, out error);
+            if (error != OnlineCalculatorError.None)
+              throw new InvalidOperationException("Error during accuracy calculation with replaced inputs.");
+
+            var impact = originalAccuracy - newAccuracy;
+            if (impact < smallestImpact) smallestImpact = impact;
+          }
+          impacts[inputVariable] = smallestImpact;
+        } else {
+          // for replacement methods shuffle and mode
+          // calculate impacts for factor variables
+
+          var newEstimates = EvaluateModelWithReplacedVariable(solution.Model, inputVariable, modifiableDataset, rows,
+            factorReplacementMethod);
+          var newAccuracy = OnlineAccuracyCalculator.Calculate(targetValues, newEstimates, out error);
+          if (error != OnlineCalculatorError.None)
+            throw new InvalidOperationException("Error during accuracy calculation with replaced inputs.");
+
+          impacts[inputVariable] = originalAccuracy - newAccuracy;
+        }
+      } // foreach
+      return impacts.OrderByDescending(i => i.Value).Select(i => Tuple.Create(i.Key, i.Value));
+    }
+
+    private static IEnumerable<double> EvaluateModelWithReplacedVariable(IClassificationModel model, string variable, ModifiableDataset dataset, IEnumerable<int> rows, ReplacementMethodEnum replacement = ReplacementMethodEnum.Median) {
+      var originalValues = dataset.GetReadOnlyDoubleValues(variable).ToList();
+      double replacementValue;
       List<double> replacementValues;
-      double replacementValue;
-
-      switch (replacementMethod) {
+      IRandom rand;
+
+      switch (replacement) {
         case ReplacementMethodEnum.Median:
           replacementValue = rows.Select(r => originalValues[r]).Median();
-          replacementValues = Enumerable.Repeat(replacementValue, modifiableDataset.Rows).ToList();
+          replacementValues = Enumerable.Repeat(replacementValue, dataset.Rows).ToList();
           break;
         case ReplacementMethodEnum.Average:
           replacementValue = rows.Select(r => originalValues[r]).Average();
-          replacementValues = Enumerable.Repeat(replacementValue, modifiableDataset.Rows).ToList();
+          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);
           // prepare a complete column for the dataset
-          replacementValues = Enumerable.Repeat(double.NaN, modifiableDataset.Rows).ToList();
+          replacementValues = Enumerable.Repeat(double.NaN, dataset.Rows).ToList();
           // shuffle only the selected rows
-          var shuffledValues = rows.Select(r => originalValues[r]).Shuffle(random).ToList();
+          var shuffledValues = rows.Select(r => originalValues[r]).Shuffle(rand).ToList();
           int i = 0;
           // update column values 
@@ -230,46 +207,29 @@
           var avg = rows.Select(r => originalValues[r]).Average();
           var stdDev = rows.Select(r => originalValues[r]).StandardDeviation();
+          rand = new FastRandom(31415);
           // prepare a complete column for the dataset
-          replacementValues = Enumerable.Repeat(double.NaN, modifiableDataset.Rows).ToList();
+          replacementValues = Enumerable.Repeat(double.NaN, dataset.Rows).ToList();
           // update column values 
           foreach (var r in rows) {
-            replacementValues[r] = NormalDistributedRandom.NextDouble(random, avg, stdDev);
+            replacementValues[r] = NormalDistributedRandom.NextDouble(rand, avg, stdDev);
           }
           break;
 
         default:
-          throw new ArgumentException(string.Format("ReplacementMethod {0} cannot be handled.", replacementMethod));
-      }
-
-      return replacementValues;
-    }
-
-    private static IList GetReplacementValuesForString(IClassificationModel model,
-      ModifiableDataset modifiableDataset,
-      string variableName,
+          throw new ArgumentException(string.Format("ReplacementMethod {0} cannot be handled.", replacement));
+      }
+
+      return EvaluateModelWithReplacedVariable(model, variable, dataset, rows, replacementValues);
+    }
+
+    private static IEnumerable<double> EvaluateModelWithReplacedVariable(
+      IClassificationModel model, string variable, ModifiableDataset dataset,
       IEnumerable<int> rows,
-      List<string> originalValues,
-      IEnumerable<double> targetValues,
-      FactorReplacementMethodEnum factorReplacementMethod = FactorReplacementMethodEnum.Shuffle) {
-
-      List<string> replacementValues = null;
-      IRandom random = new FastRandom(31415);
-
-      switch (factorReplacementMethod) {
-        case FactorReplacementMethodEnum.Best:
-          // try replacing with all possible values and find the best replacement value
-          var bestQuality = double.NegativeInfinity;
-          foreach (var repl in modifiableDataset.GetStringValues(variableName, rows).Distinct()) {
-            List<string> curReplacementValues = Enumerable.Repeat(repl, modifiableDataset.Rows).ToList();
-            //fholzing: this result could be used later on (theoretically), but is neglected for better readability/method consistency 
-            var newValue = CalculateQualityForReplacement(model, modifiableDataset, variableName, originalValues, rows, curReplacementValues, targetValues);
-            var curQuality = newValue;
-
-            if (curQuality > bestQuality) {
-              bestQuality = curQuality;
-              replacementValues = curReplacementValues;
-            }
-          }
-          break;
+      FactorReplacementMethodEnum replacement = FactorReplacementMethodEnum.Shuffle) {
+      var originalValues = dataset.GetReadOnlyStringValues(variable).ToList();
+      List<string> replacementValues;
+      IRandom rand;
+
+      switch (replacement) {
         case FactorReplacementMethodEnum.Mode:
           var mostCommonValue = rows.Select(r => originalValues[r])
@@ -277,12 +237,13 @@
             .OrderByDescending(g => g.Count())
             .First().Key;
-          replacementValues = Enumerable.Repeat(mostCommonValue, modifiableDataset.Rows).ToList();
+          replacementValues = Enumerable.Repeat(mostCommonValue, dataset.Rows).ToList();
           break;
         case FactorReplacementMethodEnum.Shuffle:
           // new var has same empirical distribution but the relation to y is broken
+          rand = new FastRandom(31415);
           // prepare a complete column for the dataset
-          replacementValues = Enumerable.Repeat(string.Empty, modifiableDataset.Rows).ToList();
+          replacementValues = Enumerable.Repeat(string.Empty, dataset.Rows).ToList();
           // shuffle only the selected rows
-          var shuffledValues = rows.Select(r => originalValues[r]).Shuffle(random).ToList();
+          var shuffledValues = rows.Select(r => originalValues[r]).Shuffle(rand).ToList();
           int i = 0;
           // update column values 
@@ -292,59 +253,29 @@
           break;
         default:
-          throw new ArgumentException(string.Format("FactorReplacementMethod {0} cannot be handled.", factorReplacementMethod));
-      }
-
-      return replacementValues;
-    }
-
-    private static double CalculateQualityForReplacement(
-      IClassificationModel model,
-      ModifiableDataset modifiableDataset,
-      string variableName,
-      IList originalValues,
-      IEnumerable<int> rows,
-      IList replacementValues,
-      IEnumerable<double> targetValues) {
-
-      modifiableDataset.ReplaceVariable(variableName, replacementValues);
-      var discModel = model as IDiscriminantFunctionClassificationModel;
-      if (discModel != null) {
-        var problemData = new ClassificationProblemData(modifiableDataset, modifiableDataset.VariableNames, model.TargetVariable);
-        discModel.RecalculateModelParameters(problemData, rows);
-      }
-
+          throw new ArgumentException(string.Format("FactorReplacementMethod {0} cannot be handled.", replacement));
+      }
+
+      return EvaluateModelWithReplacedVariable(model, variable, dataset, rows, replacementValues);
+    }
+
+    private static IEnumerable<double> EvaluateModelWithReplacedVariable(IClassificationModel model, string variable,
+      ModifiableDataset dataset, IEnumerable<int> rows, IEnumerable<double> replacementValues) {
+      var originalValues = dataset.GetReadOnlyDoubleValues(variable).ToList();
+      dataset.ReplaceVariable(variable, replacementValues.ToList());
       //mkommend: ToList is used on purpose to avoid lazy evaluation that could result in wrong estimates due to variable replacements
-      var estimates = model.GetEstimatedClassValues(modifiableDataset, rows).ToList();
-      var ret = CalculateQuality(targetValues, estimates);
-      modifiableDataset.ReplaceVariable(variableName, originalValues);
-
-      return ret;
-    }
-
-    public static double CalculateQuality(IEnumerable<double> targetValues, IEnumerable<double> estimatedClassValues) {
-      OnlineCalculatorError errorState;
-      var ret = OnlineAccuracyCalculator.Calculate(targetValues, estimatedClassValues, out errorState);
-      if (errorState != OnlineCalculatorError.None) { throw new InvalidOperationException("Error during calculation with replaced inputs."); }
-      return ret;
-    }
-
-    public static IEnumerable<int> GetPartitionRows(DataPartitionEnum dataPartition, IClassificationProblemData problemData) {
-      IEnumerable<int> rows;
-
-      switch (dataPartition) {
-        case DataPartitionEnum.All:
-          rows = problemData.AllIndices;
-          break;
-        case DataPartitionEnum.Test:
-          rows = problemData.TestIndices;
-          break;
-        case DataPartitionEnum.Training:
-          rows = problemData.TrainingIndices;
-          break;
-        default:
-          throw new NotSupportedException("DataPartition not supported");
-      }
-
-      return rows;
+      var estimates = model.GetEstimatedClassValues(dataset, rows).ToList();
+      dataset.ReplaceVariable(variable, originalValues);
+
+      return estimates;
+    }
+    private static IEnumerable<double> EvaluateModelWithReplacedVariable(IClassificationModel model, string variable,
+      ModifiableDataset dataset, IEnumerable<int> rows, IEnumerable<string> replacementValues) {
+      var originalValues = dataset.GetReadOnlyStringValues(variable).ToList();
+      dataset.ReplaceVariable(variable, replacementValues.ToList());
+      //mkommend: ToList is used on purpose to avoid lazy evaluation that could result in wrong estimates due to variable replacements
+      var estimates = model.GetEstimatedClassValues(dataset, rows).ToList();
+      dataset.ReplaceVariable(variable, originalValues);
+
+      return estimates;
     }
   }
Index: /branches/2904_CalculateImpacts/3.4/Implementation/DataAnalysisProblemData.cs
===================================================================
--- /branches/2904_CalculateImpacts/3.4/Implementation/DataAnalysisProblemData.cs	(revision 16187)
+++ /branches/2904_CalculateImpacts/3.4/Implementation/DataAnalysisProblemData.cs	(revision 16188)
@@ -163,5 +163,5 @@
 
       var variables = dataset.VariableNames.Where(variable => dataset.VariableHasType<double>(variable) || dataset.VariableHasType<string>(variable));
-      var inputVariables = new CheckedItemList<StringValue>(variables.Select(x => new StringValue(x)));
+      var inputVariables = new CheckedItemList<StringValue>(variables.Select(x => new StringValue(x).AsReadOnly()));
       foreach (StringValue x in inputVariables)
         inputVariables.SetItemCheckedState(x, allowedInputVariables.Contains(x.Value));
Index: /branches/2904_CalculateImpacts/3.4/ModifiableDataset.cs
===================================================================
--- /branches/2904_CalculateImpacts/3.4/ModifiableDataset.cs	(revision 16187)
+++ /branches/2904_CalculateImpacts/3.4/ModifiableDataset.cs	(revision 16188)
@@ -39,22 +39,42 @@
 
     private ModifiableDataset(ModifiableDataset original, Cloner cloner) : base(original, cloner) {
-      var variables = variableValues.Keys.ToList();
-      foreach (var v in variables) {
-        var type = GetVariableType(v);
-        if (type == typeof(DateTime)) {
-          variableValues[v] = GetDateTimeValues(v).ToList();
-        } else if (type == typeof(double)) {
-          variableValues[v] = GetDoubleValues(v).ToList();
-        } else if (type == typeof(string)) {
-          variableValues[v] = GetStringValues(v).ToList();
-        } else {
-          throw new ArgumentException("Unsupported type " + type + " for variable " + v);
+      variableNames = new List<string>(original.variableNames);
+      variableValues = CloneValues(original.variableValues);
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) { return new ModifiableDataset(this, cloner); }
+
+    public ModifiableDataset() { }
+
+    public ModifiableDataset(IEnumerable<string> variableNames, IEnumerable<IList> variableValues, bool cloneValues = false) :
+      base(variableNames, variableValues, cloneValues) { }
+
+    public Dataset ToDataset() {
+      return new Dataset(variableNames, variableNames.Select(v => variableValues[v]));
+    }
+
+
+    public IEnumerable<object> GetRow(int row) {
+      return variableValues.Select(x => x.Value[row]);
+    }
+
+    public void AddRow(IEnumerable<object> values) {
+      var list = values.ToList();
+      if (list.Count != variableNames.Count)
+        throw new ArgumentException("The number of values must be equal to the number of variable names.");
+      // check if all the values are of the correct type
+      for (int i = 0; i < list.Count; ++i) {
+        if (list[i].GetType() != GetVariableType(variableNames[i])) {
+          throw new ArgumentException("The type of the provided value does not match the variable type.");
         }
       }
-    }
-    public override IDeepCloneable Clone(Cloner cloner) { return new ModifiableDataset(this, cloner); }
-    public ModifiableDataset() : base() { }
-
-    public ModifiableDataset(IEnumerable<string> variableNames, IEnumerable<IList> variableValues) : base(variableNames, variableValues) { }
+      // add values
+      for (int i = 0; i < list.Count; ++i) {
+        variableValues[variableNames[i]].Add(list[i]);
+      }
+      Rows++;
+      OnRowsChanged();
+      OnReset();
+    }
 
     public void ReplaceRow(int row, IEnumerable<object> values) {
@@ -72,4 +92,42 @@
         variableValues[variableNames[i]][row] = list[i];
       }
+      OnReset();
+    }
+
+    // slow, avoid using this
+    public void RemoveRow(int row) {
+      foreach (var list in variableValues.Values)
+        list.RemoveAt(row);
+      Rows--;
+      OnRowsChanged();
+      OnReset();
+    }
+
+    // adds a new variable to the dataset
+    public void AddVariable(string variableName, IList values) {
+      InsertVariable(variableName, Columns, values);
+    }
+
+    public void InsertVariable(string variableName, int position, IList values) {
+      if (variableValues.ContainsKey(variableName))
+        throw new ArgumentException(string.Format("Variable {0} is already present in the dataset.", variableName));
+
+      if (position < 0 || position > Columns)
+        throw new ArgumentException(string.Format("Incorrect position {0} specified. The position must be between 0 and {1}.", position, Columns));
+
+      if (values == null)
+        throw new ArgumentNullException("values", "Values must not be null. At least an empty list of values has to be provided.");
+
+      if (values.Count != Rows)
+        throw new ArgumentException(string.Format("{0} values are provided, but {1} rows are present in the dataset.", values.Count, Rows));
+
+      if (!IsAllowedType(values))
+        throw new ArgumentException(string.Format("Unsupported type {0} for variable {1}.", GetElementType(values), variableName));
+
+      variableNames.Insert(position, variableName);
+      variableValues[variableName] = values;
+
+      OnColumnsChanged();
+      OnColumnNamesChanged();
       OnReset();
     }
@@ -85,41 +143,4 @@
     }
 
-    public void AddRow(IEnumerable<object> values) {
-      var list = values.ToList();
-      if (list.Count != variableNames.Count)
-        throw new ArgumentException("The number of values must be equal to the number of variable names.");
-      // check if all the values are of the correct type
-      for (int i = 0; i < list.Count; ++i) {
-        if (list[i].GetType() != GetVariableType(variableNames[i])) {
-          throw new ArgumentException("The type of the provided value does not match the variable type.");
-        }
-      }
-      // add values
-      for (int i = 0; i < list.Count; ++i) {
-        variableValues[variableNames[i]].Add(list[i]);
-      }
-      rows++;
-      OnRowsChanged();
-      OnReset();
-    }
-
-    // adds a new variable to the dataset
-    public void AddVariable(string variableName, IList values) {
-      if (variableValues.ContainsKey(variableName))
-        throw new ArgumentException(string.Format("Variable {0} is already present in the dataset.", variableName));
-
-      if (values == null || values.Count == 0)
-        throw new ArgumentException("Cannot add variable with no values.");
-
-      if (!IsAllowedType(values))
-        throw new ArgumentException(string.Format("Unsupported type {0} for variable {1}.", GetElementType(values), variableName));
-
-      variableValues[variableName] = values;
-      variableNames.Add(variableName);
-
-      OnColumnsChanged();
-      OnColumnNamesChanged();
-      OnReset();
-    }
 
     public void RemoveVariable(string variableName) {
@@ -133,12 +154,13 @@
     }
 
-    // slow, avoid using this
-    public void RemoveRow(int row) {
-      foreach (var list in variableValues.Values)
-        list.RemoveAt(row);
-      rows--;
+    public void ClearValues() {
+      foreach (var list in variableValues.Values) {
+        list.Clear();
+      }
+      Rows = 0;
       OnRowsChanged();
       OnReset();
     }
+
 
     public void SetVariableValue(object value, string variableName, int row) {
