2

我在 C# 中有一个列表列表,其中每个子列表都有三个双精度值,代表一个 3D 点:

{{x1, y1, z1},
 {x2, y2, z2},
 {x3, y3, z3}}

我想找到这个数据集的 3D 边界框,这意味着找到最小 X、最大 X、最小 Y、最大 Y 等。

使用 Python/Numpy,我会得到它,比如zmax = list_of_lists[:,2].max(),等等。

在 C# 中是否有一种优雅的方法可以做到这一点?我怀疑 Linq 是要走的路,但我还不明白它是如何工作的(如果某些答案包括 Linq,请解释它是如何工作的,请:o)

4

5 回答 5

2

像这样创建您的列表,如果您需要更多功能,可能会为 3D 点使用特定类而不是元组。

var points = new[] {
    Tuple.Create(x1, y1, z1),
    Tuple.Create(x2, y3, z2),
    Tuple.Create(x3, y3, z3)
};

然后,就像@dasblinkenlight 所写,您可以使用 linq 来选择Max()Min()

var maxX = points.Select(pt => pt.Item1).Max();

甚至更短:

var maxX = points.Max(pt => pt.Item1);

编辑:一个易于使用的简单类:

class Point3D {
    public double X { get; set; }
    public double Y { get; set; }
    public double Z { get; set; }

    public Point3D(double x, double y, double z) {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }
}

var points = new[] {
    new Point3D(x1, y1, z1),
    new Point3D(x2, y3, z2),
    new Point3D(x3, y3, z3)
};

var maxX = points.Max(pt => pt.X);
于 2013-04-03T14:04:50.873 回答
1

是的,您可以使用 LINQ 在 C# 中执行此操作,如下所示:

var orig = new List<List<double>>();
var maxX = orig.Select(pt => pt[0]).Max();
var maxY = orig.Select(pt => pt[1]).Max();
var maxZ = orig.Select(pt => pt[2]).Max();

这在 LINQ 中的工作方式是遍历点列表,对于每个点,选择请求的坐标,然后Max计算 。还有一个Min功能可以让您到达边界框的另一个角落。

这有点不太理想,因为列表被遍历了多次。一对嵌套循环可能会更有效地做同样的事情,同时保持可读性:

var min = new List<double>{double.MaxValue, double.MaxValue, double.MaxValue};
var max = new List<double>{double.MinValue, double.MinValue, double.MinValue};
foreach (var point in orig) {
    for (var i = 0 ; i != 3 ; i++) {
        min[i] = Math.Min(min[i], point[i]);
        max[i] = Math.Max(max[i], point[i]);
    }
}
于 2013-04-03T13:59:35.413 回答
0

如果所有元素都属于同一类型并且您感兴趣的属性被命名为 ,Z那么您可以这样做:

var max = list_of_points.Max(p => p.Z);

在您的情况下,由于您似乎正在使用 a double[][],您可以这样做:

var max = list_of_lists.Max(p => p[0]);

所以完整的边界框将由两个点定义,如下所示:

var topLeft = new double[] 
{
    list_of_lists.Min(p => p[0]),
    list_of_lists.Min(p => p[1]),
    list_of_lists.Min(p => p[2])
};
var bottomRight = new double[] 
{
    list_of_lists.Max(p => p[0]),
    list_of_lists.Max(p => p[1]),
    list_of_lists.Max(p => p[2])
};

但是,这需要您通过列表进行 6 次传递(每个点的每个坐标一次)。您可以这样做以提高性能:

var topLeft = list_of_lists.Aggregate((s, p) => return double[] { Math.Min(s[0], p[0]), Math.Min(s[1], p[1]), Math.Min(s[2], p[2]) });
var bottomRight = list_of_lists.Aggregate((s, p) => return double[] { Math.Max(s[0], p[0]), Math.Max(s[1], p[1]), Math.Max(s[2], p[2]) });

这将只通过列表 2 次。

注意:可以使用相同的代码来完成,List<List<double>>而不是在每个数组声明之后double[][]简单地添加。.ToList()

于 2013-04-03T13:59:47.180 回答
0

如果这真的是X、Y、Z 的顺序相同IEnumerableIEnumerables双打中的一个,那么你可以这样做:

var list = new []
            {
                new [] { 1, 2, 3 },
                new [] { 4, 5, 6 },
                new [] { 7, 8, 9 }
            };
var maxX = list.Select(s => s.Skip(0).Take(1)).Max();
var minY = list.Select(s => s.Skip(1).Take(1)).Min();
于 2013-04-03T14:09:38.487 回答
-1

您看过 SelectMany linq 运算符吗?我认为这可能对您的问题有所帮助。

于 2013-04-03T13:59:08.250 回答