1 /* 2 * This file is part of ELKI: 3 * Environment for Developing KDD-Applications Supported by Index-Structures 4 * 5 * Copyright (C) 2018 6 * ELKI Development Team 7 * 8 * This program is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU Affero General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU Affero General Public License for more details. 17 * 18 * You should have received a copy of the GNU Affero General Public License 19 * along with this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 package de.lmu.ifi.dbs.elki.data.model; 22 23 import de.lmu.ifi.dbs.elki.data.DoubleVector; 24 import de.lmu.ifi.dbs.elki.data.NumberVector; 25 import de.lmu.ifi.dbs.elki.database.ids.DBIDs; 26 import de.lmu.ifi.dbs.elki.database.relation.Relation; 27 import de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid; 28 import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.NumberVectorAdapter; 29 30 /** 31 * Utility classes for dealing with cluster models. 32 * 33 * @author Erich Schubert 34 * @since 0.7.0 35 * 36 * @assoc - - - Model 37 * @assoc - - - NumberVector 38 */ 39 public final class ModelUtil { 40 /** 41 * Private constructor. Static methods only. 42 */ ModelUtil()43 private ModelUtil() { 44 // Do not use. 45 } 46 47 /** 48 * Get (and convert!) the representative vector for a cluster model. 49 * 50 * <b>Only representative-based models are supported!</b> 51 * 52 * {@code null} is returned when the model is not supported! 53 * 54 * @param model Model 55 * @param relation Data relation (for representatives specified per DBID) 56 * @param factory Vector factory, for type conversion. 57 * @return Vector of type V, {@code null} if not supported. 58 * @param <V> desired vector type 59 */ 60 @SuppressWarnings("unchecked") getPrototype(Model model, Relation<? extends V> relation, NumberVector.Factory<V> factory)61 public static <V extends NumberVector> V getPrototype(Model model, Relation<? extends V> relation, NumberVector.Factory<V> factory) { 62 // Mean model contains a numeric Vector 63 if(model instanceof MeanModel) { 64 final double[] p = ((MeanModel) model).getMean(); 65 return factory.newNumberVector(p); 66 } 67 // Handle medoid models 68 if(model instanceof MedoidModel) { 69 NumberVector p = relation.get(((MedoidModel) model).getMedoid()); 70 if(factory.getRestrictionClass().isInstance(p)) { 71 return (V) p; 72 } 73 return factory.newNumberVector(p, NumberVectorAdapter.STATIC); 74 } 75 if(model instanceof PrototypeModel) { 76 Object p = ((PrototypeModel<?>) model).getPrototype(); 77 if(factory.getRestrictionClass().isInstance(p)) { 78 return (V) p; 79 } 80 if(p instanceof NumberVector) { 81 return factory.newNumberVector((NumberVector) p, NumberVectorAdapter.STATIC); 82 } 83 return null; // Inconvertible 84 } 85 return null; 86 } 87 88 /** 89 * Get the representative vector for a cluster model. 90 * 91 * <b>Only representative-based models are supported!</b> 92 * 93 * {@code null} is returned when the model is not supported! 94 * 95 * @param model Model 96 * @param relation Data relation (for representatives specified per DBID) 97 * @return Some {@link NumberVector}, {@code null} if not supported. 98 */ getPrototype(Model model, Relation<? extends NumberVector> relation)99 public static NumberVector getPrototype(Model model, Relation<? extends NumberVector> relation) { 100 // Mean model contains a numeric Vector 101 if(model instanceof MeanModel) { 102 return DoubleVector.wrap(((MeanModel) model).getMean()); 103 } 104 // Handle medoid models 105 if(model instanceof MedoidModel) { 106 return relation.get(((MedoidModel) model).getMedoid()); 107 } 108 if(model instanceof PrototypeModel) { 109 Object p = ((PrototypeModel<?>) model).getPrototype(); 110 if(p instanceof NumberVector) { 111 return (NumberVector) p; 112 } 113 return null; // Inconvertible 114 } 115 return null; 116 } 117 118 /** 119 * Get the representative vector for a cluster model, or compute the centroid. 120 * 121 * @param model Model 122 * @param relation Data relation (for representatives specified per DBID) 123 * @param ids Cluster ids (must not be empty. 124 * @return Vector of type V, {@code null} if not supported. 125 * @param <V> desired vector type 126 */ getPrototypeOrCentroid(Model model, Relation<? extends V> relation, DBIDs ids, NumberVector.Factory<V> factory)127 public static <V extends NumberVector> V getPrototypeOrCentroid(Model model, Relation<? extends V> relation, DBIDs ids, NumberVector.Factory<V> factory) { 128 assert (ids.size() > 0); 129 V v = getPrototype(model, relation, factory); 130 return v != null ? v : factory.newNumberVector(Centroid.make(relation, ids)); 131 } 132 133 /** 134 * Get the representative vector for a cluster model, or compute the centroid. 135 * 136 * @param model Model 137 * @param relation Data relation (for representatives specified per DBID) 138 * @param ids Cluster ids (must not be empty. 139 * @return Some {@link NumberVector}. 140 */ getPrototypeOrCentroid(Model model, Relation<? extends NumberVector> relation, DBIDs ids)141 public static NumberVector getPrototypeOrCentroid(Model model, Relation<? extends NumberVector> relation, DBIDs ids) { 142 assert (ids.size() > 0); 143 NumberVector v = getPrototype(model, relation); 144 return v != null ? v : Centroid.make(relation, ids); 145 } 146 } 147