Index: branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/Data/Columns/DateTimePreprocessingDataColumn.cs
===================================================================
--- branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/Data/Columns/DateTimePreprocessingDataColumn.cs	(revision 15291)
+++ branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/Data/Columns/DateTimePreprocessingDataColumn.cs	(revision 15291)
@@ -0,0 +1,94 @@
+﻿#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.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.DataPreprocessing {
+  [Item("DateTimePreprocessingDataColumn", "")]
+  public class DateTimePreprocessingDataColumn : NullablePreprocessingDataColumn<DateTime, TimeSpan> {
+
+    #region Constructor, Cloning & Persistence
+    public DateTimePreprocessingDataColumn()
+      : base() { }
+    public DateTimePreprocessingDataColumn(string name, IEnumerable<DateTime> values)
+      : base(name, values) {
+    }
+    public DateTimePreprocessingDataColumn(string name, IEnumerable<DateTime?> values)
+      : base(name, values) {
+    }
+
+    private DateTimePreprocessingDataColumn(DateTimePreprocessingDataColumn original, Cloner cloner)
+      : base(original, cloner) {
+    }
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new DateTimePreprocessingDataColumn(this, cloner);
+    }
+
+    [StorableConstructor]
+    private DateTimePreprocessingDataColumn(bool deserializing)
+      : base(deserializing) { }
+    #endregion
+
+    #region Statistics
+    public override TimeSpan GetRange() { return ValidValues.Max() - ValidValues.Min(); }
+    public override DateTime GetMean() { return AggregateAsDouble(ValidValues, Enumerable.Average); }
+    public override TimeSpan GetStandardDeviation() { return AggregateDistanceAsDouble(ValidValues, EnumerableStatisticExtensions.StandardDeviation); }
+    public override TimeSpan GetVariance() { return AggregateDistanceAsDouble(ValidValues, EnumerableStatisticExtensions.Variance); }
+    #endregion
+
+    #region IStringConvertibleColumn
+    public override bool Validate(string value, out string errorMessage) {
+      DateTime val;
+      bool valid = DateTime.TryParse(value, out val) || string.IsNullOrEmpty(value);
+      errorMessage = valid ? string.Empty : "Invalid Value (values must be formatted according to the current culture settings)";
+      return valid;
+    }
+    public override string GetValue(int index) {
+      var value = Values[index];
+      return value.HasValue ? value.Value.ToString("o") : string.Empty;
+    }
+    public override bool SetValue(string value, int index) {
+      DateTime val;
+      if (DateTime.TryParse(value, out val)) {
+        Values[index] = val;
+        return true;
+      } else if (string.IsNullOrEmpty(value)) {
+        Values[index] = null;
+        return true;
+      } else {
+        return false;
+      }
+    }
+    #endregion
+
+    private static DateTime AggregateAsDouble(IEnumerable<DateTime> values, Func<IEnumerable<double>, double> func) {
+      return new DateTime((long)func(values.Select(x => (double)x.Ticks)));
+    }
+    private static TimeSpan AggregateDistanceAsDouble(IEnumerable<DateTime> values, Func<IEnumerable<double>, double> func) {
+      return new TimeSpan((long)func(values.Select(x => (double)x.Ticks)));
+    }
+  }
+}
Index: branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/Data/Columns/DoublePreprocessingDataColumn.cs
===================================================================
--- branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/Data/Columns/DoublePreprocessingDataColumn.cs	(revision 15291)
+++ branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/Data/Columns/DoublePreprocessingDataColumn.cs	(revision 15291)
@@ -0,0 +1,102 @@
+﻿#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 System.Linq;
+using System.Text;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.DataPreprocessing {
+  [Item("DoublePreprocessingDataColumn", "")]
+  [StorableClass]
+  public sealed class DoublePreprocessingDataColumn : NullablePreprocessingDataColumn<double, double> {
+
+    #region Constructor, Cloning & Persistence
+    public DoublePreprocessingDataColumn()
+      : base() { }
+    public DoublePreprocessingDataColumn(string name, IEnumerable<double> values)
+      : base(name, values) {
+    }
+    public DoublePreprocessingDataColumn(string name, IEnumerable<double?> values)
+      : base(name, values) {
+    }
+
+    private DoublePreprocessingDataColumn(DoublePreprocessingDataColumn original, Cloner cloner)
+      : base(original, cloner) {
+    }
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new DoublePreprocessingDataColumn(this, cloner);
+    }
+
+    [StorableConstructor]
+    private DoublePreprocessingDataColumn(bool deserializing)
+      : base(deserializing) { }
+    #endregion
+
+    public override bool IsValidValue(double value) {
+      return !(double.IsNaN(value) || double.IsInfinity(value));
+    }
+
+    #region Statistics
+    public override double GetRange() { return ValidValues.Max() - ValidValues.Min(); }
+    public override double GetMean() { return ValidValues.Average(); }
+    public override double GetMedian() { return ValidValues.Quantile(0.5); } // IEnumerable<doube> version is faster
+    public override double GetStandardDeviation() { return ValidValues.StandardDeviation(); }
+    public override double GetVariance() { return ValidValues.Variance(); }
+    public override double GetQuantile(double alpha) { return ValidValues.Quantile(alpha); } // IEnumerable<doube> version is faster
+    #endregion
+
+    #region IStringConvertibleColumn
+    public override bool Validate(string value, out string errorMessage) {
+      double val;
+      bool valid = double.TryParse(value, out val) || string.IsNullOrEmpty(value);
+      errorMessage = string.Empty;
+      if (!valid) {
+        var sb = new StringBuilder();
+        sb.Append("Invalid Value (Valid Value Format: \"");
+        sb.Append(FormatPatterns.GetDoubleFormatPattern());
+        sb.Append("\")");
+        errorMessage = sb.ToString();
+      }
+      return valid;
+    }
+    public override string GetValue(int index) {
+      var value = Values[index];
+      return value.HasValue ? value.Value.ToString("r") : string.Empty;
+    }
+    public override bool SetValue(string value, int index) {
+      double val;
+      if (double.TryParse(value, out val)) {
+        Values[index] = val;
+        return true;
+      } else if (string.IsNullOrEmpty(value)) {
+        Values[index] = null;
+        return true;
+      } else {
+        return false;
+      }
+    }
+    #endregion
+  }
+}
Index: branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/Data/Columns/PreprocessingDataColumn.cs
===================================================================
--- branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/Data/Columns/PreprocessingDataColumn.cs	(revision 15291)
+++ branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/Data/Columns/PreprocessingDataColumn.cs	(revision 15291)
@@ -0,0 +1,190 @@
+﻿#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.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.DataPreprocessing {
+  [Item("PreprocessingDataColumn", "")]
+  [StorableClass]
+  public abstract class PreprocessingDataColumn : NamedItem {
+
+    #region Constructor, Cloning & Persistence
+    protected PreprocessingDataColumn() {
+    }
+    protected PreprocessingDataColumn(string name)
+      : base(name) {
+    }
+
+    protected PreprocessingDataColumn(PreprocessingDataColumn original, Cloner cloner)
+      : base(original, cloner) {
+    }
+
+    [StorableConstructor]
+    protected PreprocessingDataColumn(bool deserializing)
+      : base(deserializing) { }
+    #endregion
+
+    public abstract int Length { get; }
+    public abstract Type GetValueType();
+    public virtual bool IsType<T>() { return GetValueType() == typeof(T); }
+
+    public abstract bool IsValidValue(int index);
+
+
+    #region String Handling
+    public abstract bool Validate(string value, out string errorMessage);
+    public abstract string GetValue(int index);
+    public abstract bool SetValue(string value, int index);
+    #endregion
+  }
+
+  [Item("PreprocessingDataColumn", "")]
+  [StorableClass]
+  public abstract class PreprocessingDataColumn<TValue, TDistance> : PreprocessingDataColumn
+    where TValue : class, IComparable<TValue> {
+
+    #region Constructor, Cloning & Persistence
+    protected PreprocessingDataColumn()
+      : this(string.Empty, Enumerable.Empty<TValue>()) { }
+    protected PreprocessingDataColumn(string name, IEnumerable<TValue> values)
+      : base(name) {
+      Values = new List<TValue>(values);
+    }
+
+    protected PreprocessingDataColumn(PreprocessingDataColumn<TValue, TDistance> original, Cloner cloner)
+      : base(original, cloner) {
+      Values = new List<TValue>(original.Values);
+    }
+
+    [StorableConstructor]
+    protected PreprocessingDataColumn(bool deserializing)
+      : base(deserializing) { }
+    #endregion
+
+    [Storable]
+    public List<TValue> Values { get; private set; }
+    public IEnumerable<TValue> ValidValues {
+      get { return Values.Where(IsValidValue); }
+    }
+
+    public override Type GetValueType() {
+      return typeof(TValue);
+    }
+
+    public override int Length {
+      get { return Values.Count; }
+    }
+
+    public TValue this[int index] {
+      get { return Values[index]; }
+      set { Values[index] = value; }
+    }
+
+    public virtual bool IsValidValue(TValue value) { return true; }
+    public override bool IsValidValue(int index) {
+      return IsValidValue(Values[index]);
+    }
+
+    #region Statistics
+    public virtual TValue GetMin() { return Values.Min(); }
+    public virtual TValue GetMax() { return Values.Max(); }
+    public abstract TDistance GetRange();
+    public abstract TValue GetMean();
+    public virtual TValue GetMedian() { return Values.Quantile(0.5); }
+    public virtual TValue GetMode() { return Values.GroupBy(x => x).OrderByDescending(g => g.Count()).Select(g => g.Key).First(); }
+    public abstract TDistance GetStandardDeviation();
+    public abstract TDistance GetVariance();
+    public virtual TValue GetQuantile(double alpha) { return Values.Quantile(alpha); }
+    public virtual int GetDistinctValues() { return Values.GroupBy(x => x).Count(); }
+    public virtual int GetNumberOfMissingValues() { return Values.Count(IsValidValue); }
+    #endregion
+  }
+
+  [Item("NullablePreprocessingDataColumn", "")]
+  [StorableClass]
+  public abstract class NullablePreprocessingDataColumn<TValue, TDistance> : PreprocessingDataColumn
+    where TValue : struct, IComparable<TValue> {
+
+    #region Constructor, Cloning & Persistence
+    protected NullablePreprocessingDataColumn()
+      : this(string.Empty, Enumerable.Empty<TValue?>()) { }
+    protected NullablePreprocessingDataColumn(string name, IEnumerable<TValue> values)
+      : this(name, values.Select(x => (TValue?)x)) { }
+    protected NullablePreprocessingDataColumn(string name, IEnumerable<TValue?> values)
+      : base(name) {
+      Values = new List<TValue?>(values);
+    }
+
+    protected NullablePreprocessingDataColumn(NullablePreprocessingDataColumn<TValue, TDistance> original, Cloner cloner)
+      : base(original, cloner) {
+      Values = new List<TValue?>(original.Values);
+    }
+
+    [StorableConstructor]
+    protected NullablePreprocessingDataColumn(bool deserializing)
+      : base(deserializing) { }
+    #endregion
+
+    [Storable]
+    internal List<TValue?> Values { get; private set; }
+    protected IEnumerable<TValue> ValidValues {
+      get { return Values.Where(x => x.HasValue && IsValidValue(x.Value)).Select(x => x.Value); }
+    }
+
+    public override Type GetValueType() {
+      return typeof(TValue);
+    }
+
+    public override int Length {
+      get { return Values.Count; }
+    }
+
+    public TValue? this[int index] {
+      get { return Values[index]; }
+      set { Values[index] = value; }
+    }
+
+    public virtual bool IsValidValue(TValue value) { return true; }
+    public override bool IsValidValue(int index) {
+      var value = Values[index];
+      return value.HasValue && IsValidValue(value.Value);
+    }
+
+    #region Statistics
+    public virtual TValue GetMin() { return ValidValues.Min(); }
+    public virtual TValue GetMax() { return ValidValues.Max(); }
+    public abstract TDistance GetRange();
+    public abstract TValue GetMean();
+    public virtual TValue GetMedian() { return ValidValues.Quantile(0.5); }
+    public virtual TValue GetMode() { return ValidValues.GroupBy(x => x).OrderByDescending(g => g.Count()).Select(g => g.Key).First(); }
+    public abstract TDistance GetStandardDeviation();
+    public abstract TDistance GetVariance();
+    public virtual TValue GetQuantile(double alpha) { return ValidValues.Quantile(alpha); }
+    public virtual int GetDistinctValues() { return ValidValues.GroupBy(x => x).Count(); }
+    public virtual int GetNumberOfMissingValues() { return Values.Count - ValidValues.Count(); }
+    #endregion
+  }
+}
Index: branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/Data/Columns/StringPreprocessingDataColumn.cs
===================================================================
--- branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/Data/Columns/StringPreprocessingDataColumn.cs	(revision 15291)
+++ branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/Data/Columns/StringPreprocessingDataColumn.cs	(revision 15291)
@@ -0,0 +1,85 @@
+﻿#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.DataPreprocessing {
+  [Item("StringPreprocessingDataColumn", "")]
+  [StorableClass]
+  public sealed class StringPreprocessingDataColumn : PreprocessingDataColumn<string, string> {
+
+    #region Constructor, Cloning & Persistence
+    public StringPreprocessingDataColumn()
+      : base() { }
+    public StringPreprocessingDataColumn(string name, IEnumerable<string> values)
+      : base(name, values) {
+    }
+
+    private StringPreprocessingDataColumn(StringPreprocessingDataColumn original, Cloner cloner)
+      : base(original, cloner) {
+    }
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new StringPreprocessingDataColumn(this, cloner);
+    }
+
+    [StorableConstructor]
+    private StringPreprocessingDataColumn(bool deserializing)
+      : base(deserializing) { }
+    #endregion
+
+    public override bool IsValidValue(string value) {
+      return !string.IsNullOrEmpty(value);
+    }
+
+    #region Statistics
+    public override string GetRange() { return string.Empty; }
+    public override string GetMean() { return string.Empty; }
+    public override string GetStandardDeviation() { return string.Empty; }
+    public override string GetVariance() { return string.Empty; }
+    #endregion
+
+    #region IStringConvertibleColumn
+    public override bool Validate(string value, out string errorMessage) {
+      if (value == null) {
+        errorMessage = "Invalid Value (string must not be null)";
+        return false;
+      } else {
+        errorMessage = string.Empty;
+        return true;
+      }
+    }
+    public override string GetValue(int index) {
+      return Values[index];
+    }
+    public override bool SetValue(string value, int index) {
+      if (value != null) {
+        Values[index] = value;
+        return true;
+      } else {
+        return false;
+      }
+    }
+    #endregion
+  }
+}
Index: branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/Data/PreprocessingData.cs
===================================================================
--- branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/Data/PreprocessingData.cs	(revision 15285)
+++ branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/Data/PreprocessingData.cs	(revision 15291)
@@ -32,5 +32,4 @@
 
 namespace HeuristicLab.DataPreprocessing {
-
   [Item("PreprocessingData", "Represents data used for preprocessing.")]
   [StorableClass]
@@ -38,7 +37,5 @@
 
     [Storable]
-    protected IList<IList> variableValues;
-    [Storable]
-    protected IList<string> variableNames;
+    protected List<PreprocessingDataColumn> dataColumns;
 
     #region Constructor, Cloning & Persistence
@@ -47,4 +44,5 @@
       Name = "Preprocessing Data";
 
+      dataColumns = new List<PreprocessingDataColumn>();
       Transformations = new List<ITransformation>();
       selection = new Dictionary<int, IList<int>>();
@@ -57,8 +55,7 @@
     protected PreprocessingData(PreprocessingData original, Cloner cloner)
       : base(original, cloner) {
-      variableValues = CopyVariableValues(original.variableValues);
-      variableNames = new List<string>(original.variableNames);
-      TrainingPartition = (IntRange)original.TrainingPartition.Clone(cloner);
-      TestPartition = (IntRange)original.TestPartition.Clone(cloner);
+      dataColumns = new List<PreprocessingDataColumn>(original.dataColumns.Select(cloner.Clone));
+      TrainingPartition = cloner.Clone(original.TrainingPartition);
+      TestPartition = cloner.Clone(original.TestPartition);
       Transformations = new List<ITransformation>(original.Transformations.Select(cloner.Clone));
 
@@ -99,10 +96,86 @@
     #region Cells
     public bool IsCellEmpty(int columnIndex, int rowIndex) {
-      var value = variableValues[columnIndex][rowIndex];
-      return IsMissingValue(value);
-    }
+      return !dataColumns[columnIndex].IsValidValue(rowIndex);
+    }
+
+    private void ColumnTypeSwitchAction<T>(int columnIndex, T value, Action<DoublePreprocessingDataColumn, double?> doubleAction,
+      Action<StringPreprocessingDataColumn, string> stringAction = null, Action<DateTimePreprocessingDataColumn, DateTime?> dateTimeAction = null) {
+      ColumnTypeSwitchAction(dataColumns[columnIndex], value, doubleAction, stringAction, dateTimeAction);
+    }
+    private void ColumnTypeSwitchAction<T>(PreprocessingDataColumn column, T value, Action<DoublePreprocessingDataColumn, double?> doubleAction,
+      Action<StringPreprocessingDataColumn, string> stringAction = null, Action<DateTimePreprocessingDataColumn, DateTime?> dateTimeAction = null) {
+      var doubleColumn = column as DoublePreprocessingDataColumn;
+      if (doubleColumn != null && doubleAction != null) doubleAction(doubleColumn, Convert<double?>(value));
+      var stringColumn = column as StringPreprocessingDataColumn;
+      if (stringColumn != null && stringAction != null) stringAction(stringColumn, Convert<string>(value));
+      var dateTimeColumn = column as DateTimePreprocessingDataColumn;
+      if (dateTimeColumn != null && dateTimeAction != null) dateTimeAction(dateTimeColumn, Convert<DateTime?>(value));
+    }
+
+    private void ColumnTypeSwitchAction(int columnIndex, Action<DoublePreprocessingDataColumn> doubleAction,
+      Action<StringPreprocessingDataColumn> stringAction = null, Action<DateTimePreprocessingDataColumn> dateTimeAction = null) {
+      ColumnTypeSwitchAction(dataColumns[columnIndex], doubleAction, stringAction, dateTimeAction);
+    }
+    private void ColumnTypeSwitchAction(PreprocessingDataColumn column, Action<DoublePreprocessingDataColumn> doubleAction,
+      Action<StringPreprocessingDataColumn> stringAction = null, Action<DateTimePreprocessingDataColumn> dateTimeAction = null) {
+      var doubleColumn = column as DoublePreprocessingDataColumn;
+      if (doubleColumn != null && doubleAction != null) doubleAction(doubleColumn);
+      var stringColumn = column as StringPreprocessingDataColumn;
+      if (stringColumn != null && stringAction != null) stringAction(stringColumn);
+      var dateTimeColumn = column as DateTimePreprocessingDataColumn;
+      if (dateTimeColumn != null && dateTimeAction != null) dateTimeAction(dateTimeColumn);
+    }
+
+
+    private T ColumnTypeSwitchFunc<T>(int columnIndex, Func<DoublePreprocessingDataColumn, double?> doubleFunc,
+      Func<StringPreprocessingDataColumn, string> stringFunc = null, Func<DateTimePreprocessingDataColumn, DateTime?> dateTimeFunc = null) {
+      var doubleColumn = dataColumns[columnIndex] as DoublePreprocessingDataColumn;
+      if (doubleColumn != null && doubleFunc != null) return Convert<T>(doubleFunc(doubleColumn));
+      var stringColumn = dataColumns[columnIndex] as StringPreprocessingDataColumn;
+      if (stringColumn != null && stringFunc != null) return Convert<T>(stringFunc(stringColumn));
+      var dateTimeColumn = dataColumns[columnIndex] as DateTimePreprocessingDataColumn;
+      if (dateTimeColumn != null && dateTimeFunc != null) return Convert<T>(dateTimeFunc(dateTimeColumn));
+      throw new InvalidOperationException("Invalid data column type.");
+    }
+
+    private T ColumnTypeSwitchFuncResult<T>(int columnIndex, Func<DoublePreprocessingDataColumn, T> doubleFunc,
+      Func<StringPreprocessingDataColumn, T> stringFunc = null, Func<DateTimePreprocessingDataColumn, T> dateTimeFunc = null) {
+      var doubleColumn = dataColumns[columnIndex] as DoublePreprocessingDataColumn;
+      if (doubleColumn != null && doubleFunc != null) return doubleFunc(doubleColumn);
+      var stringColumn = dataColumns[columnIndex] as StringPreprocessingDataColumn;
+      if (stringColumn != null && stringFunc != null) return stringFunc(stringColumn);
+      var dateTimeColumn = dataColumns[columnIndex] as DateTimePreprocessingDataColumn;
+      if (dateTimeColumn != null && dateTimeFunc != null) return dateTimeFunc(dateTimeColumn);
+      throw new InvalidOperationException("Invalid data column type.");
+    }
+    private TOut ColumnTypeSwitchFuncResult<TIn, TOut>(int columnIndex, TIn value, Func<DoublePreprocessingDataColumn, double?, TOut> doubleFunc,
+     Func<StringPreprocessingDataColumn, string, TOut> stringFunc = null, Func<DateTimePreprocessingDataColumn, DateTime?, TOut> dateTimeFunc = null) {
+      var doubleColumn = dataColumns[columnIndex] as DoublePreprocessingDataColumn;
+      if (doubleColumn != null && doubleFunc != null) return doubleFunc(doubleColumn, Convert<double?>(value));
+      var stringColumn = dataColumns[columnIndex] as StringPreprocessingDataColumn;
+      if (stringColumn != null && stringFunc != null) return stringFunc(stringColumn, Convert<string>(value));
+      var dateTimeColumn = dataColumns[columnIndex] as DateTimePreprocessingDataColumn;
+      if (dateTimeColumn != null && dateTimeFunc != null) return dateTimeFunc(dateTimeColumn, Convert<DateTime?>(value));
+      throw new InvalidOperationException("Invalid data column type.");
+    }
+
+    private IList<T> ColumnTypeSwitchFuncList<T>(int columnIndex, Func<DoublePreprocessingDataColumn, IList<double>> doubleFunc,
+      Func<StringPreprocessingDataColumn, IList<string>> stringFunc = null, Func<DateTimePreprocessingDataColumn, IList<DateTime>> dateTimeFunc = null) {
+      var doubleColumn = dataColumns[columnIndex] as DoublePreprocessingDataColumn;
+      if (doubleColumn != null && doubleFunc != null) return Convert<IList<T>>(doubleFunc(doubleColumn));
+      var stringColumn = dataColumns[columnIndex] as StringPreprocessingDataColumn;
+      if (stringColumn != null && stringFunc != null) return Convert<IList<T>>(stringFunc(stringColumn));
+      var dateTimeColumn = dataColumns[columnIndex] as DateTimePreprocessingDataColumn;
+      if (dateTimeColumn != null && dateTimeFunc != null) return Convert<IList<T>>(dateTimeFunc(dateTimeColumn));
+      throw new InvalidOperationException("Invalid data column type.");
+    }
+    private static T Convert<T>(object obj) { return (T)obj; }
+
 
     public T GetCell<T>(int columnIndex, int rowIndex) {
-      return (T)variableValues[columnIndex][rowIndex];
+      return ColumnTypeSwitchFunc<T>(columnIndex,
+        c => c[rowIndex],
+        c => c[rowIndex],
+        c => c[rowIndex]);
     }
 
@@ -115,5 +188,9 @@
         InsertColumn<T>(i.ToString(), i);
 
-      variableValues[columnIndex][rowIndex] = value;
+      ColumnTypeSwitchAction<T>(columnIndex, value,
+        (c, v) => c[rowIndex] = v,
+        (c, v) => c[rowIndex] = v,
+        (c, v) => c[rowIndex] = v);
+
       if (!IsInTransaction)
         OnChanged(DataPreprocessingChangedEventType.ChangeItem, columnIndex, rowIndex);
@@ -121,5 +198,5 @@
 
     public string GetCellAsString(int columnIndex, int rowIndex) {
-      return variableValues[columnIndex][rowIndex].ToString();
+      return dataColumns[columnIndex].GetValue(rowIndex);
     }
 
@@ -128,9 +205,14 @@
         var list = new List<T>();
         foreach (var rowIdx in selection[columnIndex]) {
-          list.Add((T)variableValues[columnIndex][rowIdx]);
+          list.Add(GetCell<T>(columnIndex, rowIdx));
+          //list.Add((T)dataColumns[columnIndex][rowIdx]);
         }
         return list;
       } else {
-        return (IList<T>)variableValues[columnIndex];
+        return ColumnTypeSwitchFuncList<T>(columnIndex,
+          c => c.Values.Select(x => x ?? double.NaN).ToList(),
+          c => c.Values,
+          c => c.Values.Select(x => x ?? DateTime.MinValue).ToList());
+        //(IList<T>)dataColumns[columnIndex];
       }
     }
@@ -139,7 +221,16 @@
       SaveSnapshot(DataPreprocessingChangedEventType.ChangeColumn, columnIndex, -1);
       if (VariableHasType<T>(columnIndex)) {
-        variableValues[columnIndex] = (IList)values;
+        var name = dataColumns[columnIndex].Name;
+        if (dataColumns[columnIndex].IsType<double>()) {
+          dataColumns[columnIndex] = new DoublePreprocessingDataColumn(name, (IEnumerable<double>)values);
+        } else if (dataColumns[columnIndex].IsType<string>()) {
+          dataColumns[columnIndex] = new StringPreprocessingDataColumn(name, (IEnumerable<string>)values);
+        } else if (dataColumns[columnIndex].IsType<DateTime>()) {
+          dataColumns[columnIndex] = new DateTimePreprocessingDataColumn(name, (IEnumerable<DateTime>)values);
+        } else {
+          throw new ArgumentException("Unknown column type");
+        }
       } else {
-        throw new ArgumentException("The datatype of column " + columnIndex + " must be of type " + variableValues[columnIndex].GetType().Name + " but was " + typeof(T).Name);
+        throw new ArgumentException("The datatype of column " + columnIndex + " must be of type " + dataColumns[columnIndex].GetType().Name + " but was " + typeof(T).Name);
       }
       if (!IsInTransaction)
@@ -179,16 +270,9 @@
 
     public int Columns {
-      get { return variableNames.Count; }
+      get { return dataColumns.Count; }
     }
 
     public int Rows {
-      get { return variableValues.Count > 0 ? variableValues[0].Count : 0; }
-    }
-
-    public static bool IsMissingValue(object value) {
-      if (value is double) return double.IsNaN((double)value);
-      if (value is string) return string.IsNullOrEmpty((string)value);
-      if (value is DateTime) return ((DateTime)value).Equals(DateTime.MinValue);
-      throw new ArgumentException();
+      get { return dataColumns.Count > 0 ? dataColumns[0].Length : 0; }
     }
     #endregion
@@ -197,7 +281,11 @@
     public void InsertRow(int rowIndex) {
       SaveSnapshot(DataPreprocessingChangedEventType.DeleteRow, -1, rowIndex);
-      foreach (IList column in variableValues) {
-        Type type = column.GetType().GetGenericArguments()[0];
-        column.Insert(rowIndex, type.IsValueType ? Activator.CreateInstance(type) : null);
+      foreach (var column in dataColumns) {
+        ColumnTypeSwitchAction(column,
+          c => c.Values.Insert(rowIndex, null),
+          c => c.Values.Insert(rowIndex, null),
+          c => c.Values.Insert(rowIndex, null));
+        //var valueType = column.GetValueType();
+        //column.Insert(rowIndex, valueType.IsValueType ? Activator.CreateInstance(valueType) : null);
       }
       if (TrainingPartition.Start <= rowIndex && rowIndex <= TrainingPartition.End) {
@@ -219,6 +307,10 @@
     public void DeleteRow(int rowIndex) {
       SaveSnapshot(DataPreprocessingChangedEventType.AddRow, -1, rowIndex);
-      foreach (IList column in variableValues) {
-        column.RemoveAt(rowIndex);
+      foreach (var column in dataColumns) {
+        ColumnTypeSwitchAction(column,
+          c => c.Values.RemoveAt(rowIndex),
+          c => c.Values.RemoveAt(rowIndex),
+          c => c.Values.RemoveAt(rowIndex));
+        //column.RemoveAt(rowIndex);
       }
       if (TrainingPartition.Start <= rowIndex && rowIndex <= TrainingPartition.End) {
@@ -241,6 +333,10 @@
       SaveSnapshot(DataPreprocessingChangedEventType.AddRow, -1, -1);
       foreach (int rowIndex in rows.OrderByDescending(x => x)) {
-        foreach (IList column in variableValues) {
-          column.RemoveAt(rowIndex);
+        foreach (var column in dataColumns) {
+          ColumnTypeSwitchAction(column,
+            c => c.Values.RemoveAt(rowIndex),
+            c => c.Values.RemoveAt(rowIndex),
+            c => c.Values.RemoveAt(rowIndex));
+          //column.RemoveAt(rowIndex);
         }
         if (TrainingPartition.Start <= rowIndex && rowIndex <= TrainingPartition.End) {
@@ -264,6 +360,17 @@
     public void InsertColumn<T>(string variableName, int columnIndex) {
       SaveSnapshot(DataPreprocessingChangedEventType.DeleteColumn, columnIndex, -1);
-      variableValues.Insert(columnIndex, new List<T>(Enumerable.Repeat(default(T), Rows)));
-      variableNames.Insert(columnIndex, variableName);
+
+      if (typeof(T) == typeof(double)) {
+        dataColumns.Insert(columnIndex, new DoublePreprocessingDataColumn(variableName, Enumerable.Repeat<double?>(null, Rows)));
+      } else if (typeof(T) == typeof(string)) {
+        dataColumns.Add(new StringPreprocessingDataColumn(variableName, Enumerable.Repeat<string>(null, Rows)));
+      } else if (typeof(T) == typeof(DateTime)) {
+        dataColumns.Add(new DateTimePreprocessingDataColumn(variableName, Enumerable.Repeat<DateTime?>(null, Rows)));
+      } else {
+        throw new ArgumentException("The datatype of column " + variableName + " must be of type double, string or DateTime");
+      }
+
+      //dataColumns.Insert(columnIndex, new List<T>(Enumerable.Repeat(default(T), Rows)));
+      //variableNames.Insert(columnIndex, variableName);
       if (!IsInTransaction)
         OnChanged(DataPreprocessingChangedEventType.AddColumn, columnIndex, -1);
@@ -272,6 +379,6 @@
     public void DeleteColumn(int columnIndex) {
       SaveSnapshot(DataPreprocessingChangedEventType.AddColumn, columnIndex, -1);
-      variableValues.RemoveAt(columnIndex);
-      variableNames.RemoveAt(columnIndex);
+      dataColumns.RemoveAt(columnIndex);
+      //variableNames.RemoveAt(columnIndex);
       if (!IsInTransaction)
         OnChanged(DataPreprocessingChangedEventType.DeleteColumn, columnIndex, -1);
@@ -280,7 +387,7 @@
     public void RenameColumn(int columnIndex, string name) {
       SaveSnapshot(DataPreprocessingChangedEventType.ChangeColumn, columnIndex, -1);
-      if (columnIndex < 0 || columnIndex > variableNames.Count)
+      if (columnIndex < 0 || columnIndex > dataColumns.Count)
         throw new ArgumentOutOfRangeException("columnIndex");
-      variableNames[columnIndex] = name;
+      dataColumns[columnIndex].Name = name;
 
       if (!IsInTransaction)
@@ -290,9 +397,9 @@
     public void RenameColumns(IList<string> names) {
       if (names == null) throw new ArgumentNullException("names");
-      if (names.Count != variableNames.Count) throw new ArgumentException("number of names must match the number of columns.", "names");
+      if (names.Count != dataColumns.Count) throw new ArgumentException("number of names must match the number of columns.", "names");
 
       SaveSnapshot(DataPreprocessingChangedEventType.ChangeColumn, -1, -1);
       for (int i = 0; i < names.Count; i++)
-        variableNames[i] = names[i];
+        dataColumns[i].Name = names[i];
 
       if (!IsInTransaction)
@@ -307,32 +414,25 @@
     #region Variables
     public IEnumerable<string> VariableNames {
-      get { return variableNames; }
+      get { return dataColumns.Select(c => c.Name); }
     }
 
     public IEnumerable<string> GetDoubleVariableNames() {
-      var doubleVariableNames = new List<string>();
-      for (int i = 0; i < Columns; ++i) {
-        if (VariableHasType<double>(i)) {
-          doubleVariableNames.Add(variableNames[i]);
-        }
-      }
-      return doubleVariableNames;
+      return dataColumns.OfType<DoublePreprocessingDataColumn>().Select(c => c.Name);
     }
 
     public string GetVariableName(int columnIndex) {
-      return variableNames[columnIndex];
+      return dataColumns[columnIndex].Name;
     }
 
     public int GetColumnIndex(string variableName) {
-      return variableNames.IndexOf(variableName);
+      return dataColumns.FindIndex(c => c.Name == variableName);
     }
 
     public bool VariableHasType<T>(int columnIndex) {
-      return columnIndex >= variableValues.Count || variableValues[columnIndex] is List<T>;
+      return dataColumns[columnIndex].IsType<T>();
     }
 
     public Type GetVariableType(int columnIndex) {
-      var listType = variableValues[columnIndex].GetType();
-      return listType.GenericTypeArguments.Single();
+      return dataColumns[columnIndex].GetValueType();
     }
 
@@ -392,24 +492,21 @@
     #region Import & Export
     public void Import(IDataAnalysisProblemData problemData) {
-      Dataset dataset = (Dataset)problemData.Dataset;
-      variableNames = new List<string>(problemData.Dataset.VariableNames);
+      var dataset = problemData.Dataset;
       InputVariables = new List<string>(problemData.AllowedInputVariables);
-      TargetVariable = (problemData is IRegressionProblemData) ? ((IRegressionProblemData)problemData).TargetVariable
-        : (problemData is IClassificationProblemData) ? ((IClassificationProblemData)problemData).TargetVariable
-          : null;
-
-      int columnIndex = 0;
-      variableValues = new List<IList>();
+      TargetVariable = problemData is IRegressionProblemData ? ((IRegressionProblemData)problemData).TargetVariable
+        : problemData is IClassificationProblemData ? ((IClassificationProblemData)problemData).TargetVariable
+        : null;
+
+      dataColumns.Clear();
       foreach (var variableName in problemData.Dataset.VariableNames) {
         if (dataset.VariableHasType<double>(variableName)) {
-          variableValues.Insert(columnIndex, dataset.GetDoubleValues(variableName).ToList());
+          dataColumns.Add(new DoublePreprocessingDataColumn(variableName, dataset.GetDoubleValues(variableName)));
         } else if (dataset.VariableHasType<string>(variableName)) {
-          variableValues.Insert(columnIndex, dataset.GetStringValues(variableName).ToList());
+          dataColumns.Add(new StringPreprocessingDataColumn(variableName, dataset.GetStringValues(variableName)));
         } else if (dataset.VariableHasType<DateTime>(variableName)) {
-          variableValues.Insert(columnIndex, dataset.GetDateTimeValues(variableName).ToList());
+          dataColumns.Add(new DateTimePreprocessingDataColumn(variableName, dataset.GetDateTimeValues(variableName)));
         } else {
           throw new ArgumentException("The datatype of column " + variableName + " must be of type double, string or DateTime");
         }
-        ++columnIndex;
       }
 
@@ -421,10 +518,15 @@
       IList<IList> values = new List<IList>();
 
-      for (int i = 0; i < Columns; ++i) {
-        values.Add(variableValues[i]);
-      }
-
-      var dataset = new Dataset(variableNames, values);
-      return dataset;
+      for (int i = 0; i < Columns; i++) {
+        var doubleColumn = dataColumns[i] as DoublePreprocessingDataColumn;
+        var stringColumn = dataColumns[i] as StringPreprocessingDataColumn;
+        var dateTimeColumn = dataColumns[i] as DateTimePreprocessingDataColumn;
+        if (doubleColumn != null) values.Add(new List<double>(doubleColumn.Values.Select(x => x ?? double.NaN)));
+        else if (stringColumn != null) values.Add(new List<string>(stringColumn.Values));
+        else if (dateTimeColumn != null) values.Add(new List<DateTime>(dateTimeColumn.Values.Select(x => x ?? DateTime.MinValue)));
+        else throw new InvalidOperationException("Column type not supported for export");
+      }
+
+      return new Dataset(VariableNames, values);
     }
     #endregion
@@ -452,8 +554,7 @@
 
     #region Transactions
-    // Stapshot/History are nost storable/cloneable on purpose
+    // Snapshot/History are not storable/cloneable on purpose
     private class Snapshot {
-      public IList<IList> VariableValues { get; set; }
-      public IList<string> VariableNames { get; set; }
+      public List<PreprocessingDataColumn> DataColumns { get; set; }
 
       public IntRange TrainingPartition { get; set; }
@@ -472,5 +573,5 @@
     }
 
-    private const int MAX_UNDO_DEPTH = 5;
+    private const int MaxUndoDepth = 5;
 
     private readonly IList<Snapshot> undoHistory = new List<Snapshot>();
@@ -482,7 +583,7 @@
       if (IsInTransaction) return;
 
+      var cloner = new Cloner();
       var currentSnapshot = new Snapshot {
-        VariableValues = CopyVariableValues(variableValues),
-        VariableNames = new List<string>(variableNames),
+        DataColumns = new List<PreprocessingDataColumn>(dataColumns.Select(cloner.Clone)),
         TrainingPartition = new IntRange(TrainingPartition.Start, TrainingPartition.End),
         TestPartition = new IntRange(TestPartition.Start, TestPartition.End),
@@ -493,5 +594,5 @@
       };
 
-      if (undoHistory.Count >= MAX_UNDO_DEPTH)
+      if (undoHistory.Count >= MaxUndoDepth)
         undoHistory.RemoveAt(0);
 
@@ -506,6 +607,5 @@
       if (IsUndoAvailable) {
         Snapshot previousSnapshot = undoHistory[undoHistory.Count - 1];
-        variableValues = previousSnapshot.VariableValues;
-        variableNames = previousSnapshot.VariableNames;
+        dataColumns = previousSnapshot.DataColumns;
         TrainingPartition = previousSnapshot.TrainingPartition;
         TestPartition = previousSnapshot.TestPartition;
@@ -620,5 +720,20 @@
 
     private IEnumerable<T> GetValuesWithoutMissingValues<T>(int columnIndex, bool considerSelection) {
-      return GetValues<T>(columnIndex, considerSelection).Where(x => !IsMissingValue(x));
+      //var doubleColumn = dataColumns[columnIndex] as DoublePreprocessingDataColumn;
+      //var stringColumn = dataColumns[columnIndex] as StringPreprocessingDataColumn;
+      //var dateTimeColumn = dataColumns[columnIndex] as DateTimePreprocessingDataColumn;
+      //return GetValues<T>(columnIndex, considerSelection).Where(x =>
+      //  doubleColumn != null ? doubleColumn.IsValidValue(Convert<double>(x))
+      //  : stringColumn != null ? stringColumn.IsValidValue(Convert<string>(x))
+      //  : dateTimeColumn != null ? dateTimeColumn.IsValidValue(Convert<DateTime>(x))
+      //  : false);
+      //!IsMissingValue(x));
+
+      return GetValues<T>(columnIndex, considerSelection).Where(x =>
+        ColumnTypeSwitchFuncResult<T, bool>(columnIndex, x,
+          (c, v) => v.HasValue && c.IsValidValue(v.Value),
+          (c, v) => c.IsValidValue(v),
+          (c, v) => v.HasValue && c.IsValidValue(v.Value)
+      ));
     }
 
@@ -626,5 +741,4 @@
       return new DateTime((long)(func(values.Select(x => (double)x.Ticks / TimeSpan.TicksPerSecond)) * TimeSpan.TicksPerSecond));
     }
-    private static T Convert<T>(object obj) { return (T)obj; }
 
     public int GetMissingValueCount() {
Index: branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/HeuristicLab.DataPreprocessing-3.4.csproj
===================================================================
--- branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/HeuristicLab.DataPreprocessing-3.4.csproj	(revision 15285)
+++ branches/DataPreprocessing Cleanup/HeuristicLab.DataPreprocessing/3.4/HeuristicLab.DataPreprocessing-3.4.csproj	(revision 15291)
@@ -123,4 +123,8 @@
     <Compile Include="Content\ScatterPlotContent.cs" />
     <Compile Include="Content\DataCompletenessChartContent.cs" />
+    <Compile Include="Data\Columns\DateTimePreprocessingDataColumn.cs" />
+    <Compile Include="Data\Columns\DoublePreprocessingDataColumn.cs" />
+    <Compile Include="Data\Columns\PreprocessingDataColumn.cs" />
+    <Compile Include="Data\Columns\StringPreprocessingDataColumn.cs" />
     <Compile Include="Data\FilteredPreprocessingData.cs" />
     <Compile Include="Content\ManipulationContent.cs" />
