As input I have a 2D array PointXY[][] clusters
, which looks like this:
[[23.237633,53.78671], [69.15293,17.138134], [23.558687,45.70517]] . . .
[[47
Create a temporary float/double array of size [n][(n-1) * n] where input matrix is of the size [n][n-1].
Compute the euclidean distances of all the points in the lower part of the matrix with all the points in the first row and store them at their respective positions in the temporary array.
Create a copy of each temporary sub-array.
Perform any sorting operation on the copy, preferably selection sort as you only need to sort the array partially until the lowest n-1 elements are found.
Finally, create a new output array of size [n][n-1], correspond the lowest euclidean distances to their points, and store the sorted (n-1) element groups of (n-1) points right below their closest reference points.
See another answer: Sorting a 2d array of objects by columns starting from the second row and transposing an array
Building sorted cluster. First row are the center points. Other points are grouped by the nearest center. In this case all points are grouped by the second center point:
PointXY[][] clusters = {
{new PointXY(23.237633, 53.78671),
new PointXY(69.15293, 17.138134),
new PointXY(23.558687, 45.70517)},
{new PointXY(47.851738, 16.525734),
new PointXY(47.802097, 16.689285),
new PointXY(47.946404, 16.732542)},
{new PointXY(47.89601, 16.638218),
new PointXY(47.833263, 16.478987),
new PointXY(47.88203, 16.45793)},
{new PointXY(47.75438, 16.549816),
new PointXY(47.915512, 16.506475),
new PointXY(47.768547, 16.67624)}};
// array of a center points
PointXY[] centers = clusters[0];
PointXY[][] clustersSorted = Arrays
// iterate over array of center points
.stream(centers)
// for each center point
.map(center -> Stream.of(center, Arrays
// iterate over array of clusters starting from the second row
.stream(clusters, 1, clusters.length)
// stream over the full array
.flatMap(Arrays::stream)
// filter nearest points to the current center point
.filter(point -> Arrays
// iterate over array of center points
.stream(centers)
// sort by euclidean distance from current point
.sorted(Comparator.comparingDouble(centerXY ->
Math.sqrt(Math.pow(centerXY.x - point.x, 2)
+ Math.pow(centerXY.y - point.y, 2))))
// find nearest center point to the current point
.findFirst()
// check this center point is the current center point
.get() == center)
// array of the nearest points to this center point
.toArray(PointXY[]::new))
// center point + array of its nearest points
.flatMap(element -> element instanceof PointXY ?
Stream.of((PointXY) element) :
Arrays.stream((PointXY[]) element))
// sorted cluster
.toArray(PointXY[]::new))
// sorted array of clusters
.toArray(PointXY[][]::new);
// output
Arrays.stream(clustersSorted).map(Arrays::toString).forEach(System.out::println);
[23.237633,53.78671]
[69.15293,17.138134, 47.851738,16.525734, 47.802097,16.689285, 47.946404,16.732542, 47.89601,16.638218, 47.833263,16.478987, 47.88203,16.45793, 47.75438,16.549816, 47.915512,16.506475, 47.768547,16.67624]
[23.558687,45.70517]
Class PointXY
should look like this:
public static class PointXY {
double x, y;
public PointXY(double x, double y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return x + "," + y;
}
}
See another answer: Grouping all points starting from the second row by the nearest center point from the first row
To sort a 2d array of objects by columns, you can first transpose this array and sort each row, and then transpose it back. To sort a row of elements starting from the second one by the distance from the first one with a custom comparator - you can use Arrays.sort(T[],int,int,Comparator) method:
int m = 4;
int n = 3;
PointXY[][] clusters = {
{new PointXY(23.237633, 53.78671),
new PointXY(69.15293, 17.138134),
new PointXY(23.558687, 45.70517)},
{new PointXY(47.851738, 16.525734),
new PointXY(47.802097, 16.689285),
new PointXY(47.946404, 16.732542)},
{new PointXY(47.89601, 16.638218),
new PointXY(47.833263, 16.478987),
new PointXY(47.88203, 16.45793)},
{new PointXY(47.75438, 16.549816),
new PointXY(47.915512, 16.506475),
new PointXY(47.768547, 16.67624)}};
// transpose a matrix
PointXY[][] transposed = new PointXY[n][m];
IntStream.range(0, n).forEach(i ->
IntStream.range(0, m).forEach(j ->
transposed[i][j] = clusters[j][i]));
// sort each line starting from the second
// element by the distance from the first element
Arrays.stream(transposed).forEach(cluster ->
Arrays.sort(cluster, 1, cluster.length,
Comparator.comparingDouble(point ->
Math.sqrt(Math.pow(cluster[0].x - point.x, 2)
+ Math.pow(cluster[0].y - point.y, 2)))));
// transpose a matrix back
PointXY[][] clustersSorted = new PointXY[m][n];
IntStream.range(0, m).forEach(i ->
IntStream.range(0, n).forEach(j ->
clustersSorted[i][j] = transposed[j][i]));
// output
Arrays.stream(clustersSorted).map(Arrays::toString).forEach(System.out::println);
[23.237633,53.78671, 69.15293,17.138134, 23.558687,45.70517]
[47.75438,16.549816, 47.915512,16.506475, 47.768547,16.67624]
[47.89601,16.638218, 47.833263,16.478987, 47.946404,16.732542]
[47.851738,16.525734, 47.802097,16.689285, 47.88203,16.45793]
Class PointXY
should look like this:
public class PointXY {
double x, y;
public PointXY(double x, double y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return x + "," + y;
}
}