计算xy点密度最高的中心点
Calculate the center point of the highest density of xy points
我有一个方形网格 (200 x 200)。我希望用户能够在此 space 中对对象的下一个期望位置进行投票。当用户投票时,我将 XY 位置添加到数组中。
目前为了找到 "winning" 位置,我只取 X 的平均值和 Y 的平均值,但我发现获胜的位置往往集中在该区域的中间(自然地这是一个平均值!)。
白色=投票,黄色="winning" XY点
我更喜欢它找到投票最密集的区域,然后选择中间的一个点。
即单个分散的选票不会像平均那样将获胜位置拉离密集区域太远。
这可能吗?
示例数组
[ { x: 39, y: 28 },
{ x: 69, y: 33 },
{ x: 51, y: 51 },
{ x: 14, y: 63 },
{ x: 75, y: 140 },
{ x: 171, y: 68 },
{ x: 140, y: 53 },
{ x: 173, y: 150 },
{ x: 123, y: 179 },
{ x: 103, y: 150 },
{ x: 145, y: 119 },
{ x: 92, y: 85 },
{ x: 58, y: 49 },
{ x: 28, y: 65 },
{ x: 41, y: 39 },
{ x: 46, y: 41 },
{ x: 49, y: 51 },
{ x: 43, y: 55 },
{ x: 35, y: 48 },
{ x: 29, y: 31 },
{ x: 68, y: 22 },
{ x: 58, y: 39 } ]
这其实很简单 - 将所有 X 组合成 1 个变量,然后除以 X 的个数。同样适用于 y:
var totalX = 0, totalY = 0, avgX, avgY;
var coords = [ { x: 39, y: 28 },
{ x: 69, y: 33 },
{ x: 51, y: 51 },
{ x: 14, y: 63 },
{ x: 75, y: 140 },
{ x: 171, y: 68 },
{ x: 140, y: 53 },
{ x: 173, y: 150 },
{ x: 123, y: 179 },
{ x: 103, y: 150 },
{ x: 145, y: 119 },
{ x: 92, y: 85 },
{ x: 58, y: 49 },
{ x: 28, y: 65 },
{ x: 41, y: 39 },
{ x: 46, y: 41 },
{ x: 49, y: 51 },
{ x: 43, y: 55 },
{ x: 35, y: 48 },
{ x: 29, y: 31 },
{ x: 68, y: 22 },
{ x: 58, y: 39 } ]
for (var i = 0; i <= coords.length; i++) {
totalX += coords[i].x;
totalY += coords[i].y;
}
avgX = totalX / coords.length;
avgY = totalY / coords.length;
看到它在行动哈尔(笑在ID):https://jsfiddle.net/harr55d2/
编辑:这不是正确答案,抱歉,误读了您的问题。
不过,您可以做的是占据这个平均位置,然后从那里计算到所有点的距离。你会从中得到一些数字。如果您然后计算距离的平均值,我认为您会接近您想要的点。
另一种方法是以 20x20 块为单位扫描 200x200 区域(或其他东西,也许圆圈效果最好)并计算其中的所有选票。得票最多者获胜。
编辑:另一种方法;
取每张选票与平均点的距离。那么就是某票的"score"或"weight"。然后您可以看到最接近平均数的选票被加权 "best"。如果你明白我的意思。
一个解决方案可以是为每个点计算从它到所有其他点的距离总和。总和最小的点应该是密度最高的区域的中心。
好吧,我不只是发布一个想法,而是发布实际代码。希望你能看看它是否适合你。
你的问题基本属于聚类分析范畴。您想要识别数据集中存在的不同数据组并形成一个集群。 https://en.wikipedia.org/wiki/Cluster_analysis
幸运的是,这个问题已经有一些方法可以解决,甚至还有一个 NPM 包可以让你执行一些众所周知的集群分析工具。
我选择了 DBSCAN 来解决你的点集,如果你觉得你需要别的东西,很幸运可以尝试另一种算法。 https://en.wikipedia.org/wiki/DBSCAN
我使用了 NPM 的密度聚类库。 https://github.com/LukaszKrawczyk/density-clustering
给予 DBSCAN 的任意参数是 20 邻域半径和 5最少要考虑的点数 a "cluster"
并且该代码是 NPM 包的 github 页面中给出的示例的修改版本。
var sample = [ { x: 39, y: 28 },
{ x: 69, y: 33 },
{ x: 51, y: 51 },
{ x: 14, y: 63 },
{ x: 75, y: 140 },
{ x: 171, y: 68 },
{ x: 140, y: 53 },
{ x: 173, y: 150 },
{ x: 123, y: 179 },
{ x: 103, y: 150 },
{ x: 145, y: 119 },
{ x: 92, y: 85 },
{ x: 58, y: 49 },
{ x: 28, y: 65 },
{ x: 41, y: 39 },
{ x: 46, y: 41 },
{ x: 49, y: 51 },
{ x: 43, y: 55 },
{ x: 35, y: 48 },
{ x: 29, y: 31 },
{ x: 68, y: 22 },
{ x: 58, y: 39 } ];
/* Transform your dataset to the format expected by the library */
var dataset = [];
for(var i=0; i<sample.length; i++){
dataset.push([sample[i].x, sample[i].y]);
}
var clustering = require('density-clustering');
var dbscan = new clustering.DBSCAN();
/* parameters:
20 - neighborhood radius
5 - number of points in neighborhood to form a cluster
*/
var clusters = dbscan.run(dataset, 20, 5);
if(clusters.length > 0){
/* Find the biggest cluster */
var biggestCluster = clusters[0];
for(i=1;i<clusters.length;i++){
if(cluster[i].length > biggestCluster.length){
biggestCluster = cluster[i];
}
}
/* The output of the library contains the indexes of the points in the cluster, not the coordinates, so we need to get the point from the dataset */
var clusterPoints = [];
var sumx = 0;
var sumy = 0;
for(i=0;i<biggestCluster.length;i++){
var point = dataset[biggestCluster[i]];
clusterPoints.push(point);
/* It's also a good opportunity to cumulate x and y so we can get the average */
sumx+=point[0];
sumy+=point[1];
}
console.log('Cluster:', clusterPoints);
console.log('Center:', sumx/clusterPoints.length, sumy / clusterPoints.length);
}
else{
console.log('No clusters');
}
这个程序的输出将是:
Cluster: [ [ 51, 51 ], [ 58, 49 ], [ 41, 39 ], [ 46, 41 ], [ 49, 51 ], [ 43, 55 ], [ 35, 48 ], [ 58, 39 ], [ 69, 33 ], [ 39, 28 ], [ 29, 31 ], [ 28, 65 ], [ 68, 22 ] ]
Center: 47.23076923076923 42.46153846153846
我有一个方形网格 (200 x 200)。我希望用户能够在此 space 中对对象的下一个期望位置进行投票。当用户投票时,我将 XY 位置添加到数组中。
目前为了找到 "winning" 位置,我只取 X 的平均值和 Y 的平均值,但我发现获胜的位置往往集中在该区域的中间(自然地这是一个平均值!)。
白色=投票,黄色="winning" XY点
我更喜欢它找到投票最密集的区域,然后选择中间的一个点。
即单个分散的选票不会像平均那样将获胜位置拉离密集区域太远。
这可能吗?
示例数组
[ { x: 39, y: 28 },
{ x: 69, y: 33 },
{ x: 51, y: 51 },
{ x: 14, y: 63 },
{ x: 75, y: 140 },
{ x: 171, y: 68 },
{ x: 140, y: 53 },
{ x: 173, y: 150 },
{ x: 123, y: 179 },
{ x: 103, y: 150 },
{ x: 145, y: 119 },
{ x: 92, y: 85 },
{ x: 58, y: 49 },
{ x: 28, y: 65 },
{ x: 41, y: 39 },
{ x: 46, y: 41 },
{ x: 49, y: 51 },
{ x: 43, y: 55 },
{ x: 35, y: 48 },
{ x: 29, y: 31 },
{ x: 68, y: 22 },
{ x: 58, y: 39 } ]
这其实很简单 - 将所有 X 组合成 1 个变量,然后除以 X 的个数。同样适用于 y:
var totalX = 0, totalY = 0, avgX, avgY;
var coords = [ { x: 39, y: 28 },
{ x: 69, y: 33 },
{ x: 51, y: 51 },
{ x: 14, y: 63 },
{ x: 75, y: 140 },
{ x: 171, y: 68 },
{ x: 140, y: 53 },
{ x: 173, y: 150 },
{ x: 123, y: 179 },
{ x: 103, y: 150 },
{ x: 145, y: 119 },
{ x: 92, y: 85 },
{ x: 58, y: 49 },
{ x: 28, y: 65 },
{ x: 41, y: 39 },
{ x: 46, y: 41 },
{ x: 49, y: 51 },
{ x: 43, y: 55 },
{ x: 35, y: 48 },
{ x: 29, y: 31 },
{ x: 68, y: 22 },
{ x: 58, y: 39 } ]
for (var i = 0; i <= coords.length; i++) {
totalX += coords[i].x;
totalY += coords[i].y;
}
avgX = totalX / coords.length;
avgY = totalY / coords.length;
看到它在行动哈尔(笑在ID):https://jsfiddle.net/harr55d2/
编辑:这不是正确答案,抱歉,误读了您的问题。
不过,您可以做的是占据这个平均位置,然后从那里计算到所有点的距离。你会从中得到一些数字。如果您然后计算距离的平均值,我认为您会接近您想要的点。
另一种方法是以 20x20 块为单位扫描 200x200 区域(或其他东西,也许圆圈效果最好)并计算其中的所有选票。得票最多者获胜。
编辑:另一种方法;
取每张选票与平均点的距离。那么就是某票的"score"或"weight"。然后您可以看到最接近平均数的选票被加权 "best"。如果你明白我的意思。
一个解决方案可以是为每个点计算从它到所有其他点的距离总和。总和最小的点应该是密度最高的区域的中心。
好吧,我不只是发布一个想法,而是发布实际代码。希望你能看看它是否适合你。
你的问题基本属于聚类分析范畴。您想要识别数据集中存在的不同数据组并形成一个集群。 https://en.wikipedia.org/wiki/Cluster_analysis
幸运的是,这个问题已经有一些方法可以解决,甚至还有一个 NPM 包可以让你执行一些众所周知的集群分析工具。
我选择了 DBSCAN 来解决你的点集,如果你觉得你需要别的东西,很幸运可以尝试另一种算法。 https://en.wikipedia.org/wiki/DBSCAN
我使用了 NPM 的密度聚类库。 https://github.com/LukaszKrawczyk/density-clustering
给予 DBSCAN 的任意参数是 20 邻域半径和 5最少要考虑的点数 a "cluster"
并且该代码是 NPM 包的 github 页面中给出的示例的修改版本。
var sample = [ { x: 39, y: 28 },
{ x: 69, y: 33 },
{ x: 51, y: 51 },
{ x: 14, y: 63 },
{ x: 75, y: 140 },
{ x: 171, y: 68 },
{ x: 140, y: 53 },
{ x: 173, y: 150 },
{ x: 123, y: 179 },
{ x: 103, y: 150 },
{ x: 145, y: 119 },
{ x: 92, y: 85 },
{ x: 58, y: 49 },
{ x: 28, y: 65 },
{ x: 41, y: 39 },
{ x: 46, y: 41 },
{ x: 49, y: 51 },
{ x: 43, y: 55 },
{ x: 35, y: 48 },
{ x: 29, y: 31 },
{ x: 68, y: 22 },
{ x: 58, y: 39 } ];
/* Transform your dataset to the format expected by the library */
var dataset = [];
for(var i=0; i<sample.length; i++){
dataset.push([sample[i].x, sample[i].y]);
}
var clustering = require('density-clustering');
var dbscan = new clustering.DBSCAN();
/* parameters:
20 - neighborhood radius
5 - number of points in neighborhood to form a cluster
*/
var clusters = dbscan.run(dataset, 20, 5);
if(clusters.length > 0){
/* Find the biggest cluster */
var biggestCluster = clusters[0];
for(i=1;i<clusters.length;i++){
if(cluster[i].length > biggestCluster.length){
biggestCluster = cluster[i];
}
}
/* The output of the library contains the indexes of the points in the cluster, not the coordinates, so we need to get the point from the dataset */
var clusterPoints = [];
var sumx = 0;
var sumy = 0;
for(i=0;i<biggestCluster.length;i++){
var point = dataset[biggestCluster[i]];
clusterPoints.push(point);
/* It's also a good opportunity to cumulate x and y so we can get the average */
sumx+=point[0];
sumy+=point[1];
}
console.log('Cluster:', clusterPoints);
console.log('Center:', sumx/clusterPoints.length, sumy / clusterPoints.length);
}
else{
console.log('No clusters');
}
这个程序的输出将是:
Cluster: [ [ 51, 51 ], [ 58, 49 ], [ 41, 39 ], [ 46, 41 ], [ 49, 51 ], [ 43, 55 ], [ 35, 48 ], [ 58, 39 ], [ 69, 33 ], [ 39, 28 ], [ 29, 31 ], [ 28, 65 ], [ 68, 22 ] ]
Center: 47.23076923076923 42.46153846153846