使用LINQ过滤点与XY的距离

时间:2014-01-03 15:52:53

标签: linq filter grouping point

我有一个XY列表,我希望按给定距离对它们进行分组,假设它们之间x距离处的所有点都应该分组在不同的列表中。

基本上如果我有A =(0,0),B =(0,1),C =(0,2),我想对所有maxDistance为1的点进行分组,以获得:[ [A,B],[C]];

4 个答案:

答案 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,这意味着将一组数据(在您的情况下为二维点)分组为一组具有一些特征(点之间的给定距离)的组。我强烈建议您阅读上面提供的链接,以便更好地理解它。您可能对两种类型的聚类感兴趣:

  • 层次聚类,根据距离连接创建群组,
  • centroids ,它创建“围绕”群组中心的群组

这完全取决于您拥有多少数据。对于小型集合,您可以尝试自己实现一些简单的算法。对于更大的数据,我更喜欢使用像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>()));
}

导致:

enter image description here

答案 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如何用于获得相同的结果,确保更加健壮,谢谢大家:)