我有一个XY列表,我希望按给定距离对它们进行分组,假设它们之间x距离处的所有点都应该分组在不同的列表中。
基本上如果我有A =(0,0),B =(0,1),C =(0,2),我想对所有maxDistance为1的点进行分组,以获得:[ [A,B],[C]];
答案 0 :(得分:0)
我真的不明白你的问题,所以我不确定你想怎么做分组,但这可能会让你朝着正确的方向前进,至少。
(用VB编写,但在C#中几乎相同 - 你也没有说明你的语言偏好):
Dim MyPoints As New List(Of Point)
MyPoints.Add(New Point(0, 0))
MyPoints.Add(New Point(0, 1))
MyPoints.Add(New Point(0, 2))
Dim query = From pt1 In MyPoints
From pt2 In MyPoints
Where Not (pt1.Equals(pt2))
Select New With {.pt1 = pt1, .pt2 = pt2, .dist = Math.Sqrt((pt1.X - pt2.X) ^ 2 + (pt1.Y - pt2.Y) ^ 2)}
答案 1 :(得分:0)
您要做的是命名为clustering,这意味着将一组数据(在您的情况下为二维点)分组为一组具有一些特征(点之间的给定距离)的组。我强烈建议您阅读上面提供的链接,以便更好地理解它。您可能对两种类型的聚类感兴趣:
这完全取决于您拥有多少数据。对于小型集合,您可以尝试自己实现一些简单的算法。对于更大的数据,我更喜欢使用像Numl这样的第三方库,其中包含上述两种类型的方法。
以下是使用Numl进行群集的示例代码。鉴于课程:
class Point
{
[Feature]
public double X { get; set; }
[Feature]
public double Y { get; set; }
public Point(double X, double Y)
{
this.X = X;
this.Y = Y;
}
public override string ToString()
{
return string.Format("({0}; {1})", X, Y);
}
}
你可以写:
var model = new HClusterModel();
var desc = Descriptor.Create<Point>();
var linker = new CentroidLinker(new EuclidianDistance());
var data = new List<Point>() { new Point(0.0, 1.0),
new Point(0.0, 2.0),
new Point (10.0, 0.0) };
var result = model.Generate(desc, data, linker);
foreach (var cluster in result.Children)
{
Console.WriteLine("Cluster:");
Console.WriteLine(string.Join(", ", cluster.Members.OfType<Point>()));
}
导致:
答案 2 :(得分:0)
我不完全确定你是如何定义“范围内”的,所以我假设了一个简单的距离计算。
// Set up some points
List<Point> Points = new List<Point>();
Points.Add(new Point(0, 0));
Points.Add(new Point(0, 1));
Points.Add(new Point(0, 2));
// Distance
int maxDistance = 1;
// Replace as appropriate
Func<Point, Point, int, bool> myDistanceFunction = delegate(Point p1, Point p2, int range)
{
// Same coordinate.
if (p1 == p2)
return true;
int xDelta = p1.X - p2.X;
int yDelta = p1.Y - p2.Y;
double distance = Math.Sqrt(xDelta * xDelta + yDelta * yDelta);
return (distance <= range);
};
// Loop through all points and calculate distance to all other points.
var Results = Points.Select(firstPoint => new
{
TargetPoint = firstPoint,
PointsInRange = Points
.Where(secondPoint =>
(secondPoint != firstPoint) && // Will you allow same coordinates?
myDistanceFunction(secondPoint, firstPoint, maxDistance))
});
// Spit the results out.
foreach (var result in Results)
{
Console.WriteLine("Point {0} - Points within {1} unit(s):", result.TargetPoint, maxDistance);
foreach (var point in result.PointsInRange)
{
Console.WriteLine("\t{0}", point);
}
}
输出:
Point {X=0,Y=0} - Points within 1 unit(s):
{X=0,Y=1}
Point {X=0,Y=1} - Points within 1 unit(s):
{X=0,Y=0}
{X=0,Y=2}
Point {X=0,Y=2} - Points within 1 unit(s):
{X=0,Y=1}
还有改进的余地,例如计算两次点的距离并不是很聪明,如果你允许重复坐标,我就不会这样,但是那里可能会有一些用处。
你也可以把距离函数写成一个lamba表达式,虽然我不确定它是否更清晰。
Func<Point, Point, int, bool> myDistanceFunction =
(
(p1, p2, range) => Math.Sqrt(
((p1.X - p2.X) * (p1.X - p2.X)) +
((p1.Y - p2.Y) * (p1.Y - p2.Y))
) <= range
);
答案 3 :(得分:0)
对不起,我做了一个不太清楚的帖子,基本上我使用c#而我为特定目的排除了聚类,让我说我有一些点和他们的ID,我需要“聚集他们”,保留有关id的信息,然后简单地在X轴上做一个中点,我只对通过该位置属性进行分组感兴趣。
最后,积分最多为10,保持有关ids的信息非常重要,知道谁在哪里,所以我想收集足够接近的点数,然后使用坐标列表列出结果,它是非常原始的,因为我匆忙,但完全开放进一步实施,只是我不能使用linq :)
所以我使用了这样的东西:
// class to hold information
public class userObject{
public string id;
public Vector3D position=Vector3D.Zero;
public userObject(string Id, Vector3D Position){
id=Id;
position=Position;
}
}
// list of grouped ids (nanocluster :)
public Dictionary<int, List<userObject>> slots ;
private void forceCheck(){
// create list of object from incoming coordinates (ids and point vector3d)
List<userObject> users=new List<userObject>();
for(int a=0;a<FId_In.SliceCount;a++){
userObject uo=new userObject(FId_In[a],FPositions_In[a]);
users.Add(uo);
}
// Clean result, this is different in another version im working on
slots =new Dictionary<int,List<userObject>>();
// check for close points ( a couple of lines should be changed to achieve a real clustring, but this way i can control all points will not create an horizontal cluster, told u raw mode on
for(int k=0;k<users.Count;k++){
List<userObject> matches=new List<userObject>();
// Check if ids is already registered in one slot
int isInSlot=checkIdInSlots(users[k].id);
if(isInSlot==-1){
matches.Add(users[k]);
for(int j=k+1;j<users.Count;j++){
// call a function to check x distance, but can use full vector3d when needed
if(checkClose(users[k].position,users[j].position,FXThreshold_In[0])){
matches.Add(users[j]);
}
}
// finally add entry with grouped ids....sure all this is a line of linq :D
addNewSlot(matches);
}
}
}
很高兴能够更好地理解linq如何用于获得相同的结果,确保更加健壮,谢谢大家:)