1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * https://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* 19 * This is not the original file distributed by the Apache Software Foundation 20 * It has been modified by the Hipparchus project 21 */ 22 23 package org.hipparchus.clustering.evaluation; 24 25 import java.util.List; 26 27 import org.hipparchus.clustering.CentroidCluster; 28 import org.hipparchus.clustering.Cluster; 29 import org.hipparchus.clustering.Clusterable; 30 import org.hipparchus.clustering.DoublePoint; 31 import org.hipparchus.clustering.distance.DistanceMeasure; 32 import org.hipparchus.clustering.distance.EuclideanDistance; 33 34 /** 35 * Base class for cluster evaluation methods. 36 * 37 * @param <T> type of the clustered points 38 */ 39 public abstract class ClusterEvaluator<T extends Clusterable> { 40 41 /** The distance measure to use when evaluating the cluster. */ 42 private final DistanceMeasure measure; 43 44 /** 45 * Creates a new cluster evaluator with an {@link EuclideanDistance} 46 * as distance measure. 47 */ 48 public ClusterEvaluator() { 49 this(new EuclideanDistance()); 50 } 51 52 /** 53 * Creates a new cluster evaluator with the given distance measure. 54 * @param measure the distance measure to use 55 */ 56 public ClusterEvaluator(final DistanceMeasure measure) { 57 this.measure = measure; 58 } 59 60 /** 61 * Computes the evaluation score for the given list of clusters. 62 * @param clusters the clusters to evaluate 63 * @return the computed score 64 */ 65 public abstract double score(List<? extends Cluster<T>> clusters); 66 67 /** 68 * Returns whether the first evaluation score is considered to be better 69 * than the second one by this evaluator. 70 * <p> 71 * Specific implementations shall override this method if the returned scores 72 * do not follow the same ordering, i.e. smaller score is better. 73 * 74 * @param score1 the first score 75 * @param score2 the second score 76 * @return {@code true} if the first score is considered to be better, {@code false} otherwise 77 */ 78 public boolean isBetterScore(double score1, double score2) { 79 return score1 < score2; 80 } 81 82 /** 83 * Calculates the distance between two {@link Clusterable} instances 84 * with the configured {@link DistanceMeasure}. 85 * 86 * @param p1 the first clusterable 87 * @param p2 the second clusterable 88 * @return the distance between the two clusterables 89 */ 90 protected double distance(final Clusterable p1, final Clusterable p2) { 91 return measure.compute(p1.getPoint(), p2.getPoint()); 92 } 93 94 /** 95 * Computes the centroid for a cluster. 96 * 97 * @param cluster the cluster 98 * @return the computed centroid for the cluster, 99 * or {@code null} if the cluster does not contain any points 100 */ 101 protected Clusterable centroidOf(final Cluster<T> cluster) { 102 final List<T> points = cluster.getPoints(); 103 if (points.isEmpty()) { 104 return null; 105 } 106 107 // in case the cluster is of type CentroidCluster, no need to compute the centroid 108 if (cluster instanceof CentroidCluster) { 109 return ((CentroidCluster<T>) cluster).getCenter(); 110 } 111 112 final int dimension = points.get(0).getPoint().length; 113 final double[] centroid = new double[dimension]; 114 for (final T p : points) { 115 final double[] point = p.getPoint(); 116 for (int i = 0; i < centroid.length; i++) { 117 centroid[i] += point[i]; 118 } 119 } 120 for (int i = 0; i < centroid.length; i++) { 121 centroid[i] /= points.size(); 122 } 123 return new DoublePoint(centroid); 124 } 125 126 }