【问题标题】:C# LINQ TimeSpan Average DaysC# LINQ TimeSpan 平均天数
【发布时间】:2011-02-12 01:09:41
【问题描述】:

我正在学习 LINQ,并且想要一个与以下代码等效的 LINQ 查询。

IEnumerable 列表包含一个排序的日期列表:从最旧到最新。

下面的代码通过从数组的第一个元素日期中减去数组的第零个元素日期,从第 2 个元素中减去第一个元素日期,从第 3 个元素中减去第 2 个元素,得出 TimeSpans。 TimeSpan.Days 然后在代码中的其他地方进行平均。

我相信 LINQ 查询不需要构造数组。 IEnumerable<DateTime> 结构可以用作数据源。

 IEnumerable<DateTime> list; // contains a sorted list of oldest to newest dates

// build DateTime array 

 DateTime[] datesArray = null;
 TimeSpan ts;
 List<int> daysList = new List<int>();

 datesArray = (from dt in list
               select dt).ToArray();

// loop through the array and derive the TimeSpan by subtracting the previous date,
// contained in the previous array element, from the date in the current array element.
// start the loop at element 1.

 for (int i = 1; i < list.Count(); i++)
 {
     ts = datesArray[i].Subtract(datesArray[i - 1]); // ts is a TimeSpan type
     daysList.Add(ts.Days);
     // add the TimeSpan.Days to a List<int> for averaging elsewhere.
 }

谢谢,

斯科特

【问题讨论】:

  • (from dt in list select dt).ToArray() 最好写成list.ToArray()

标签: c# linq timespan


【解决方案1】:

我想你想要:

double averageDays = list
                     .Skip(1)
                     .Zip(list, (next, prev) => (double)next.Subtract(prev).Days)
                     .Average();

请注意,这是一个有损平均值。您确定不想改用TotalDays

编辑:

这种工作方式是用序列的“单延迟”版本覆盖序列,这样可以轻松计算连续的增量。然后只需对增量进行平均即可得出结果。

作为比较,您现有代码的“忠实”翻译如下所示:

double averageDays = Enumerable
                     .Range(1, list.Count - 1)
                     .Average(i => (double)list[i].Subtract(list[i - 1]).Days);

【讨论】:

  • Zip 这里好像有点没必要,应该可以用Aggregate
  • ...但是理解代码的意图要容易得多。尽管Aggregate 很方便,但它会生成不明显的丑陋代码。
  • Linq Zip 是一个框架 4.0。我需要一个 Framework 3.5 或更低版本的解决方案。我还需要一个单独的日期增量列表来进行统计分析,而不是平均值。
  • @scottL:第二个解决方案怎么样?将 Average 替换为 Select 以获取各个增量。当然,你可以自己实现Zip;这很容易。 blogs.msdn.com/b/ericlippert/archive/2009/05/07/zip-me-up.aspx
  • 知道了。我只是按照您的建议进行操作,并将平均值替换为 Select。这就是我寻求的解决方案。谢谢。
【解决方案2】:

如果您只需要平均值,则无需检查列表中的所有元素。您只需要第一个和最后一个。

var period = list.Last() - list.First();
var averageDays = (double)period.TotalDays / (list.Count() - 1);

【讨论】:

  • 请注意,OP 正在计算 DeltaD.Days 的平均值。那里固有的四舍五入意味着它与这种方法不同。
  • 谢谢谢尔盖。鉴于我提供的信息,您的方法在数学上是正确的并且更容易。但是,我需要将各个天的增量用于其他统计分析:模式等,这是我在原始帖子中没有提供的信息。
【解决方案3】:

这被安排成更 Lispy 的缩进样式,以强调功能样式并避免跑到页面右侧的滚动条区域。

list.Skip(1)
    .Aggregate(new { Previous = list.FirstOrDefault(), 
                     Sum = 0.0,
                     Count = 0 },
               (a, n) => new { Previous = n,
                               Sum = a.Sum + (n - a.Previous).Days,
                               Count = a.Count + 1 },
               a => a.Sum / a.Count)

【讨论】:

  • 如果打算跳过第一项,你不应该在 Aggregate 的种子中调用 .Skip(1) 吗?
  • 我怀疑你想要TotalDays,看看你如何将Sum定义为double
  • @vcsjones:第一项已预加载到种子中,这就是它被跳过的原因。
  • @Gabe: Sumdouble 以避免溢出问题,因为平均值将作为一个返回。我单独留下了Days,因为原始代码使用了它。
  • 感谢大家回答问题。我需要单独的天数增量来进行平均计算。我在上执行统计模式和其他计算
猜你喜欢
  • 2012-02-09
  • 1970-01-01
  • 2017-03-07
  • 1970-01-01
  • 2012-06-08
  • 2016-04-01
  • 1970-01-01
  • 2016-09-18
  • 1970-01-01
相关资源
最近更新 更多