如何在DateTime值数组中查找平均日期/时间

时间:2013-05-22 04:21:18

标签: c# datetime average

如果我有一个DateTime值数组:

List<DateTime> arrayDateTimes;

在它们中找到平均日期时间的方法是什么?

例如,如果我有:

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

平均值应为:

2003-May-21 18:00:00

7 个答案:

答案 0 :(得分:13)

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

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

答案 1 :(得分:6)

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

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

事实上上面确实存在更大的列表和更大的间隙。我认为你可以使用秒来获得更好的范围。 (再次,排序第一)此外,这可能不是最高性能的方法,但仍然完成10M日期对我来说相对较快。不确定是否更容易阅读,YYMV。

var first = dates.First();
var average = first.AddSeconds(dates.Average(d => (d - first).TotalSeconds));

答案 2 :(得分:1)

代码:

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

答案 3 :(得分: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());
    }
}

答案 4 :(得分:0)

来源:取自Here并稍作修改。

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

答案 5 :(得分: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);
        }
        
    }

答案 6 :(得分:-1)

answer by neouser99是正确的。通过进行增量平均可以防止溢出。

但是,此answer by 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

这里有一些info on the distributive property

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

我会对他的答复发表评论,但我的声誉不够。