Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/NearestNeighbour/NearestNeighbourClassification.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/NearestNeighbour/NearestNeighbourClassification.cs	(revision 14307)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/NearestNeighbour/NearestNeighbourClassification.cs	(revision 14308)
@@ -40,8 +40,13 @@
     private const string KParameterName = "K";
     private const string NearestNeighbourClassificationModelResultName = "Nearest neighbour classification solution";
+    private const string WeightsParameterName = "Weights";
+
 
     #region parameter properties
     public IFixedValueParameter<IntValue> KParameter {
       get { return (IFixedValueParameter<IntValue>)Parameters[KParameterName]; }
+    }
+    public IValueParameter<DoubleArray> WeightsParameter {
+      get { return (IValueParameter<DoubleArray>)Parameters[WeightsParameterName]; }
     }
     #endregion
@@ -53,4 +58,8 @@
         else KParameter.Value.Value = value;
       }
+    }
+    public DoubleArray Weights {
+      get { return WeightsParameter.Value; }
+      set { WeightsParameter.Value = value; }
     }
     #endregion
@@ -64,8 +73,16 @@
       : base() {
       Parameters.Add(new FixedValueParameter<IntValue>(KParameterName, "The number of nearest neighbours to consider for regression.", new IntValue(3)));
+      Parameters.Add(new OptionalValueParameter<DoubleArray>(WeightsParameterName, "Optional: use weights to specify individual scaling values for all features. If not set the weights are calculated automatically (each feature is scaled to unit variance)"));
       Problem = new ClassificationProblem();
     }
     [StorableHook(HookType.AfterDeserialization)]
-    private void AfterDeserialization() { }
+    private void AfterDeserialization() {
+      // BackwardsCompatibility3.3
+      #region Backwards compatible code, remove with 3.4
+      if (!Parameters.ContainsKey(WeightsParameterName)) {
+        Parameters.Add(new OptionalValueParameter<DoubleArray>(WeightsParameterName, "Optional: use weights to specify individual scaling values for all features. If not set the weights are calculated automatically (each feature is scaled to unit variance)"));
+      }
+      #endregion
+    }
 
     public override IDeepCloneable Clone(Cloner cloner) {
@@ -75,14 +92,16 @@
     #region nearest neighbour
     protected override void Run() {
-      var solution = CreateNearestNeighbourClassificationSolution(Problem.ProblemData, K);
+      double[] weights = null;
+      if (Weights != null) weights = Weights.CloneAsArray();
+      var solution = CreateNearestNeighbourClassificationSolution(Problem.ProblemData, K, weights);
       Results.Add(new Result(NearestNeighbourClassificationModelResultName, "The nearest neighbour classification solution.", solution));
     }
 
-    public static IClassificationSolution CreateNearestNeighbourClassificationSolution(IClassificationProblemData problemData, int k) {
+    public static IClassificationSolution CreateNearestNeighbourClassificationSolution(IClassificationProblemData problemData, int k, double[] weights = null) {
       var problemDataClone = (IClassificationProblemData)problemData.Clone();
-      return new NearestNeighbourClassificationSolution(Train(problemDataClone, k), problemDataClone);
+      return new NearestNeighbourClassificationSolution(Train(problemDataClone, k, weights), problemDataClone);
     }
 
-    public static INearestNeighbourModel Train(IClassificationProblemData problemData, int k) {
+    public static INearestNeighbourModel Train(IClassificationProblemData problemData, int k, double[] weights = null) {
       return new NearestNeighbourModel(problemData.Dataset,
         problemData.TrainingIndices,
@@ -90,4 +109,5 @@
         problemData.TargetVariable,
         problemData.AllowedInputVariables,
+        weights,
         problemData.ClassValues.ToArray());
     }
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/NearestNeighbour/NearestNeighbourModel.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/NearestNeighbour/NearestNeighbourModel.cs	(revision 14307)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/NearestNeighbour/NearestNeighbourModel.cs	(revision 14308)
@@ -58,4 +58,8 @@
     [Storable]
     private int k;
+    [Storable(DefaultValue = null)]
+    private double[] weights; // not set for old versions loaded from disk
+    [Storable(DefaultValue = null)]
+    private double[] offsets; // not set for old versions loaded from disk
 
     [StorableConstructor]
@@ -93,9 +97,16 @@
 
       k = original.k;
+      isCompatibilityLoaded = original.IsCompatibilityLoaded;
+      if (!IsCompatibilityLoaded) {
+        weights = new double[original.weights.Length];
+        Array.Copy(original.weights, weights, weights.Length);
+        offsets = new double[original.offsets.Length];
+        Array.Copy(original.offsets, this.offsets, this.offsets.Length);
+      }
       allowedInputVariables = (string[])original.allowedInputVariables.Clone();
       if (original.classValues != null)
         this.classValues = (double[])original.classValues.Clone();
     }
-    public NearestNeighbourModel(IDataset dataset, IEnumerable<int> rows, int k, string targetVariable, IEnumerable<string> allowedInputVariables, double[] classValues = null)
+    public NearestNeighbourModel(IDataset dataset, IEnumerable<int> rows, int k, string targetVariable, IEnumerable<string> allowedInputVariables, IEnumerable<double> weights = null, double[] classValues = null)
       : base(targetVariable) {
       Name = ItemName;
@@ -103,8 +114,29 @@
       this.k = k;
       this.allowedInputVariables = allowedInputVariables.ToArray();
-
-      var inputMatrix = AlglibUtil.PrepareInputMatrix(dataset,
-                                   allowedInputVariables.Concat(new string[] { targetVariable }),
-                                   rows);
+      double[,] inputMatrix;
+      if (IsCompatibilityLoaded) {
+        // no scaling
+        inputMatrix = AlglibUtil.PrepareInputMatrix(dataset,
+          this.allowedInputVariables.Concat(new string[] { targetVariable }),
+          rows);
+      } else {
+        this.offsets = this.allowedInputVariables
+          .Select(name => dataset.GetDoubleValues(name, rows).Average() * -1)
+          .Concat(new double[] { 0 }) // no offset for target variable
+          .ToArray();
+        if (weights == null) {
+          // automatic determination of weights (all features should have variance = 1)
+          this.weights = this.allowedInputVariables
+            .Select(name => 1.0 / dataset.GetDoubleValues(name, rows).StandardDeviationPop())
+            .Concat(new double[] { 1.0 }) // no scaling for target variable
+            .ToArray();
+        } else {
+          // user specified weights (+ 1 for target)
+          this.weights = weights.Concat(new double[] { 1.0 }).ToArray();
+          if (this.weights.Length - 1 != this.allowedInputVariables.Length)
+            throw new ArgumentException("The number of elements in the weight vector must match the number of input variables");
+        }
+        inputMatrix = CreateScaledData(dataset, this.allowedInputVariables.Concat(new string[] { targetVariable }), rows, this.offsets, this.weights);
+      }
 
       if (inputMatrix.Cast<double>().Any(x => double.IsNaN(x) || double.IsInfinity(x)))
@@ -132,4 +164,18 @@
     }
 
+    private static double[,] CreateScaledData(IDataset dataset, IEnumerable<string> variables, IEnumerable<int> rows, double[] offsets, double[] factors) {
+      var x = new double[rows.Count(), variables.Count()];
+      var colIdx = 0;
+      foreach (var variableName in variables) {
+        var rowIdx = 0;
+        foreach (var val in dataset.GetDoubleValues(variableName, rows)) {
+          x[rowIdx, colIdx] = (val + offsets[colIdx]) * factors[colIdx];
+          rowIdx++;
+        }
+        colIdx++;
+      }
+      return x;
+    }
+
     public override IDeepCloneable Clone(Cloner cloner) {
       return new NearestNeighbourModel(this, cloner);
@@ -137,10 +183,14 @@
 
     public IEnumerable<double> GetEstimatedValues(IDataset dataset, IEnumerable<int> rows) {
-      double[,] inputData = AlglibUtil.PrepareInputMatrix(dataset, allowedInputVariables, rows);
+      double[,] inputData;
+      if (IsCompatibilityLoaded) {
+        inputData = AlglibUtil.PrepareInputMatrix(dataset, allowedInputVariables, rows);
+      } else {
+        inputData = CreateScaledData(dataset, allowedInputVariables, rows, offsets, weights);
+      }
 
       int n = inputData.GetLength(0);
       int columns = inputData.GetLength(1);
       double[] x = new double[columns];
-      double[] y = new double[1];
       double[] dists = new double[k];
       double[,] neighbours = new double[k, columns + 1];
@@ -152,5 +202,5 @@
         int actNeighbours = alglib.nearestneighbor.kdtreequeryknn(kdTree, x, k, false);
         alglib.nearestneighbor.kdtreequeryresultsdistances(kdTree, ref dists);
-        alglib.nearestneighbor.kdtreequeryresultsxy(kdTree, ref neighbours);
+        alglib.nearestneighbor.kdtreequeryresultsxy(kdTree, ref neighbours); // gkronber: this call changes the kdTree data structure
 
         double distanceWeightedValue = 0.0;
@@ -166,6 +216,10 @@
     public override IEnumerable<double> GetEstimatedClassValues(IDataset dataset, IEnumerable<int> rows) {
       if (classValues == null) throw new InvalidOperationException("No class values are defined.");
-      double[,] inputData = AlglibUtil.PrepareInputMatrix(dataset, allowedInputVariables, rows);
-
+      double[,] inputData;
+      if (IsCompatibilityLoaded) {
+        inputData = AlglibUtil.PrepareInputMatrix(dataset, allowedInputVariables, rows);
+      } else {
+        inputData = CreateScaledData(dataset, allowedInputVariables, rows, offsets, weights);
+      }
       int n = inputData.GetLength(0);
       int columns = inputData.GetLength(1);
@@ -219,4 +273,15 @@
     #endregion
 
+
+    // BackwardsCompatibility3.3
+    #region Backwards compatible code, remove with 3.4
+
+    private bool isCompatibilityLoaded = false; // new kNN models have the value false, kNN models loaded from disc have the value true
+    [Storable(DefaultValue = true)]
+    public bool IsCompatibilityLoaded {
+      get { return isCompatibilityLoaded; }
+      set { isCompatibilityLoaded = value; }
+    }
+    #endregion
     #region persistence
     [Storable]
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/NearestNeighbour/NearestNeighbourRegression.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/NearestNeighbour/NearestNeighbourRegression.cs	(revision 14307)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/NearestNeighbour/NearestNeighbourRegression.cs	(revision 14308)
@@ -39,8 +39,13 @@
     private const string KParameterName = "K";
     private const string NearestNeighbourRegressionModelResultName = "Nearest neighbour regression solution";
+    private const string WeightsParameterName = "Weights";
 
     #region parameter properties
     public IFixedValueParameter<IntValue> KParameter {
       get { return (IFixedValueParameter<IntValue>)Parameters[KParameterName]; }
+    }
+
+    public IValueParameter<DoubleArray> WeightsParameter {
+      get { return (IValueParameter<DoubleArray>)Parameters[WeightsParameterName]; }
     }
     #endregion
@@ -52,4 +57,9 @@
         else KParameter.Value.Value = value;
       }
+    }
+
+    public DoubleArray Weights {
+      get { return WeightsParameter.Value; }
+      set { WeightsParameter.Value = value; }
     }
     #endregion
@@ -63,8 +73,17 @@
       : base() {
       Parameters.Add(new FixedValueParameter<IntValue>(KParameterName, "The number of nearest neighbours to consider for regression.", new IntValue(3)));
+      Parameters.Add(new OptionalValueParameter<DoubleArray>(WeightsParameterName, "Optional: use weights to specify individual scaling values for all features. If not set the weights are calculated automatically (each feature is scaled to unit variance)"));
       Problem = new RegressionProblem();
     }
+
     [StorableHook(HookType.AfterDeserialization)]
-    private void AfterDeserialization() { }
+    private void AfterDeserialization() {
+      // BackwardsCompatibility3.3
+      #region Backwards compatible code, remove with 3.4
+      if (!Parameters.ContainsKey(WeightsParameterName)) {
+        Parameters.Add(new OptionalValueParameter<DoubleArray>(WeightsParameterName, "Optional: use weights to specify individual scaling values for all features. If not set the weights are calculated automatically (each feature is scaled to unit variance)"));
+      }
+      #endregion
+    }
 
     public override IDeepCloneable Clone(Cloner cloner) {
@@ -74,19 +93,22 @@
     #region nearest neighbour
     protected override void Run() {
-      var solution = CreateNearestNeighbourRegressionSolution(Problem.ProblemData, K);
+      double[] weights = null;
+      if (Weights != null) weights = Weights.CloneAsArray();
+      var solution = CreateNearestNeighbourRegressionSolution(Problem.ProblemData, K, weights);
       Results.Add(new Result(NearestNeighbourRegressionModelResultName, "The nearest neighbour regression solution.", solution));
     }
 
-    public static IRegressionSolution CreateNearestNeighbourRegressionSolution(IRegressionProblemData problemData, int k) {
+    public static IRegressionSolution CreateNearestNeighbourRegressionSolution(IRegressionProblemData problemData, int k, double[] weights = null) {
       var clonedProblemData = (IRegressionProblemData)problemData.Clone();
-      return new NearestNeighbourRegressionSolution(Train(problemData, k), clonedProblemData);
+      return new NearestNeighbourRegressionSolution(Train(problemData, k, weights), clonedProblemData);
     }
 
-    public static INearestNeighbourModel Train(IRegressionProblemData problemData, int k) {
+    public static INearestNeighbourModel Train(IRegressionProblemData problemData, int k, double[] weights = null) {
       return new NearestNeighbourModel(problemData.Dataset,
         problemData.TrainingIndices,
         k,
         problemData.TargetVariable,
-        problemData.AllowedInputVariables);
+        problemData.AllowedInputVariables,
+        weights);
     }
     #endregion
