001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.experiment.evaluation.cluster; 031 032import java.util.ArrayList; 033import java.util.HashMap; 034import java.util.List; 035import java.util.Map; 036import java.util.Map.Entry; 037 038import org.openimaj.experiment.evaluation.AnalysisResult; 039import org.openimaj.experiment.evaluation.Evaluator; 040import org.openimaj.experiment.evaluation.cluster.analyser.ClusterAnalyser; 041import org.openimaj.ml.clustering.dbscan.SparseMatrixDBSCAN; 042import org.openimaj.util.function.Function; 043import org.openimaj.util.iterator.UniformDoubleRangeIterable; 044 045import ch.akuhn.matrix.SparseMatrix; 046 047/** 048 * @author Sina Samangooei (ss@ecs.soton.ac.uk) 049 * 050 * @param <D> 051 * The type of data which the internal clusterer can cluster lists of 052 * @param <T> 053 * The type of results the 054 */ 055public class RangedDBSCANClusterEvaluator<D, T extends AnalysisResult> 056 implements 057 Evaluator<Map<Double, int[][]>, RangedAnalysisResult<Double, T>> 058{ 059 060 private int[][] correct; 061 private ClusterAnalyser<T> analyser; 062 private SparseMatrixDBSCAN gen; 063 private SparseMatrix data; 064 UniformDoubleRangeIterable r; 065 066 /** 067 * @param r 068 * the range of values for the {@link SparseMatrixDBSCAN} eps 069 * value 070 * @param gen 071 * @param data 072 * @param clusters 073 * @param analyser 074 */ 075 public RangedDBSCANClusterEvaluator(UniformDoubleRangeIterable r, SparseMatrixDBSCAN gen, SparseMatrix data, 076 int[][] clusters, ClusterAnalyser<T> analyser) 077 { 078 this.gen = gen; 079 this.correct = clusters; 080 this.analyser = analyser; 081 this.data = data; 082 } 083 084 /** 085 * @param r 086 * the range of values for the {@link SparseMatrixDBSCAN} eps 087 * value 088 * @param gen 089 * @param data 090 * @param dataset 091 * extract the elements of this map "in order" and build a ground 092 * truth. very dangerous. 093 * @param analyser 094 */ 095 public <A, B> RangedDBSCANClusterEvaluator(UniformDoubleRangeIterable r, SparseMatrixDBSCAN gen, SparseMatrix data, 096 Map<A, ? extends List<B>> dataset, ClusterAnalyser<T> analyser) 097 { 098 this.r = r; 099 this.gen = gen; 100 this.correct = new int[dataset.size()][]; 101 int j = 0; 102 int k = 0; 103 for (final Entry<A, ? extends List<B>> es : dataset.entrySet()) { 104 this.correct[j] = new int[es.getValue().size()]; 105 int i = 0; 106 final List<B> value = es.getValue(); 107 for (int l = 0; l < value.size(); l++) { 108 this.correct[j][i++] = k; 109 k++; 110 } 111 j++; 112 } 113 this.analyser = analyser; 114 this.data = data; 115 } 116 117 /** 118 * @param r 119 * the range of values for the {@link SparseMatrixDBSCAN} eps 120 * value 121 * @param gen 122 * @param data 123 * @param indexFunc 124 * given a data instance, return its index 125 * @param dataset 126 * @param analyser 127 */ 128 public <A, B> RangedDBSCANClusterEvaluator( 129 UniformDoubleRangeIterable r, 130 SparseMatrixDBSCAN gen, 131 SparseMatrix data, 132 Function<B, Integer> indexFunc, 133 Map<A, ? extends List<B>> dataset, 134 ClusterAnalyser<T> analyser) 135 { 136 this.r = r; 137 this.gen = gen; 138 this.correct = new int[dataset.size()][]; 139 int j = 0; 140 for (final Entry<A, ? extends List<B>> es : dataset.entrySet()) { 141 this.correct[j] = new int[es.getValue().size()]; 142 int i = 0; 143 final List<B> value = es.getValue(); 144 for (final B b : value) { 145 this.correct[j][i++] = indexFunc.apply(b); 146 } 147 j++; 148 } 149 this.analyser = analyser; 150 this.data = data; 151 } 152 153 /** 154 * @param r 155 * the range of values for the {@link SparseMatrixDBSCAN} eps 156 * value 157 * @param gen 158 * @param dataset 159 * @param transform 160 * turn a list of dataset items into the required type for 161 * clustering 162 * @param analyser 163 */ 164 public <A, B> RangedDBSCANClusterEvaluator( 165 UniformDoubleRangeIterable r, 166 SparseMatrixDBSCAN gen, 167 Map<A, ? extends List<B>> dataset, 168 Function<List<B>, SparseMatrix> transform, 169 ClusterAnalyser<T> analyser) 170 { 171 this.r = r; 172 this.gen = gen; 173 this.analyser = analyser; 174 this.correct = new int[dataset.size()][]; 175 int j = 0; 176 final List<B> flattened = new ArrayList<B>(); 177 for (final Entry<A, ? extends List<B>> es : dataset.entrySet()) { 178 this.correct[j] = new int[es.getValue().size()]; 179 int i = 0; 180 for (final B b : es.getValue()) { 181 this.correct[j][i++] = flattened.size(); 182 flattened.add(b); 183 } 184 j++; 185 } 186 this.data = transform.apply(flattened); 187 } 188 189 @Override 190 public Map<Double, int[][]> evaluate() { 191 final Map<Double, int[][]> ret = new HashMap<Double, int[][]>(); 192 for (final Double eps : this.r) { 193 this.gen.setEps(eps); 194 ret.put(eps, new ClusterEvaluator<SparseMatrix, T>(gen, data, correct, analyser).evaluate()); 195 } 196 return ret; 197 } 198 199 @Override 200 public RangedAnalysisResult<Double, T> analyse(Map<Double, int[][]> estimated) { 201 final RangedAnalysisResult<Double, T> ret = new RangedAnalysisResult<Double, T>(); 202 for (final Entry<Double, int[][]> ent : estimated.entrySet()) { 203 ret.put(ent.getKey(), this.analyser.analyse(correct, ent.getValue())); 204 } 205 return ret; 206 } 207 208}