View Javadoc
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 }