代码之家  ›  专栏  ›  技术社区  ›  Judah Gabriel Himango

如何将多个时间跨度减少为较少的平均时间跨度?

  •  4
  • Judah Gabriel Himango  · 技术社区  · 15 年前

    我有一个包含500个元素的C队列<TimeSpan>。

    我需要将这些减少到50个元素,通过采取10个时间跨度组并选择它们的平均值。

    有干净的方法吗?我认为Linq会有帮助,但我找不到一个干净的方法。有什么想法吗?

    7 回复  |  直到 15 年前
        1
  •  3
  •   Jonathan Allen    15 年前

    我将使用chunk函数和循环。

    foreach(var set in source.ToList().Chunk(10)){
        target.Enqueue(TimeSpan.FromMilliseconds(
                                set.Average(t => t.TotalMilliseconds)));
    }
    

    区块是我的标准助手库的一部分。 http://clrextensions.codeplex.com/

    Source for Chunk

        2
  •  2
  •   Bob King    15 年前

    看看 .Skip() and .Take() 将队列划分为集合的扩展方法。然后,可以使用.average(t=>t.ticks)获取表示平均值的新时间跨度。把这50条平均线中的每一条塞进一个新的队列,你就可以走了。

    Queue<TimeSpan> allTimeSpans = GetQueueOfTimeSpans();
    Queue<TimeSpan> averages = New Queue<TimeSpan>(50);
    int partitionSize = 10;
    for (int i = 0; i <50; i++) {
        var avg = allTimeSpans.Skip(i * partitionSize).Take(partitionSize).Average(t => t.Ticks)
        averages.Enqueue(new TimeSpan(avg));
    }
    

    我是一个vb.net用户,所以在这个例子中可能有一些语法不是100%写的。告诉我,我会修好的!

        3
  •  1
  •   Michael Meadows    15 年前

    在这种情况下,在方法调用中,可能没有什么比良好的旧过程执行更好的了。这不是幻想,但很容易,而且可以由小级别的开发人员维护。

    public static Queue<TimeSpan> CompressTimeSpan(Queue<TimeSpan> original, int interval)
    {
        Queue<TimeSpan> newQueue = new Queue<TimeSpan>();
        if (original.Count == 0) return newQueue;
    
        int current = 0;
        TimeSpan runningTotal = TimeSpan.Zero;
        TimeSpan currentTimeSpan = original.Dequeue();
    
        while (original.Count > 0 && current < interval)
        {
            runningTotal += currentTimeSpan;
            if (++current >= interval)
            {
                newQueue.Enqueue(TimeSpan.FromTicks(runningTotal.Ticks / interval));
                runningTotal = TimeSpan.Zero;
                current = 0;
            }
            currentTimeSpan = original.Dequeue();
        }
        if (current > 0)
            newQueue.Enqueue(TimeSpan.FromTicks(runningTotal.Ticks / current));
    
        return newQueue;
    }
    
        4
  •  1
  •   Daniel LeCheminant    15 年前

    你可以只用

    static public TimeSpan[] Reduce(TimeSpan[] spans, int blockLength)
    {
        TimeSpan[] avgSpan = new TimeSpan[original.Count / blockLength];
    
        int currentIndex = 0;
    
        for (int outputIndex = 0;
             outputIndex < avgSpan.Length; 
             outputIndex++)
        {
            long totalTicks = 0;
    
            for (int sampleIndex = 0; sampleIndex < blockLength; sampleIndex++)
            {
                totalTicks += spans[currentIndex].Ticks;
                currentIndex++;
            }
    
            avgSpan[outputIndex] =
                TimeSpan.FromTicks(totalTicks / blockLength);
        }
    
        return avgSpan;
    }
    

    它有点冗长(不使用LINQ),但很容易看到它在做什么…(您可以很容易地在数组之间建立队列)

        5
  •  1
  •   mqp    15 年前

    我会用一个循环,但只是为了好玩:

    IEnumerable<TimeSpan> AverageClumps(Queue<TimeSpan> lots, int clumpSize)
    {
        while (lots.Any())
        {
            var portion = Math.Min(clumpSize, lots.Count);
            yield return Enumerable.Range(1, portion).Aggregate(TimeSpan.Zero,
                (t, x) => t.Add(lots.Dequeue()),
                (t) => new TimeSpan(t.Ticks / portion));
            }
        }
    }
    

    这只检查每个元素一次,因此性能比其他Linq产品要好得多。不幸的是,它改变了队列,但它可能是一个特性,而不是一个bug?

    它确实有一个迭代器的好处,所以它一次给你一个平均值。

        6
  •  1
  •   MarkusQ    15 年前

    将它与整数(0..n)压缩在一起,并按序号DIV10分组?

    我不是Linq用户,但我相信它看起来像这样:

    for (n,item) from Enumerable.Range(0, queue.length).zip(queue) group by n/10
    

    采取(10)的解决方案可能更好。

        7
  •  1
  •   Richard    15 年前

    如何进行分组?

    假设非常简单(一次取10个),您可以从以下内容开始:

    List<TimeSpan> input = Enumerable.Range(0, 500)
                                     .Select(i => new TimeSpan(0, 0, i))
                                      .ToList();
    
    var res = input.Select((t, i) => new { time=t.Ticks, index=i })
                   .GroupBy(v => v.index / 10, v => v.time)
                   .Select(g => new TimeSpan((long)g.Average()));
    
    int n = 0;
    foreach (var t in res) {
        Console.WriteLine("{0,3}: {1}", ++n, t);
    }
    

    笔记:

    • 重载select得到索引,然后使用这个整数除法取10个组。可以用模将每10个元素取为一组,每10+1取为另一组,…
    • 分组的结果是一系列具有键属性的枚举。但这里需要这些单独的序列。
    • 没有可枚举的。的平均重载 IEnumerable<TimeSpan> 所以,使用记号(长的)。

    编辑:每组10个,以更好地适应问题。
    edit2:现在测试代码。