如何编写扩展函数来返回自定义类型的平均值?

发布于 2024-11-14 21:42:37 字数 611 浏览 4 评论 0原文

这可能非常简单,但我的尝试(在 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 包含 timexy & z 值是组成列表的元素的这些属性的平均值。我该怎么做?我正在努力解决的问题是(我认为)someMagicHere,但我可能一开始就使用了完全错误的方法。

This is probably very simple, but my attempts (guided by Intellisense and MSDN) have all been off the mark.

If I have a class which contains 3 double, how can I get the average of a list of these?

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

I want averagePoint to contain time, x, y & z values that are the average of these properties of the elements that make up the list. How do I do this? The bit I'm struggling with is (I think) someMagicHere, but I could be using completely the wrong approach to begin with.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

孤独陪着我 2024-11-21 21:42:37

问题并不完全清楚,但听起来您想要的是一个新点 P,其中 PX 是列表中所有点的 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);

现在您有了总和,因此计算平均值很简单。

The question is not entirely clear, but it sounds like what you want is a new point P where P.X is the average of all the X coordinates of the points in the list, and so on, yes?

The general way to solve a problem like this is to break it down:

First transform the list of points into four lists of integers.

var times = from p in points select p.Time;
var xs = from p in points select p.X;
... and so on ..

Or, if you prefer this notation:

var times = points.Select(p=>p.Time);

Now you can average those:

double averageTime = times.Average();
double averageX = xs.Average();
... and so on ...

and now you have your four values -- as doubles -- that you can use to construct the average point. Of course you'll have to convert the doubles to integers, using whatever rounding you prefer.

However, there is a special version of "Average" which combines the Select and the Average into one operation. You can just say

double averageTime = points.Average(p=>p.Time);

and do it in one step for both the projection and the average.

The down side of this approach, as some have noted, is that the sequence is enumerated four times. Which is probably not a big deal, since it is an in-memory list, but might be more of a big deal if it were an expensive database query.

Another approach would be to define the addition operator on your DataPoint class (if in general it makes sense to sum two points, which it might not). Once you have an addition operator, making the sum of all the points is straightforward.

Whether you define an addition operator or not, you can use Aggregate to compute the sum of all the Points, and then divide the four fields of the sum by the number of points.

DataPoint sum = points.Aggregate(
    new DataPoint(0, 0, 0, 0), 
    (agg, point)=> new DataPoint(agg.time + point.time, agg.x + point.x, ... ));

or, if you have the operator, simply:

DataPoint sum = points.Aggregate(
    new DataPoint(0, 0, 0, 0), 
    (agg, point)=> agg + point);

And now you have the sum, so computing the average is straightforward.

不再让梦枯萎 2024-11-21 21:42:37
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};
 }
}
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};
 }
}
帅的被狗咬 2024-11-21 21:42:37

好吧,看来您需要依次取每个投影的平均值

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

Well, it would seem that you need to take average of each projection in turn

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

I cast to int, because your types are int, although they probably should be double, or converted to your integer lattice more intelligently.

Another way is using running averages. It's slower than Lamperts, and assumes that the backing datatype for the DataPoint is double. But if the set of Points is HUGE, and points are ordered randomly it has a nice Monte-Carlo convergence. It also enumarates the List only once.:

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文