|
|
@@ -1,11 +1,6 @@
|
|
|
package de.jplag.clustering.algorithm;
|
|
|
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.Collection;
|
|
|
-import java.util.HashSet;
|
|
|
-import java.util.List;
|
|
|
-import java.util.PriorityQueue;
|
|
|
-import java.util.Set;
|
|
|
+import java.util.*;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
import org.apache.commons.math3.linear.RealMatrix;
|
|
|
@@ -32,32 +27,29 @@ public class AgglomerativeClustering implements GenericClusteringAlgorithm {
|
|
|
// clusters, those need to be ignored.
|
|
|
PriorityQueue<ClusterConnection> similarities;
|
|
|
|
|
|
- {
|
|
|
- // initialization
|
|
|
+ // initialization
|
|
|
|
|
|
- List<Cluster> initialClusters = new ArrayList<>(size);
|
|
|
- for (int i = 0; i < size; i++) {
|
|
|
- List<Integer> members = new ArrayList<>();
|
|
|
- members.add(i);
|
|
|
- Cluster cluster = new Cluster(members);
|
|
|
- initialClusters.add(cluster);
|
|
|
- clusters.add(cluster);
|
|
|
- }
|
|
|
+ List<Cluster> initialClusters = new ArrayList<>(size);
|
|
|
+ for (int i = 0; i < size; i++) {
|
|
|
+ List<Integer> members = new ArrayList<>();
|
|
|
+ members.add(i);
|
|
|
+ Cluster cluster = new Cluster(members);
|
|
|
+ initialClusters.add(cluster);
|
|
|
+ clusters.add(cluster);
|
|
|
+ }
|
|
|
|
|
|
- List<ClusterConnection> initialSimilarities = new ArrayList<>(size * (size - 1) / 2);
|
|
|
+ List<ClusterConnection> initialSimilarities = new ArrayList<>(size * (size - 1) / 2);
|
|
|
|
|
|
- for (int leftIndex = 0; leftIndex < initialClusters.size(); leftIndex++) {
|
|
|
- Cluster leftCluster = initialClusters.get(leftIndex);
|
|
|
- for (int rightIndex = leftIndex + 1; rightIndex < initialClusters.size(); rightIndex++) {
|
|
|
- Cluster rightCluster = initialClusters.get(rightIndex);
|
|
|
- initialSimilarities
|
|
|
- .add(new ClusterConnection(leftCluster, rightCluster, (float) similarityMatrix.getEntry(leftIndex, rightIndex)));
|
|
|
- }
|
|
|
+ for (int leftIndex = 0; leftIndex < initialClusters.size(); leftIndex++) {
|
|
|
+ Cluster leftCluster = initialClusters.get(leftIndex);
|
|
|
+ for (int rightIndex = leftIndex + 1; rightIndex < initialClusters.size(); rightIndex++) {
|
|
|
+ Cluster rightCluster = initialClusters.get(rightIndex);
|
|
|
+ initialSimilarities.add(new ClusterConnection(leftCluster, rightCluster, (float) similarityMatrix.getEntry(leftIndex, rightIndex)));
|
|
|
}
|
|
|
-
|
|
|
- similarities = new PriorityQueue<>(initialSimilarities);
|
|
|
}
|
|
|
|
|
|
+ similarities = new PriorityQueue<>(initialSimilarities);
|
|
|
+
|
|
|
while (clusters.size() > 1) {
|
|
|
ClusterConnection nearest = similarities.poll();
|
|
|
// TODO Check that nearest is never null
|
|
|
@@ -70,8 +62,8 @@ public class AgglomerativeClustering implements GenericClusteringAlgorithm {
|
|
|
}
|
|
|
clusters.remove(nearest.left);
|
|
|
clusters.remove(nearest.right);
|
|
|
- nearest.left.getSubmissions().addAll(nearest.right.getSubmissions());
|
|
|
- Cluster combined = new Cluster(nearest.left.getSubmissions());
|
|
|
+ nearest.left.submissions().addAll(nearest.right.submissions());
|
|
|
+ Cluster combined = new Cluster(nearest.left.submissions());
|
|
|
for (Cluster otherCluster : clusters) {
|
|
|
float similarity = options.getAgglomerativeInterClusterSimilarity().clusterSimilarity(combined.submissions, otherCluster.submissions,
|
|
|
similarityMatrix);
|
|
|
@@ -80,21 +72,10 @@ public class AgglomerativeClustering implements GenericClusteringAlgorithm {
|
|
|
clusters.add(combined);
|
|
|
}
|
|
|
|
|
|
- return clusters.stream().map(Cluster::getSubmissions).collect(Collectors.toList());
|
|
|
+ return clusters.stream().map(Cluster::submissions).collect(Collectors.toList());
|
|
|
}
|
|
|
|
|
|
- private static class ClusterConnection implements Comparable<ClusterConnection> {
|
|
|
-
|
|
|
- private final Cluster left;
|
|
|
- private final Cluster right;
|
|
|
- private final float similarity;
|
|
|
-
|
|
|
- public ClusterConnection(Cluster left, Cluster right, float similarity) {
|
|
|
- this.left = left;
|
|
|
- this.right = right;
|
|
|
- this.similarity = similarity;
|
|
|
- }
|
|
|
-
|
|
|
+ private record ClusterConnection(Cluster left, Cluster right, float similarity) implements Comparable<ClusterConnection> {
|
|
|
@Override
|
|
|
public int compareTo(ClusterConnection other) {
|
|
|
return (int) Math.signum(other.similarity - similarity);
|
|
|
@@ -106,16 +87,7 @@ public class AgglomerativeClustering implements GenericClusteringAlgorithm {
|
|
|
* Encapsulate a list in a class because we do not want hashing based on the members but on identity only. Also a
|
|
|
* cluster need a different identity than the list because the lists are reused.
|
|
|
*/
|
|
|
- private static class Cluster {
|
|
|
- private final List<Integer> submissions;
|
|
|
-
|
|
|
- public Cluster(List<Integer> submissions) {
|
|
|
- this.submissions = submissions;
|
|
|
- }
|
|
|
-
|
|
|
- public List<Integer> getSubmissions() {
|
|
|
- return submissions;
|
|
|
- }
|
|
|
+ private record Cluster(List<Integer> submissions) {
|
|
|
}
|
|
|
|
|
|
}
|