我在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)
答案 0 :(得分: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);
答案 1 :(得分: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]);
}
}
答案 2 :(得分:0)
如果所有元素属于同一类型且您感兴趣的属性被命名为Z
,则可以执行以下操作:
var max = list_of_points.Max(p => p.Z);
在您的情况下,由于您似乎使用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()
完成相同的代码。
答案 3 :(得分:0)
如果这实际上是IEnumerable
个IEnumerables
的双倍,并且总是具有相同的X,Y,Z顺序,那么你可以这样做:
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();
答案 4 :(得分:-1)
您是否看过SelectMany linq运算符?我认为这可能对您的问题有所帮助。