How to find an average date/time in the array of D

2020-04-02 05:51发布

问题:

If I have an array of DateTime values:

List<DateTime> arrayDateTimes;

What's the way to find the average DateTime among them?

For instance, if I have:

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

the average should be:

2003-May-21 18:00:00

回答1:

If you have large list you can use below method

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


回答2:

This shouldn't overflow, it does assume the datetimes are ordered though:

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

The above does in fact overflow with larger lists and larger gaps. I think you could use seconds for better range. (again, sorted first) Also, this might not be the most performant method, but still completed with 10M dates relatively quickly for me. Not sure if it's easier to read or not, YYMV.

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


回答3:

The code:

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

is Wrong. Average=(x1 + x2 + ... xN) / N not (x1/N + x2/N + ... xN/N)

Try:

var avg=new DateTime((long)dates.Select(d => d.Ticks).Average());


回答4:

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


回答5:

Source: Taken from Here and modified a bit.

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

Then you can do:

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

Output in: 1956-Dec-29 06:09:25

Originally the code from the article was like:

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


回答6:

The answer by neouser99 is correct. It prevents overflow by doing an incremental average.

However, this answer by David Jiménez is wrong because it doesn't handle overflow and his misunderstanding on the formula.

Average=(x1 + x2 + ... xN) / N not (x1/N + x2/N + ... xN/N)

These are the same formula. It's simple math using the distributive property:

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

The average formula is the same as multiplying your sum by 1/N. Or multiplying each individual X by 1/N and adding them together.

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

which by the distributive property becomes:

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

Here's some info on the distributive property

His answer is also bad because it doesn't prevent overflow like the accepted answer.

I would have commented on his reply, but I don't have enough reputation.