22

如果我有一组 DateTime 值:

List<DateTime> arrayDateTimes;

在其中找到平均 DateTime 的方法是什么?

例如,如果我有:

2003-May-21 15:00:00
2003-May-21 19:00:00
2003-May-21 20:00:00

平均值应该是:

2003-May-21 18:00:00
4

7 回答 7

16

如果您有大列表,您可以使用以下方法

var count = dates.Count;
double temp = 0D;
for (int i = 0; i < count; i++)
{
    temp += dates[i].Ticks / (double)count;
}
var average = new DateTime((long)temp);
于 2013-05-22T04:24:57.673 回答
9

这不应该溢出,它确实假设日期时间是有序的:

var first = dates.First().Ticks;
var average = new DateTime(first + (long) dates.Average(d => d.Ticks - first));

实际上,上面确实溢出了更大的列表和更大的间隙。我认为您可以使用秒来获得更好的范围。(再次,先排序)另外,这可能不是最高效的方法,但对我来说仍然相对较快地完成了 1000 万个日期。不知道它是否更容易阅读,YYMV。

var first = dates.First();
var average = first.AddSeconds(dates.Average(d => (d - first).TotalSeconds));
于 2013-05-22T04:35:12.317 回答
2

编码:

var count = dates.Count;
double temp = 0D;
for (int i = 0; i < count; i++)
{
    temp += dates[i].Ticks / (double)count;
}
var average = new DateTime((long)temp);

是错的。平均值=(x1 + x2 + ... xN) / N 不是 (x1/N + x2/N + ... xN/N)

尝试:

var avg=new DateTime((long)dates.Select(d => d.Ticks).Average());
于 2015-10-16T11:43:54.287 回答
1

来源:取自这里并稍作修改。

List<DateTime> dates = new List<DateTime>();
//Add dates
for (int i = 1; i <= 28; i++) //days
    for (int j = 1; j <= 12; j++) //month
        for (int k = 1900; k <= 2013; k++) //year
            dates.Add(new DateTime(k, j, i, 1, 2, 3)); //over 38000 dates

然后你可以这样做:

var averageDateTime = DateTime
                            .MinValue
                            .AddSeconds
                            ((dates
                                 .Sum(r => (r - DateTime.MinValue).TotalSeconds))
                                     / dates.Count);
Console.WriteLine(averageDateTime.ToString("yyyy-MMM-dd HH:mm:ss"));

输出:1956-Dec-29 06:09:25

最初文章中的代码是这样的:

double totalSec = 0;
for (int i = 0; i < dates.Count; i++)
{
    TimeSpan ts = dates[i].Subtract(DateTime.MinValue);
    totalSec += ts.TotalSeconds;
}
double averageSec = totalSec / dates.Count;
DateTime averageDateTime = DateTime.MinValue.AddSeconds(averageSec);
于 2013-05-22T05:04:21.370 回答
0
class Program
{
    static void Main(string[] args)
    {
        List<DateTime> dates = new List<DateTime>(){
        new DateTime(2003, 5, 21, 16, 0, 0), new DateTime(2003, 5, 21, 17, 0, 0),
        new DateTime(2003, 5, 21, 18, 0, 0), new DateTime(2003, 5, 21, 19, 0, 0),
        new DateTime(2003, 5, 21, 20, 0, 0), new DateTime(2003, 5, 21, 16, 0, 0),
        new DateTime(2003, 5, 21, 17, 0, 0), new DateTime(2003, 5, 21, 18, 0, 0),
        new DateTime(2003, 5, 21, 19, 0, 0), new DateTime(2003, 5, 21, 20, 0, 0),
        new DateTime(2003, 5, 21, 16, 0, 0), new DateTime(2003, 5, 21, 17, 0, 0),
        new DateTime(2003, 5, 21, 18, 0, 0), new DateTime(2003, 5, 21, 19, 0, 0),
        new DateTime(2003, 5, 21, 20, 0, 0), new DateTime(2003, 5, 21, 16, 0, 0),
        new DateTime(2003, 5, 21, 18, 0, 0), new DateTime(2003, 5, 21, 19, 0, 0),
        new DateTime(2003, 5, 21, 20, 0, 0), new DateTime(2003, 5, 21, 16, 0, 0),
        new DateTime(2003, 5, 21, 18, 0, 0), new DateTime(2003, 5, 21, 19, 0, 0),
        new DateTime(2003, 5, 21, 20, 0, 0), new DateTime(2003, 5, 21, 16, 0, 0),
        new DateTime(2003, 5, 21, 18, 0, 0), new DateTime(2003, 5, 21, 19, 0, 0),
        new DateTime(2003, 5, 21, 20, 0, 0), new DateTime(2003, 5, 21, 16, 0, 0),
        new DateTime(2003, 5, 21, 18, 0, 0), new DateTime(2003, 5, 21, 19, 0, 0),
        new DateTime(2003, 5, 21, 20, 0, 0), new DateTime(2003, 5, 21, 16, 0, 0),
        new DateTime(2003, 5, 21, 18, 0, 0), new DateTime(2003, 5, 21, 19, 0, 0),
        new DateTime(2003, 5, 21, 20, 0, 0), new DateTime(2003, 5, 21, 16, 0, 0),
        new DateTime(2003, 5, 21, 18, 0, 0), new DateTime(2003, 5, 21, 19, 0, 0),
        new DateTime(2003, 5, 21, 20, 0, 0), new DateTime(2003, 5, 21, 16, 0, 0),
    };

        var averageDate = dates.Average();

        Console.WriteLine(averageDate);

        Console.ReadKey();
    }

}

public static class Extensions
{
    public static long Average(this IEnumerable<long> longs)
    {
        long count = longs.Count();

        long mean = 0;

        foreach (var val in longs)
        {
            mean += val / count;
        }

        return mean;
    }

    public static DateTime Average(this IEnumerable<DateTime> dates)
    {
        return new DateTime(dates.Select(x => x.Ticks).Average());
    }
}
于 2013-05-22T04:30:02.523 回答
0

使用双秒而不是长滴答声将避免任何实际输入溢出 - 此处的扩展方法。

    public static DateTime Average(this IEnumerable<DateTime> elements)
    {
        if (elements == null)
        {
            throw new ArgumentNullException(nameof(elements));
        }
        var enumerated = elements.ToArray(); //so we don't iterate a potentially one-use stream multiple times.
        if (!enumerated.Any())
        {
            throw new ArgumentException("Average of 0 elements is undefined", nameof(elements));
        }

        var epoch = enumerated.Min();
        var secondsSinceEpoch = enumerated.Select(d => (d - epoch).TotalSeconds).ToArray();
        var n = secondsSinceEpoch.LongLength;
        double totalSecondsSinceEpoch = secondsSinceEpoch.Sum();
        return epoch.AddSeconds(totalSecondsSinceEpoch / n);
    }

    [TestMethod]
    public void HugeDateAverage_DoesntThrow()
    {
        var epoch = new DateTime(1900,1,1);
        try
        {
            var dates = Enumerable.Range(1, 1_000_000_000)
             .Select(i => epoch.AddSeconds(i));
            var result = dates.Average();
        }
        catch (Exception ex)
        {
            Assert.Fail();
        }
    }

如果你真的想退化,你可以检测到溢出并递归一半的元素,小心奇数 N 的情况。这是未经测试的,但这是一个想法:

    //NOT FOR ACTUAL USE - JUST FOR FUN
    public static DateTime AverageHuge(this IEnumerable<DateTime> elements)
    {
        if (elements == null)
        {
            throw new ArgumentNullException(nameof(elements));
        }
        var enumerated = elements.ToArray(); //so we don't iterate a potentially one-use stream multiple times.
        if (!enumerated.Any())
        {
            throw new ArgumentException("Average of 0 elements is undefined", nameof(elements));
        }

        var epoch = enumerated.Min();
        var secondsSinceEpoch = enumerated.Select(d => (d - epoch).TotalSeconds).ToArray();
        var n = secondsSinceEpoch.LongLength;
        if (n > int.MaxValue)
        {
            //we could actually support more by coding Take+Skip with long arguments.
            throw new NotSupportedException($"only int.MaxValue elements supported");
        }

        try
        {
            double totalSecondsSinceEpoch = secondsSinceEpoch.Sum(); //if this throws, we'll have to break the problem up
            //otherwise we're done.
            return epoch.AddSeconds(totalSecondsSinceEpoch / n);
        }
        catch (OverflowException) { } //fall out of this catch first so we don't throw from a catch block

        //Overengineering to support large lists whose totals would be too big for a double.
        //recursively get the average of each half of values.
        int pivot = (int)n / 2;
        var avgOfAvgs = (new []
        {
            enumerated.Take(pivot).AverageHuge(),
            enumerated.Skip(pivot).Take(pivot).AverageHuge()
        }).AverageHuge();
        if (pivot * 2 == n)
        {   // we had an even number of elements so we're done.
            return avgOfAvgs;
        }
        else
        {   //we had an odd number of elements and omitted the last one.
            //it affects the average by 1/Nth its difference from the average (could be negative)
            var adjust = ((enumerated.Last() - avgOfAvgs).TotalSeconds) / n;
            return avgOfAvgs.AddSeconds(adjust);
        }
        
    }
于 2020-08-16T07:00:08.253 回答
-1

neouser99的回答是正确的。它通过执行增量平均来防止溢出。

但是,David Jiménez 的这个答案是错误的,因为它没有处理溢出和他对公式的误解。

平均值=(x1 + x2 + ... xN) / N 不是 (x1/N + x2/N + ... xN/N)

这些是相同的公式。这是使用分配属性的简单数学:

2(x + y) = 2x + 2y

平均公式与将总和乘以 1/N 相同。或者将每个单独的 X 乘以 1/N 并将它们加在一起。

1/n (x1 + x2 + ... xn)

通过分配属性变为:

x1/n + x2/n + ... xn/n

这是有关分配属性的一些信息

他的回答也很糟糕,因为它不能像接受的答案那样防止溢出。

我会评论他的回复,但我没有足够的声誉。

于 2018-10-26T23:29:22.573 回答