Index: /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Dataset.cs
===================================================================
--- /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Dataset.cs	(revision 15029)
+++ /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Dataset.cs	(revision 15030)
@@ -74,5 +74,4 @@
         throw new ArgumentException(message);
       }
-
       rows = variableValues.First().Count;
       this.variableNames = new List<string>(variableNames);
@@ -115,9 +114,9 @@
       foreach (var v in variableNames) {
         if (VariableHasType<double>(v)) {
-          values.Add(new List<double>((List<double>)variableValues[v]));
+          values.Add(new List<double>((IList<double>)variableValues[v]));
         } else if (VariableHasType<string>(v)) {
-          values.Add(new List<string>((List<string>)variableValues[v]));
+          values.Add(new List<string>((IList<string>)variableValues[v]));
         } else if (VariableHasType<DateTime>(v)) {
-          values.Add(new List<DateTime>((List<DateTime>)variableValues[v]));
+          values.Add(new List<DateTime>((IList<DateTime>)variableValues[v]));
         } else {
           throw new ArgumentException("Unknown variable type.");
@@ -125,4 +124,13 @@
       }
       return new ModifiableDataset(variableNames, values);
+    }
+    /// <summary>
+    /// Shuffle a dataset's rows
+    /// </summary>
+    /// <param name="random">Random number generator used for shuffling.</param>
+    /// <returns>A shuffled copy of the current dataset.</returns>
+    public Dataset Shuffle(IRandom random) {
+      var values = variableNames.Select(x => variableValues[x]).ToList();
+      return new Dataset(variableNames, values.ShuffleLists(random));
     }
 
@@ -166,6 +174,11 @@
     }
     public IEnumerable<string> DoubleVariables {
-      get { return variableValues.Where(p => p.Value is List<double>).Select(p => p.Key); }
-    }
+      get { return variableValues.Where(p => p.Value is IList<double>).Select(p => p.Key); }
+    }
+
+    public IEnumerable<string> StringVariables {
+      get { return variableValues.Where(p => p.Value is IList<string>).Select(p => p.Key); }
+    }
+
     public IEnumerable<double> GetDoubleValues(string variableName) {
       return GetValues<double>(variableName);
@@ -180,5 +193,5 @@
     public ReadOnlyCollection<double> GetReadOnlyDoubleValues(string variableName) {
       var values = GetValues<double>(variableName);
-      return values.AsReadOnly();
+      return new ReadOnlyCollection<double>(values);
     }
     public double GetDoubleValue(string variableName, int row) {
@@ -189,13 +202,27 @@
       return GetValues<double>(variableName, rows);
     }
+
+    public string GetStringValue(string variableName, int row) {
+      var values = GetValues<string>(variableName);
+      return values[row];
+    }
+
+    public IEnumerable<string> GetStringValues(string variableName, IEnumerable<int> rows) {
+      return GetValues<string>(variableName, rows);
+    }
+    public ReadOnlyCollection<string> GetReadOnlyStringValues(string variableName) {
+      var values = GetValues<string>(variableName);
+      return new ReadOnlyCollection<string>(values);
+    }
+
     private IEnumerable<T> GetValues<T>(string variableName, IEnumerable<int> rows) {
       var values = GetValues<T>(variableName);
       return rows.Select(x => values[x]);
     }
-    private List<T> GetValues<T>(string variableName) {
+    private IList<T> GetValues<T>(string variableName) {
       IList list;
       if (!variableValues.TryGetValue(variableName, out list))
         throw new ArgumentException("The variable " + variableName + " does not exist in the dataset.");
-      List<T> values = list as List<T>;
+      IList<T> values = list as IList<T>;
       if (values == null) throw new ArgumentException("The variable " + variableName + " is not a " + typeof(T) + " variable.");
       return values;
Index: /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/DatasetExtensions.cs
===================================================================
--- /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/DatasetExtensions.cs	(revision 15029)
+++ /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/DatasetExtensions.cs	(revision 15030)
@@ -20,14 +20,91 @@
 #endregion
 
+using System;
 using System.Collections.Generic;
+using System.Linq;
 
 namespace HeuristicLab.Problems.DataAnalysis {
   public static class DatasetExtensions {
-    public static IEnumerable<T> TakeEvery<T>(this IEnumerable<T> xs, int nth) {
-      int i = 0;
-      foreach (var x in xs) {
-        if (i % nth == 0) yield return x;
-        i++;
+    public static double[,] ToArray(this IDataset dataset, IEnumerable<string> variables, IEnumerable<int> rows) {
+      return ToArray(dataset,
+        variables,
+        transformations: variables.Select(_ => (ITransformation<double>)null), // no transform
+        rows: rows);
+    }
+    public static double[,] ToArray(this IDataset dataset, IEnumerable<string> variables,
+      IEnumerable<ITransformation<double>> transformations, IEnumerable<int> rows) {
+      string[] variablesArr = variables.ToArray();
+      int[] rowsArr = rows.ToArray();
+      ITransformation<double>[] transformArr = transformations.ToArray();
+      if (transformArr.Length != variablesArr.Length)
+        throw new ArgumentException("Number of variables and number of transformations must match.");
+
+      double[,] matrix = new double[rowsArr.Length, variablesArr.Length];
+
+      for (int i = 0; i < variablesArr.Length; i++) {
+        var origValues = dataset.GetDoubleValues(variablesArr[i], rowsArr);
+        var values = transformArr[i] != null ? transformArr[i].Apply(origValues) : origValues;
+        int row = 0;
+        foreach (var value in values) {
+          matrix[row, i] = value;
+          row++;
+        }
       }
+
+      return matrix;
+    }
+
+    /// <summary>
+    /// Prepares a binary data matrix from a number of factors and specified factor values
+    /// </summary>
+    /// <param name="dataset">A dataset that contains the variable values</param>
+    /// <param name="factorVariables">An enumerable of categorical variables (factors). For each variable an enumerable of values must be specified.</param>
+    /// <param name="rows">An enumerable of row indices for the dataset</param>
+    /// <returns></returns>
+    /// <remarks>Factor variables (categorical variables) are split up into multiple binary variables one for each specified value.</remarks>
+    public static double[,] ToArray(
+      this IDataset dataset,
+      IEnumerable<KeyValuePair<string, IEnumerable<string>>> factorVariables,
+      IEnumerable<int> rows) {
+      // check input variables. Only string variables are allowed.
+      var invalidInputs =
+        factorVariables.Select(kvp => kvp.Key).Where(name => !dataset.VariableHasType<string>(name));
+      if (invalidInputs.Any())
+        throw new NotSupportedException("Unsupported inputs: " + string.Join(", ", invalidInputs));
+
+      int numBinaryColumns = factorVariables.Sum(kvp => kvp.Value.Count());
+
+      List<int> rowsList = rows.ToList();
+      double[,] matrix = new double[rowsList.Count, numBinaryColumns];
+
+      int col = 0;
+      foreach (var kvp in factorVariables) {
+        var varName = kvp.Key;
+        var cats = kvp.Value;
+        if (!cats.Any()) continue;
+        foreach (var cat in cats) {
+          var values = dataset.GetStringValues(varName, rows);
+          int row = 0;
+          foreach (var value in values) {
+            matrix[row, col] = value == cat ? 1 : 0;
+            row++;
+          }
+          col++;
+        }
+      }
+      return matrix;
+    }
+
+    public static IEnumerable<KeyValuePair<string, IEnumerable<string>>> GetFactorVariableValues(
+      this IDataset ds, IEnumerable<string> factorVariables, IEnumerable<int> rows) {
+      return from factor in factorVariables
+             let distinctValues = ds.GetStringValues(factor, rows).Distinct().ToArray()
+             // 1 distinct value => skip (constant)
+             // 2 distinct values => only take one of the two values
+             // >=3 distinct values => create a binary value for each value
+             let reducedValues = distinctValues.Length <= 2
+               ? distinctValues.Take(distinctValues.Length - 1)
+               : distinctValues
+             select new KeyValuePair<string, IEnumerable<string>>(factor, reducedValues);
     }
   }
Index: /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/DatasetUtil.cs
===================================================================
--- /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/DatasetUtil.cs	(revision 15030)
+++ /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/DatasetUtil.cs	(revision 15030)
@@ -0,0 +1,62 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 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;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using HeuristicLab.Core;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Problems.DataAnalysis {
+  public static class DatasetUtil {
+    /// <summary>
+    /// Shuffle all the lists with the same shuffling.
+    /// </summary>
+    /// <param name="values">The value lists to be shuffled.</param>
+    /// <param name="random">The random number generator</param>
+    /// <returns>A new list containing shuffled copies of the original value lists.</returns>
+    public static List<IList> ShuffleLists(this List<IList> values, IRandom random) {
+      int count = values.First().Count;
+      int[] indices = Enumerable.Range(0, count).Shuffle(random).ToArray();
+      List<IList> shuffled = new List<IList>(values.Count);
+      for (int col = 0; col < values.Count; col++) {
+
+        if (values[col] is IList<double>)
+          shuffled.Add(new List<double>());
+        else if (values[col] is IList<DateTime>)
+          shuffled.Add(new List<DateTime>());
+        else if (values[col] is IList<string>)
+          shuffled.Add(new List<string>());
+        else
+          throw new InvalidOperationException();
+
+        for (int i = 0; i < count; i++) {
+          shuffled[col].Add(values[col][indices[i]]);
+        }
+      }
+      return shuffled;
+
+    }
+  }
+}
Index: /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/HeuristicLab.Problems.DataAnalysis-3.4.csproj
===================================================================
--- /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/HeuristicLab.Problems.DataAnalysis-3.4.csproj	(revision 15029)
+++ /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/HeuristicLab.Problems.DataAnalysis-3.4.csproj	(revision 15030)
@@ -151,4 +151,5 @@
   <ItemGroup>
     <Compile Include="DatasetExtensions.cs" />
+    <Compile Include="DatasetUtil.cs" />
     <Compile Include="DoubleLimit.cs" />
     <Compile Include="Implementation\Classification\ClassificationEnsembleModel.cs">
Index: /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisProblemData.cs
===================================================================
--- /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisProblemData.cs	(revision 15029)
+++ /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisProblemData.cs	(revision 15030)
@@ -41,16 +41,37 @@
 
     #region parameter properites
+    //mkommend: inserted parameter caching due to performance reasons
+    private IFixedValueParameter<Dataset> datasetParameter;
     public IFixedValueParameter<Dataset> DatasetParameter {
-      get { return (IFixedValueParameter<Dataset>)Parameters[DatasetParameterName]; }
-    }
+      get {
+        if (datasetParameter == null) datasetParameter = (IFixedValueParameter<Dataset>)Parameters[DatasetParameterName];
+        return datasetParameter;
+      }
+    }
+
+    private IFixedValueParameter<ReadOnlyCheckedItemList<StringValue>> inputVariablesParameter;
     public IFixedValueParameter<ReadOnlyCheckedItemList<StringValue>> InputVariablesParameter {
-      get { return (IFixedValueParameter<ReadOnlyCheckedItemList<StringValue>>)Parameters[InputVariablesParameterName]; }
-    }
+      get {
+        if (inputVariablesParameter == null) inputVariablesParameter = (IFixedValueParameter<ReadOnlyCheckedItemList<StringValue>>)Parameters[InputVariablesParameterName];
+        return inputVariablesParameter;
+      }
+    }
+
+    private IFixedValueParameter<IntRange> trainingPartitionParameter;
     public IFixedValueParameter<IntRange> TrainingPartitionParameter {
-      get { return (IFixedValueParameter<IntRange>)Parameters[TrainingPartitionParameterName]; }
-    }
+      get {
+        if (trainingPartitionParameter == null) trainingPartitionParameter = (IFixedValueParameter<IntRange>)Parameters[TrainingPartitionParameterName];
+        return trainingPartitionParameter;
+      }
+    }
+
+    private IFixedValueParameter<IntRange> testPartitionParameter;
     public IFixedValueParameter<IntRange> TestPartitionParameter {
-      get { return (IFixedValueParameter<IntRange>)Parameters[TestPartitionParameterName]; }
-    }
+      get {
+        if (testPartitionParameter == null) testPartitionParameter = (IFixedValueParameter<IntRange>)Parameters[TestPartitionParameterName];
+        return testPartitionParameter;
+      }
+    }
+
     public IFixedValueParameter<ReadOnlyItemList<ITransformation>> TransformationsParameter {
       get { return (IFixedValueParameter<ReadOnlyItemList<ITransformation>>)Parameters[TransformationsParameterName]; }
@@ -73,4 +94,9 @@
     }
 
+    public double[,] AllowedInputsTrainingValues {
+      get { return Dataset.ToArray(AllowedInputVariables, TrainingIndices); }
+    }
+
+    public double[,] AllowedInputsTestValues { get { return Dataset.ToArray(AllowedInputVariables, TestIndices); } }
     public IntRange TrainingPartition {
       get { return TrainingPartitionParameter.Value; }
@@ -102,6 +128,6 @@
     public virtual bool IsTrainingSample(int index) {
       return index >= 0 && index < Dataset.Rows &&
-        TrainingPartition.Start <= index && index < TrainingPartition.End &&
-        (index < TestPartition.Start || TestPartition.End <= index);
+             TrainingPartition.Start <= index && index < TrainingPartition.End &&
+             (index < TestPartition.Start || TestPartition.End <= index);
     }
 
@@ -131,10 +157,11 @@
     protected DataAnalysisProblemData(IDataset dataset, IEnumerable<string> allowedInputVariables, IEnumerable<ITransformation> transformations = null) {
       if (dataset == null) throw new ArgumentNullException("The dataset must not be null.");
-      if (allowedInputVariables == null) throw new ArgumentNullException("The allowedInputVariables must not be null.");
-
-      if (allowedInputVariables.Except(dataset.DoubleVariables).Any())
-        throw new ArgumentException("All allowed input variables must be present in the dataset and of type double.");
-
-      var inputVariables = new CheckedItemList<StringValue>(dataset.DoubleVariables.Select(x => new StringValue(x)));
+      if (allowedInputVariables == null) throw new ArgumentNullException("The allowed input variables must not be null.");
+
+      if (allowedInputVariables.Except(dataset.DoubleVariables).Except(dataset.StringVariables).Any())
+        throw new ArgumentException("All allowed input variables must be present in the dataset and of type double or string.");
+
+      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)));
       foreach (StringValue x in inputVariables)
         inputVariables.SetItemCheckedState(x, allowedInputVariables.Contains(x.Value));
@@ -214,8 +241,4 @@
         InputVariables.SetItemCheckedState(inputVariable, variable != null && data.InputVariables.ItemChecked(variable));
       }
-
-      TrainingPartition.Start = TrainingPartition.End = 0;
-      TestPartition.Start = 0;
-      TestPartition.End = Dataset.Rows;
     }
   }
Index: /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionSolutionVariableImpactsCalculator.cs
===================================================================
--- /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionSolutionVariableImpactsCalculator.cs	(revision 15029)
+++ /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionSolutionVariableImpactsCalculator.cs	(revision 15030)
@@ -42,5 +42,9 @@
       Noise
     }
-
+    public enum FactorReplacementMethodEnum {
+      Best,
+      Mode,
+      Shuffle
+    }
     public enum DataPartitionEnum {
       Training,
@@ -88,7 +92,9 @@
     }
 
-    public static IEnumerable<Tuple<string, double>> CalculateImpacts(IRegressionSolution solution,
+    public static IEnumerable<Tuple<string, double>> CalculateImpacts(
+      IRegressionSolution solution,
       DataPartitionEnum data = DataPartitionEnum.Training,
-      ReplacementMethodEnum replacement = ReplacementMethodEnum.Median) {
+      ReplacementMethodEnum replacementMethod = ReplacementMethodEnum.Median,
+      FactorReplacementMethodEnum factorReplacementMethod = FactorReplacementMethodEnum.Best) {
 
       var problemData = solution.ProblemData;
@@ -128,6 +134,7 @@
       var allowedInputVariables = dataset.VariableNames.Where(v => inputvariables.Contains(v)).ToList();
 
-      foreach (var inputVariable in allowedInputVariables) {
-        var newEstimates = EvaluateModelWithReplacedVariable(solution.Model, inputVariable, modifiableDataset, rows, replacement);
+      // 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 newR2 = OnlinePearsonsRCalculator.Calculate(targetValues, newEstimates, out error);
         if (error != OnlineCalculatorError.None) throw new InvalidOperationException("Error during R² calculation with replaced inputs.");
@@ -137,4 +144,37 @@
         impacts[inputVariable] = impact;
       }
+
+      // 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 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;
+            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 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;
+        }
+      } // foreach
       return impacts.OrderByDescending(i => i.Value).Select(i => Tuple.Create(i.Key, i.Value));
     }
@@ -184,5 +224,47 @@
       }
 
-      dataset.ReplaceVariable(variable, replacementValues);
+      return EvaluateModelWithReplacedVariable(model, variable, dataset, rows, replacementValues);
+    }
+
+    private static IEnumerable<double> EvaluateModelWithReplacedVariable(
+      IRegressionModel model, string variable, ModifiableDataset dataset,
+      IEnumerable<int> rows,
+      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])
+            .GroupBy(v => v)
+            .OrderByDescending(g => g.Count())
+            .First().Key;
+          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, dataset.Rows).ToList();
+          // shuffle only the selected rows
+          var shuffledValues = rows.Select(r => originalValues[r]).Shuffle(rand).ToList();
+          int i = 0;
+          // update column values 
+          foreach (var r in rows) {
+            replacementValues[r] = shuffledValues[i++];
+          }
+          break;
+        default:
+          throw new ArgumentException(string.Format("FactorReplacementMethod {0} cannot be handled.", replacement));
+      }
+
+      return EvaluateModelWithReplacedVariable(model, variable, dataset, rows, replacementValues);
+    }
+
+    private static IEnumerable<double> EvaluateModelWithReplacedVariable(IRegressionModel 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.GetEstimatedValues(dataset, rows).ToList();
@@ -191,4 +273,14 @@
       return estimates;
     }
+    private static IEnumerable<double> EvaluateModelWithReplacedVariable(IRegressionModel 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.GetEstimatedValues(dataset, rows).ToList();
+      dataset.ReplaceVariable(variable, originalValues);
+
+      return estimates;
+    }
   }
 }
Index: /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Transformations/LinearTransformation.cs
===================================================================
--- /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Transformations/LinearTransformation.cs	(revision 15029)
+++ /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Transformations/LinearTransformation.cs	(revision 15030)
@@ -52,5 +52,5 @@
     public double Multiplier {
       get { return MultiplierParameter.Value.Value; }
-      protected set {
+      set {
         MultiplierParameter.Value.Value = value;
       }
@@ -59,5 +59,5 @@
     public double Addend {
       get { return AddendParameter.Value.Value; }
-      protected set {
+      set {
         AddendParameter.Value.Value = value;
       }
Index: /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Transformations/ShiftStandardDistributionTransformation.cs
===================================================================
--- /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Transformations/ShiftStandardDistributionTransformation.cs	(revision 15029)
+++ /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Transformations/ShiftStandardDistributionTransformation.cs	(revision 15030)
@@ -71,5 +71,4 @@
 
     public override IEnumerable<double> Apply(IEnumerable<double> data) {
-      ConfigureParameters(data);
       if (OriginalStandardDeviation.IsAlmost(0.0)) {
         return data;
@@ -94,5 +93,5 @@
     }
 
-    protected void ConfigureParameters(IEnumerable<double> data) {
+    public override void ConfigureParameters(IEnumerable<double> data) {
       OriginalStandardDeviation = data.StandardDeviation();
       OriginalMean = data.Average();
Index: /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Transformations/ShiftToRangeTransformation.cs
===================================================================
--- /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Transformations/ShiftToRangeTransformation.cs	(revision 15029)
+++ /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Transformations/ShiftToRangeTransformation.cs	(revision 15030)
@@ -44,9 +44,4 @@
     }
 
-    public override IEnumerable<double> Apply(IEnumerable<double> data) {
-      ConfigureParameters(data);
-      return base.Apply(data);
-    }
-
     public override bool Check(IEnumerable<double> data, out string errorMsg) {
       ConfigureParameters(data);
@@ -54,5 +49,5 @@
     }
 
-    protected void ConfigureParameters(IEnumerable<double> data) {
+    public override void ConfigureParameters(IEnumerable<double> data) {
       double originalRangeStart = data.Min();
       double originalRangeEnd = data.Max();
Index: /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Transformations/Transformation.cs
===================================================================
--- /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Transformations/Transformation.cs	(revision 15029)
+++ /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Transformations/Transformation.cs	(revision 15030)
@@ -66,5 +66,13 @@
     protected Transformation(IEnumerable<string> allowedColumns) : base(allowedColumns) { }
 
+    public virtual void ConfigureParameters(IEnumerable<T> data) {
+      // override in transformations with parameters
+    }
+
     public abstract IEnumerable<T> Apply(IEnumerable<T> data);
+    public IEnumerable<T> ConfigureAndApply(IEnumerable<T> data) {
+      ConfigureParameters(data);
+      return Apply(data);
+    }
 
     public abstract bool Check(IEnumerable<T> data, out string errorMsg);
Index: /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Interfaces/IDataAnalysisProblemData.cs
===================================================================
--- /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Interfaces/IDataAnalysisProblemData.cs	(revision 15029)
+++ /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Interfaces/IDataAnalysisProblemData.cs	(revision 15030)
@@ -33,4 +33,7 @@
     IEnumerable<string> AllowedInputVariables { get; }
 
+    double[,] AllowedInputsTrainingValues { get; }
+    double[,] AllowedInputsTestValues { get; }
+
     IntRange TrainingPartition { get; }
     IntRange TestPartition { get; }
Index: /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Interfaces/IDataset.cs
===================================================================
--- /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Interfaces/IDataset.cs	(revision 15029)
+++ /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Interfaces/IDataset.cs	(revision 15030)
@@ -30,4 +30,7 @@
     IEnumerable<string> VariableNames { get; }
     IEnumerable<string> DoubleVariables { get; }
+    IEnumerable<string> StringVariables { get; }
+
+    bool VariableHasType<T>(string variableName);
 
     double GetDoubleValue(string variableName, int row);
@@ -36,5 +39,9 @@
     ReadOnlyCollection<double> GetReadOnlyDoubleValues(string variableName);
 
+    string GetStringValue(string variableName, int row);
     IEnumerable<string> GetStringValues(string variableName);
+    IEnumerable<string> GetStringValues(string variableName, IEnumerable<int> rows);
+    ReadOnlyCollection<string> GetReadOnlyStringValues(string VariableName);
+
     IEnumerable<DateTime> GetDateTimeValues(string variableName);
   }
Index: /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Interfaces/ITransformation.cs
===================================================================
--- /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Interfaces/ITransformation.cs	(revision 15029)
+++ /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/Interfaces/ITransformation.cs	(revision 15030)
@@ -30,4 +30,6 @@
 
   public interface ITransformation<T> : ITransformation {
+    void ConfigureParameters(IEnumerable<T> data);
+    IEnumerable<T> ConfigureAndApply(IEnumerable<T> data);
     IEnumerable<T> Apply(IEnumerable<T> data);
   }
Index: /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/ModifiableDataset.cs
===================================================================
--- /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/ModifiableDataset.cs	(revision 15029)
+++ /branches/HeuristicLab.DatastreamAnalysis/HeuristicLab.Problems.DataAnalysis/3.4/ModifiableDataset.cs	(revision 15030)
@@ -77,5 +77,5 @@
     public void ReplaceVariable(string variableName, IList values) {
       if (!variableValues.ContainsKey(variableName))
-        throw new ArgumentException(string.Format("Variable {0} is not present in the dataset."), variableName);
+        throw new ArgumentException(string.Format("Variable {0} is not present in the dataset.", variableName));
       if (values.Count != variableValues[variableName].Count)
         throw new ArgumentException("The number of values must coincide with the number of dataset rows.");
