这可能非常简单,但我的尝试(由Intellisense和MSDN引导)都不合适。
如果我有一个包含3个double的类,我怎么能得到这些列表的平均值?
class DataPoint
{
public int time;
public int X;
public int Y;
public int Z;
// Constructor omitted
}
class Main
{
List<DataPoint> points = new List<DataPoint>();
// Populate list
DataPoint averagePoint = points.Average(someMagicHere);
}
我希望averagePoint
包含time
,x
,y
&amp; z
值是构成列表的元素的这些属性的平均值。我该怎么做呢?我正在努力的一点是(我认为)someMagicHere
,但我可能完全采用错误的方法开始。
答案 0 :(得分:6)
问题并不完全清楚,但听起来你想要的是一个新的点P,其中P.X是列表中各点的所有X坐标的平均值,依此类推,是吗?
解决这类问题的一般方法是将其分解:
首先将点列表转换为四个整数列表。
var times = from p in points select p.Time;
var xs = from p in points select p.X;
... and so on ..
或者,如果您更喜欢这种表示法:
var times = points.Select(p=>p.Time);
现在你可以平均那些:
double averageTime = times.Average();
double averageX = xs.Average();
... and so on ...
现在你有四个值 - 双倍 - 你可以用来构建平均点。当然,你必须使用你喜欢的任何四舍五入将双打转换为整数。
但是,有一个特殊版本的“平均值”,它将“选择”和“平均”组合成一个操作。你可以说
double averageTime = points.Average(p=>p.Time);
并为投影和平均值一步完成。
正如一些人所指出的,这种方法的缺点是序列被枚举四次。这可能不是什么大问题,因为它是一个内存列表,但如果它是一个昂贵的数据库查询可能会更重要。
另一种方法是在DataPoint类上定义加法运算符(如果总的来说将两个点相加是有意义的,它可能不是)。一旦你有了一个加法运算符,就可以直接得出所有点的总和。
无论是否定义加法运算符,都可以使用Aggregate计算所有点的总和,然后将总和的四个字段除以点数。
DataPoint sum = points.Aggregate(
new DataPoint(0, 0, 0, 0),
(agg, point)=> new DataPoint(agg.time + point.time, agg.x + point.x, ... ));
或者,如果您有运营商,只需:
DataPoint sum = points.Aggregate(
new DataPoint(0, 0, 0, 0),
(agg, point)=> agg + point);
现在你有了总和,所以计算平均值很简单。
答案 1 :(得分:4)
static class DataPointExtensions
{
public static DataPoint Average (this IEnumerable<DataPoint> points)
{
int sumX=0, sumY=0, sumZ=0, count=0;
foreach (var pt in points)
{
sumX += pt.X;
sumY += pt.Y;
sumZ += pt.Z;
count++;
}
// also calc average time?
if (count == 0)
return new DataPoint ();
return new DataPoint {X=sumX/count,Y=sumY/count,Z=sumZ/count};
}
}
答案 2 :(得分:0)
好吧,你似乎需要依次取每个投影的平均值
DataPoint averagePoint = new DataPoint{
X = (int)Points.Average(p => X),
Y = (int)Points.Average(p => P.Y),
Z = (int)Points.Average(p => p.Z),
time = (int)Points.Average(p => p.time)
};
我转换为int,因为你的类型是int
,虽然它们可能应该是double
,或者更智能地转换为整数格。
另一种方法是使用流动平均值。它比Lamperts慢,并假设DataPoint
的支持数据类型是double。但是如果点集是巨大的,并且点是随机排序的,那么它具有很好的蒙特卡罗收敛性。它也只会使List
只使用一次。
var averagePoint = Points.First();
foreach(var point in Points.Skip(1).Select((p,i) => new{ Point = p, Index = i})){
averagePoint.X = (point.Index * averagePoint.X + p.Point.X)/(point.Index + 1);
averagePoint.Y = (point.Index * averagePoint.Y + p.Point.Y)/(point.Index + 1);
averagePoint.Z = (point.Index * averagePoint.Z + p.Point.Z)/(point.Index + 1);
}