#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 . */ #endregion using System; using System.Collections.Generic; using System.Linq; using System.Threading; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Optimization; using HeuristicLab.Parameters; using HeuristicLab.Persistence; using HeuristicLab.Problems.DataAnalysis; namespace HeuristicLab.Algorithms.DataAnalysis { /// /// k-Means clustering algorithm data analysis algorithm. /// [Item("k-Means", "The k-Means clustering algorithm (wrapper for ALGLIB).")] [Creatable(CreatableAttribute.Categories.DataAnalysis, Priority = 10)] [StorableType("b85d0012-07d4-4601-87a4-2a6d19648b22")] public sealed class KMeansClustering : FixedDataAnalysisAlgorithm { private const string KParameterName = "k"; private const string RestartsParameterName = "Restarts"; private const string KMeansSolutionResultName = "k-Means clustering solution"; #region parameter properties public IValueParameter KParameter { get { return (IValueParameter)Parameters[KParameterName]; } } public IValueParameter RestartsParameter { get { return (IValueParameter)Parameters[RestartsParameterName]; } } #endregion #region properties public IntValue K { get { return KParameter.Value; } } public IntValue Restarts { get { return RestartsParameter.Value; } } #endregion [StorableConstructor] private KMeansClustering(StorableConstructorFlag deserializing) : base(deserializing) { } private KMeansClustering(KMeansClustering original, Cloner cloner) : base(original, cloner) { } public KMeansClustering() : base() { Parameters.Add(new ValueParameter(KParameterName, "The number of clusters.", new IntValue(3))); Parameters.Add(new ValueParameter(RestartsParameterName, "The number of restarts.", new IntValue(0))); Problem = new ClusteringProblem(); } [StorableHook(HookType.AfterDeserialization)] private void AfterDeserialization() { } public override IDeepCloneable Clone(Cloner cloner) { return new KMeansClustering(this, cloner); } #region k-Means clustering protected override void Run(CancellationToken cancellationToken) { var solution = CreateKMeansSolution(Problem.ProblemData, K.Value, Restarts.Value); Results.Add(new Result(KMeansSolutionResultName, "The k-Means clustering solution.", solution)); } public static KMeansClusteringSolution CreateKMeansSolution(IClusteringProblemData problemData, int k, int restarts) { var dataset = problemData.Dataset; IEnumerable allowedInputVariables = problemData.AllowedInputVariables; IEnumerable rows = problemData.TrainingIndices; int info; double[,] centers; int[] xyc; double[,] inputMatrix = dataset.ToArray(allowedInputVariables, rows); if (inputMatrix.Cast().Any(x => double.IsNaN(x) || double.IsInfinity(x))) throw new NotSupportedException("k-Means clustering does not support NaN or infinity values in the input dataset."); alglib.kmeansgenerate(inputMatrix, inputMatrix.GetLength(0), inputMatrix.GetLength(1), k, restarts + 1, out info, out centers, out xyc); if (info != 1) throw new ArgumentException("Error in calculation of k-Means clustering solution"); KMeansClusteringSolution solution = new KMeansClusteringSolution(new KMeansClusteringModel(centers, allowedInputVariables), (IClusteringProblemData)problemData.Clone()); return solution; } #endregion } }