/**
* Copyright (C) 2013-2017 Vasilis Vryniotis
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.datumbox.framework.core.machinelearning.clustering;
import com.datumbox.framework.common.Configuration;
import com.datumbox.framework.common.concurrency.ForkJoinStream;
import com.datumbox.framework.common.concurrency.StreamMethods;
import com.datumbox.framework.common.dataobjects.AssociativeArray;
import com.datumbox.framework.core.common.dataobjects.Dataframe;
import com.datumbox.framework.core.common.dataobjects.Record;
import com.datumbox.framework.common.storage.interfaces.StorageEngine;
import com.datumbox.framework.common.storage.interfaces.StorageEngine.MapType;
import com.datumbox.framework.common.storage.interfaces.StorageEngine.StorageHint;
import com.datumbox.framework.core.common.utilities.MapMethods;
import com.datumbox.framework.core.machinelearning.common.abstracts.AbstractTrainer;
import com.datumbox.framework.core.machinelearning.common.abstracts.modelers.AbstractClusterer;
import com.datumbox.framework.core.machinelearning.common.interfaces.PredictParallelizable;
import com.datumbox.framework.core.machinelearning.common.interfaces.TrainParallelizable;
import com.datumbox.framework.core.mathematics.distances.Distance;
import com.datumbox.framework.core.statistics.descriptivestatistics.Descriptives;
import java.util.*;
/**
* This class implements the Hierarchical Agglomerative clustering algorithm
* supporting different Linkage and Distance methods.
*
* References:
* http://nlp.stanford.edu/IR-book/html/htmledition/hierarchical-agglomerative-clustering-1.html
* http://php-nlp-tools.com/posts/faster-hierarchical-clustering.html
*
* @author Vasilis Vryniotis
*/
public class HierarchicalAgglomerative extends AbstractClusterer implements PredictParallelizable, TrainParallelizable {
/** {@inheritDoc} */
public static class Cluster extends AbstractClusterer.AbstractCluster {
private static final long serialVersionUID = 1L;
private Record centroid;
private boolean active = true;
private final AssociativeArray xi_sum;
/**
* @param clusterId
* @see AbstractCluster#AbstractCluster(java.lang.Integer)
*/
protected Cluster(int clusterId) {
super(clusterId);
centroid = new Record(new AssociativeArray(), null);
xi_sum = new AssociativeArray();
}
/**
* Returns the centroid of the cluster.
*
* @return
*/
public Record getCentroid() {
return centroid;
}
/**
* Merges this cluster with the provided cluster.
*
* @param c
*/
protected void merge(Cluster c) {
xi_sum.addValues(c.xi_sum);
size += c.size;
}
/**
* Updates the cluster parameters.
*
* @return
*/
protected boolean updateClusterParameters() {
boolean changed=false;
AssociativeArray centoidValues = xi_sum.copy();
if(size>0) {
centoidValues.multiplyValues(1.0/size);
}
if(!centroid.getX().equals(centoidValues)) {
changed=true;
centroid = new Record(centoidValues, centroid.getY());
}
return changed;
}
/**
* Getter for whether the cluster is active.
*
* @return
*/
protected boolean isActive() {
return active;
}
/**
* Setter for whether the cluster is active.
*
* @param active
*/
protected void setActive(boolean active) {
this.active = active;
}
/** {@inheritDoc} */
@Override
protected void add(Record r) {
size++;
xi_sum.addValues(r.getX());
}
/** {@inheritDoc} */
@Override
protected void remove(Record r) {
throw new UnsupportedOperationException("Remove operation is not supported.");
}
/** {@inheritDoc} */
@Override
protected void clear() {
xi_sum.clear();
}
}
/** {@inheritDoc} */
public static class ModelParameters extends AbstractClusterer.AbstractModelParameters {
private static final long serialVersionUID = 1L;
/**
* @param storageEngine
* @see AbstractTrainer.AbstractModelParameters#AbstractModelParameters(StorageEngine)
*/
protected ModelParameters(StorageEngine storageEngine) {
super(storageEngine);
}
}
/** {@inheritDoc} */
public static class TrainingParameters extends AbstractClusterer.AbstractTrainingParameters {
private static final long serialVersionUID = 1L;
/**
* The Linkage method used in the calculations.
*/
public enum Linkage {
/**
* Average Linkage (all points to all points).
*/
AVERAGE,
/**
* Nearest Neighbour.
*/
SINGLE,
/**
* Farther Neighbour.
*/
COMPLETE;
}
/**
* The Distance method used in the calculations.
*/
public enum Distance {
/**
* Euclidian distance.
*/
EUCLIDIAN,
/**
* Manhattan distance.
*/
MANHATTAN,
/**
* Maximum distance.
*/
MAXIMUM;
}
//Vars
private Linkage linkageMethod = Linkage.COMPLETE;
private Distance distanceMethod = Distance.EUCLIDIAN;
private double maxDistanceThreshold = Double.MAX_VALUE;
private double minClustersThreshold = 2;
//Getters Setters
/**
* Getter for Linkage Method.
*
* @return
*/
public Linkage getLinkageMethod() {
return linkageMethod;
}
/**
* Setter for Linkage Method.
*
* @param linkageMethod
*/
public void setLinkageMethod(Linkage linkageMethod) {
this.linkageMethod = linkageMethod;
}
/**
* Getter for Distance Method.
*
* @return
*/
public Distance getDistanceMethod() {
return distanceMethod;
}
/**
* Setter for Distance Method.
*
* @param distanceMethod
*/
public void setDistanceMethod(Distance distanceMethod) {
this.distanceMethod = distanceMethod;
}
/**
* Getter for the maximum distance threshold.
*
* @return
*/
public double getMaxDistanceThreshold() {
return maxDistanceThreshold;
}
/**
* Setter for the maximum distance threshold.
*
* @param maxDistanceThreshold
*/
public void setMaxDistanceThreshold(double maxDistanceThreshold) {
this.maxDistanceThreshold = maxDistanceThreshold;
}
/**
* Getter for the minimum clusters threshold.
*
* @return
*/
public double getMinClustersThreshold() {
return minClustersThreshold;
}
/**
* Setter for the minimum clusters threshold.
*
* @param minClustersThreshold
*/
public void setMinClustersThreshold(double minClustersThreshold) {
this.minClustersThreshold = minClustersThreshold;
}
}
/**
* @param trainingParameters
* @param configuration
* @see AbstractTrainer#AbstractTrainer(AbstractTrainingParameters, Configuration)
*/
protected HierarchicalAgglomerative(TrainingParameters trainingParameters, Configuration configuration) {
super(trainingParameters, configuration);
streamExecutor = new ForkJoinStream(knowledgeBase.getConfiguration().getConcurrencyConfiguration());
}
/**
* @param storageName
* @param configuration
* @see AbstractTrainer#AbstractTrainer(String, Configuration)
*/
protected HierarchicalAgglomerative(String storageName, Configuration configuration) {
super(storageName, configuration);
streamExecutor = new ForkJoinStream(knowledgeBase.getConfiguration().getConcurrencyConfiguration());
}
private boolean parallelized = true;
/**
* This executor is used for the parallel processing of streams with custom
* Thread pool.
*/
protected final ForkJoinStream streamExecutor;
/** {@inheritDoc} */
@Override
public boolean isParallelized() {
return parallelized;
}
/** {@inheritDoc} */
@Override
public void setParallelized(boolean parallelized) {
this.parallelized = parallelized;
}
/** {@inheritDoc} */
@Override
protected void _predict(Dataframe newData) {
_predictDatasetParallel(newData, knowledgeBase.getStorageEngine(), knowledgeBase.getConfiguration().getConcurrencyConfiguration());
}
/** {@inheritDoc} */
@Override
public Prediction _predictRecord(Record r) {
ModelParameters modelParameters = knowledgeBase.getModelParameters();
Map clusterMap = modelParameters.getClusterMap();
AssociativeArray clusterDistances = new AssociativeArray();
for(Map.Entry e : clusterMap.entrySet()) {
Integer clusterId = e.getKey();
Cluster c = e.getValue();
double distance = calculateDistance(r, c.getCentroid());
clusterDistances.put(clusterId, distance);
}
Descriptives.normalize(clusterDistances);
return new Prediction(getSelectedClusterFromDistances(clusterDistances), clusterDistances);
}
/** {@inheritDoc} */
@Override
protected void _fit(Dataframe trainingData) {
ModelParameters modelParameters = knowledgeBase.getModelParameters();
Set
Ads help maintain this website.