【问题标题】:Filter points XY by distance with LINQ使用 LINQ 按距离过滤点 XY
【发布时间】:2014-01-03 15:52:52
【问题描述】:

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

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

【问题讨论】:

  • Linq-2 对象?实体框架?其他一些 Linq 提供商?
  • 如果您有A=(0,0)B=(0,1)C=(0,2) 的分数,您希望会发生什么? BAC 的距离为1,但AC 的距离为2。他们应该在同一组吗?
  • 如果我有 A=(0,0)、B=(0,1) 和 C=(0,2) 以及给定的 delta 1,我希望有不同的组所有具有该距离的点
  • 距离什么?起源?

标签: linq filter grouping point


【解决方案1】:

我不太明白你的问题,所以我不太确定你想如何进行分组,但这至少可以让你朝着正确的方向前进。

(用 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)}

【讨论】:

    【解决方案2】:

    你想要做的是命名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>()));
    }
    

    导致:

    【讨论】:

      【解决方案3】:

      我尝试过,虽然这可能不是一种非常有效的做事方式;康拉德答案中的链接似乎是一个探索的好地方。

      我不完全确定你是如何定义“范围内”的,所以我假设了一个简单的距离计算。

      // 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}
      

      还有改进的余地,例如计算两次点对的距离并不明智,如果您允许重复坐标,我不会这样做,但那里可能会有用处。

      你也可以将距离函数写成兰巴表达式,虽然我不确定它是否更清晰。

      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
      );
      

      【讨论】:

        【解决方案4】:

        对不起,我发的帖子不太清楚,基本上我使用的是 c#,我排除了特定目的的聚类,假设我有一些点和它们的 id,我需要“对它们进行聚类”,保留关于 id 的信息,然后简单地在 X 轴上做了一个中间点,因为我只对按该位置属性进行分组感兴趣。

        最后,点数最多为 10,保留有关 id 的信息对于知道谁在哪里非常重要,所以我想收集足够接近的点的 id,然后使用该坐标列表来得出结果,做得很原始,因为我很匆忙,但完全可以进一步实施,只是我无法使用 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 来获得相同的结果,肯定会更强大,谢谢大家 :)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-09-23
          • 1970-01-01
          • 1970-01-01
          • 2011-03-12
          • 2018-06-17
          • 1970-01-01
          • 2019-07-01
          • 2012-02-02
          相关资源
          最近更新 更多