我有一個無序的時間戳序列。我需要能夠計算每個后續時間戳之間的min、max和平均差異。例如給定:
DateTimeOffset now = new DateTimeOffset(new DateTime(2022, 1, 1, 0, 0, 0, 0));
DateTimeOffset[] timestamps = new[] {
now,
now.AddSeconds(5),
now.AddSeconds(10),
now.AddSeconds(15),
now.AddSeconds(30),
now.AddSeconds(31)
};
IEnumerable<DateTimeOffset> timestampsSorted = timestamps.OrderByDescending(x => x);
應該產生:
2022-01-01 00:00:31->2022-01-01 00:00:30 | 00:00:01
2022-01-01 00:00:30->2022-01-01 00:00:15 | 00:00:15
2022-01-01 00:00:15->2022-01-01 00:00:10 | 00:00:05
2022-01-01 00:00:10->2022-01-01 00:00:05 | 00:00:05
2022-01-01 00:00:05->2022-01-01 00:00:00 | 00:00:05
Min 00:00:01
Max 00:00:15
Avg 00:00:06.2000000
我想出的程式解決方案如下,如果我可以使用 LINQ 簡化它,那就太好了。
TimeSpan min = TimeSpan.MaxValue;
TimeSpan max = TimeSpan.MinValue;
List<TimeSpan> deltas = new();
for (int i = timestampsSorted.Length - 1; i > 0; i--)
{
DateTimeOffset later = timestamps[i];
DateTimeOffset prev = timestamps[i - 1];
TimeSpan delta = later - prev;
if (delta > max) { max = delta; }
if (delta < min) { min = delta; }
deltas.Add(delta);
Console.WriteLine($"{later:yyyy-MM-dd HH:mm:ss}->{prev:yyyy-MM-dd HH:mm:ss} | {delta}");
}
var result = new {
Min = min,
Max = max,
Avg = TimeSpan.FromMilliseconds(deltas.Average(d => d.TotalMilliseconds))
};
uj5u.com熱心網友回復:
首先,計算與下一項的日期差并將其存盤在diff串列中:
var diff = timestampsSorted.Select(x => x- ((timestampsSorted.IndexOf(x) 1)< timestampsSorted.Count?
timestampsSorted[timestampsSorted.IndexOf(x) 1]: now) ).ToList();
diff.RemoveAt(diff.Count-1);
00:00:01
00:00:15
00:00:05
00:00:05
00:00:05
然后mean,max和average很容易計算:
Console.WriteLine("Min {0}",diff.Min());
Console.WriteLine("Max {0}", diff.Max());
var averageDates = (long)diff.Select(d => d.Ticks).Average();
Console.WriteLine("Avg {0}", new DateTime(averageDates).ToString("HH:mm:ss.fff"));
最小
00:00:01 最大 00:00:15
平均 00:00:06.200
uj5u.com熱心網友回復:
使用LINQ's 的內置Min,Max和Average函式。
var timestampsSorted = timestamps.OrderByDescending(o => o).ToArray();
var data = timestampsSorted
.Skip(1)
.Select((o, i) => timestampsSorted[i] - o)
.ToArray();
var min = data.Min();
var max = data.Max();
var avg = TimeSpan.FromSeconds(data.Average(o => o.TotalSeconds));
請注意,對這些和Min函式的單獨呼叫會導致對陣列中的專案進行 3 次迭代。MaxAveragedata
uj5u.com熱心網友回復:
這遠非最佳,因為該集合被列舉了多次,但是因為您問...
// Expensive, multiple enumeration
var diffs = timestampsSorted.Skip(1).Zip(timestampsSorted, (first, second) => second.Subtract(first));
Console.WriteLine($"Min = {timestampsSorted.Min():ss.fff}, Max = {timestampsSorted.Max():ss.fff}, Ave = {diffs.Select(t => t.TotalMilliseconds).Average() / 1000:0.000}");
參考:Zip()和Skip()
uj5u.com熱心網友回復:
您不需要將所有delta值存盤在List<TimeSpan>要呼叫的 a 中Average();只保留一個運行總和然后將其除以比較的對數 ( timestamps.Length - 1) 會更有效。所以這...
// ...
List<TimeSpan> deltas = new();
for (int i = timestamps.Length - 1; i > 0; i--)
{
// ...
deltas.Add(delta);
// ...
}
var result = new {
// ...
Avg = TimeSpan.FromMilliseconds(deltas.Average(d => d.TotalMilliseconds))
};
……會改成……
// ...
TimeSpan sum = TimeSpan.Zero;
for (int i = timestamps.Length - 1; i > 0; i--)
{
// ...
sum = delta;
// ...
}
var result = new {
// ...
//TODO: Avoid division for sequences with less than 2 elements, if expected
Avg = TimeSpan.FromMilliseconds(sum.TotalMilliseconds / (timestamps.Length - 1))
};
Aggregate()是您用來在序列程序中累積一個或多個值的方法。這是一種用于計算與回圈Aggregate()相同的值的方法...for
static (TimeSpan? Minimum, TimeSpan? Maximum, TimeSpan? Average, int Count) GetDeltaStatistics(IEnumerable<DateTimeOffset> timestamps)
{
var seed = (
Previous: (DateTimeOffset?) null,
Minimum: (TimeSpan?) null,
Maximum: (TimeSpan?) null,
Sum: TimeSpan.Zero,
Count: 0
);
return timestamps.Aggregate(
seed,
(accumulator, current) => {
if (accumulator.Previous != null)
{
TimeSpan delta = current - accumulator.Previous.Value;
if ( accumulator.Count > 1)
{
// This is not the first comparison; Minimum and Maximum are non-null
if (delta < accumulator.Minimum.Value)
accumulator.Minimum = delta;
if (delta > accumulator.Maximum.Value)
accumulator.Maximum = delta;
}
else
{
// No prior comparisons have been performed
// Minimum and Maximum must be null so unconditionally overwrite them
accumulator.Minimum = accumulator.Maximum = delta;
}
accumulator.Sum = delta;
Console.WriteLine($"{current:yyyy-MM-dd HH:mm:ss}->{accumulator.Previous:yyyy-MM-dd HH:mm:ss} | {delta}");
}
accumulator.Previous = current;
return accumulator;
},
accumulator => (
accumulator.Minimum,
accumulator.Maximum,
Average: accumulator.Count > 0
? new TimeSpan(accumulator.Sum.Ticks / accumulator.Count)
: (TimeSpan?) null,
accumulator.Count
)
);
}
此多載的第二個引數Aggregate()是 a Func<>,它傳遞序列 () 中的當前元素以及從( current) 的先前呼叫回傳的狀態。第一個引數提供 的初始值。第三個引數是 a ,將這個狀態的最終值轉換為 的回傳值。狀態和回傳值都是值元組。Func<>accumulatoraccumulatorFunc<>Aggregate()
請注意,GetDeltaStatistics()只需要一個IEnumerable<DateTimeOffset>而不是一個IList<DateTimeOffset>或DateTimeOffset[];但是,由于沒有對相鄰元素的隨機訪問,因此 的值將current通過 . 傳遞到下一次呼叫accumulator.Previous。我還讓呼叫者負責提供排序的輸入,但您可以在方法內輕松地執行此操作。
GetDeltaStatistics()與...通話
static void Main()
{
DateTimeOffset now = new DateTimeOffset(new DateTime(2022, 1, 1, 0, 0, 0, 0));
DateTimeOffset[] timestamps = new[] {
now,
now.AddSeconds(5),
now.AddSeconds(10),
now.AddSeconds(15),
now.AddSeconds(30),
now.AddSeconds(31)
};
IEnumerable<IEnumerable<DateTimeOffset>> timestampSequences = new IEnumerable<DateTimeOffset>[] {
timestamps,
timestamps.Take(2),
timestamps.Take(1),
timestamps.Take(0)
};
foreach (IEnumerable<DateTimeOffset> sequence in timestampSequences)
{
var (minimum, maximum, average, count) = GetDeltaStatistics(sequence.OrderBy(offset => offset));
Console.WriteLine($"Minimum: {GetDisplayText(minimum)}");
Console.WriteLine($"Maximum: {GetDisplayText(maximum)}");
Console.WriteLine($"Average: {GetDisplayText(average)}");
Console.WriteLine($" Count: {count}");
Console.WriteLine();
}
}
static string GetDisplayText(TimeSpan? delta) => delta == null ? "(null)" : delta.Value.ToString();
...產生這個輸出...
2022-01-01 00:00:05->2022-01-01 00:00:00 | 00:00:05 2022-01-01 00:00:10->2022-01-01 00:00:05 | 00:00:05 2022-01-01 00:00:15->2022-01-01 00:00:10 | 00:00:05 2022-01-01 00:00:30->2022-01-01 00:00:15 | 00:00:15 2022-01-01 00:00:31->2022-01-01 00:00:30 | 00:00:01 最小值:00:00:01 最大值:00:00:15 平均:00:00:06.2000000 計數:5 2022-01-01 00:00:05->2022-01-01 00:00:00 | 00:00:05 最小值:00:00:05 最大值:00:00:05 平均:00:00:05 計數:1 最小值:(空) 最大值:(空) 平均:(空) 計數:0 最小值:(空) 最大值:(空) 平均:(空) 計數:0
雖然原始代碼會導致拋出例外,但對于少于兩個元素的序列,結果具有 a Countof0而其他欄位為null.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/522813.html
標籤:C#。网林克
下一篇:C#LINQ-回圈串列
