This is the web page for the older, density based algorithm in the project which is based upon distances between voids and fits to a Generalized Extreme Value function. Its runtime complexity is between O(Npoints2) and O(Npoints3) while the newer code presented on the main page has a runtime complexity of O(Npixels X log2(Npixels)).

The code is a density based clustering algorithm. The algorithm does not require prior knowledge of the number of clusters, nor does it require a separation distance for association of points, and the algorithm finds non-convex cluster shapes in a statistically based manner that is reproducable. The algorithm learns the association separation distance of points by the statistics of the data itself and applies that in a critical threshold for membership of points within a cluster.

The algorithm, in an unsupervised manner, constructs histograms of rectangular voids in the data and fits Generalized Extreme Value curves to the histograms to learn the background density. Clusters in the data are found as points that are within a separation distance smaller than the critical density for membership.

The results are available as data and visualized through generated html plots. (Improvements to packaging and documentation are in progress.)

More about the density distribution:

The location of the 'background' points in two dimensional space are likely Poisson, that is their locations in a fixed interval of space are independent of one another and occur randomly. The 2-point void densities are formed into a histogram that is well fit by Generalized Extreme Value (GEV) curves. Extreme value distributions are used to describe the maximum or minimum of values drawn from a sample distribution that is essentially exponential.

There are 2 methods for determining clusters in this code:

The code automatically determines which of method (1) and (2) to use.

If the user has better knowledge of which should be applied, can set that with:

useFindMethodForDataWithoutBackgroundPoints() 
or useFindMethodForDataWithBackgroundPoints()

The GEV curve contains 3 independent fitting parameters and the curve is an exponential combined with a polynomial, so it's resulting fitted parameters are not unique, but the curve is useful for characterizing the background point distribution by then integrating under the curve.

The points within a cluster are may have interesting distributions that can be better modeled after they've been found by these means.

Usage as an API:

To use the code with default settings:

  TwoPointCorrelation clusterFinder 
       = new TwoPointCorrelation(x, y, xErrors, yErrors, totalNumberOfPoints);

  clusterFinder.calculateBackground();

  clusterFinder.findClusters();

The results are available as group points or as convex hulls surrounding the groups:

 int n = clusterFinder.getNumberOfGroups()

 int groupNumber = 0;

 To get the hull for groupId 0:
     ArrayPair hull0 = clusterFinder.getGroupHull(groupNumber);

 To get the points in groupId 0:
     ArrayPair group0 = clusterFinder.getGroup(groupNumber)

 To plot the results:
     String plotFilePath = clusterFinder.plotClusters();

If debugging is turned on, plots are generated and those file paths are printed to standard out, and statements are printed to standard out.

To set the background density manually:

 TwoPointCorrelation clusterFinder 
     = new TwoPointCorrelation(x, y, xErrors, yErrors, >getTotalNumberOfPoints());
 clusterFinder.setBackground(0.03f, 0.003f);
 clusterFinder.findClusters();
 String plotFilePath = clusterFinder.plotClusters();

If the centers of the cluster hulls are needed for something else, seeds for a Voronoi diagram, for instance, one can use:

ArrayPair seeds = clusterFinder.getHullCentroids();

The scatter plots and histograms below use d3 js

Note that improvements in the histogram code is *in progress. Currently datasets with a small number of points may have less than ideal solutions.*

Note also that the code has the ability to refine a solution: that is to determine groups and then subtract them from the data and then re-determine the background density from the remaining points. The ability is not enabled by default, but can be with the method setAllowRefinement(). More information is here.


The citation for use of this code in a publication is:

http://code.google.com/p/two-point-correlation/, Nichole King, "Unsupervised Clustering Based Upon Voids in Two-Point Correlation". March 15, 2013.

Note that I wrote the core algorithm in this work (without the automated density calculation) several years ago and the results were part of a publication. What got published was the results from this algorithm used as input for another algorithm that requires knowledge of association radius in order to work. The algorithm that used my algorithm's input required a parameter that was not derivable from it's use alone. Similarly, "k-means clustering" requires knowledge of the number of clusters before use. "k-means++" is an algorithm that suggests one can adjust the number of clusters k, but a statistical method of doing so would still require a density based analysis, and hence, it would need to do the same work as the algorithm here, but presumably less efficiently. Delaunay Triangulation is useful if there are no background points within a dataset, that is all points will be members of a group, and if groups do not have non-convex shapes. KDTrees are useful as a nearest neighbor algorithm, but its use in determining clusters would still require as input, an association radius. Fun stuff, but the core of the algorithm here is was what I needed to create awhile back for work applied to surveys. The addition published here is automation of the background determination and large improvements of the overall algorithm.


Sparsely Populated Background


Moderately Populated Background


More Densely Populated Background


All Background, No Clusters


Non-Convex Morphology Clusters

The cluster "shape" datasets collected at http://cs.joensuu.fi/sipu/datasets/ are fit here with explanations of code settings used and comments about the data.

These clusters were found with the default algorithm settings. No additional settings were needed for the background:

These clusters were found with the default algorithm settings plus using the method useFindMethodForDataWithoutBackgroundPoints();

These clusters were found with the default algorithm settings plus using the method useFindMethodForDataWithoutBackgroundPoints(); Note that these next 3 datasets have the uncommon property of being composed of isolated pairs of points, that is, there are never 3 or more points within the pair point separation of one another. If one wanted to apply a slightly agglomerative approach to finding clusters in those datasets, one could use the found cluster centers in a new histogram and determine the critical density from those. That isn't done in this algorithm in order to keep a uniform way of determining clusters in a statistically reproducable manner.


Performance Metrics

Roughly determined by estimation and measured with a very small number of iterations on a computer with characteristics: 64-bit Intel Core 2 Duo processor w/ speed 2 GHz w/ 4 MB L2 Cache 3GB RAM and a bus speed of 800 MHz

JVM characteristics: J2SE 1.6, 64-bit HotSpot server w/ initial heap size 512 MB and max heap size 1024 MB.

Measurements are from the June 29, 2014 code (e35ea01990b54cc8e88b76e9ce30220a35cdb79d).

Runtime (RT) complexity for findVoids() is less than O(N^2) but it invokes functions that vary from O(1) to O(N-1) as it compares the range between points, so best is less than O(N^2) and worse is less than O(N^3). RT complexity for findGroups() is a little worse than O(N^2). The RT complexity for findGroups is a DFS search over sorted x so is < O(N^2) due to being able to truncate the search over potential neighbors.

(NOTE that improvements in findGroups is in progress. The total RT should be smaller afterwards by using memoization w/ Fibonacci Heaps in the findVoids stage to limit the neighbor search in findGroups's DFS.)

Empirically derived runtimes follow.

  N          findVoids        findGroups     Sys load  Total RT
points      RT      mem       RT      mem     at start
         in sec   in MB     in sec  in MB               in sec

   99      0       0.2        0      0.2      0.7        0     

 1089      1       20         0      20       0.7        1 

 10605     4                  1                          5 

101400    29       14        226     21       1          255

Note that for the datasets with number of points > 15000, the void density is determined through partial sampling rather than complete sampling, so there may be a need occasionally to override the automatic decision for sampling in the code. Methods are provided to override defaults.