Aporte:
public class MyObject { public double Value { get; set; } public DateTime Date { get; set; } }Método para generar objetos de prueba:
public static MyObject[] GetTestObjects() { var rnd = new Random(); var date = new DateTime(2021, 1, 1, 0, 0, 0); var result = new List<MyObject>(); for (int i = 0; i < 50000; i++) { //this is to simulate real data having gaps if (rnd.Next(100) < 25) { continue; } var myObject = new MyObject() { Value = rnd.NextDouble(), Date = date.AddMinutes(15 * i) }; result.Add(myObject); } return result.ToArray(); }Dado esto, necesito calcular el valor máximo de los 12 meses anteriores para cada myObject. Podría pensar en hacer esto InParallel, pero tal vez haya una solución optimizada.
Perdón por no estar claro, esto es lo que uso ahora mismo para obtener lo que quiero:
public MyObject[] BruteForceBackward(MyObject[] testData) { return testData.AsParallel().Select(point => { var max = testData.Where(x => x.Date <= point.Date && x.Date >= point.Date.AddYears(-1)).Max(x => x.Value); return new MyObject() { Date = point.Date, Value = point.Value / max }; }).OrderBy(r => r.Date).ToArray(); }Esto funciona pero es lento y consume recursos del procesador (imagínate, tienes 100k objetos), creo que debe haber algo mejor
Suponiendo que quiere decir que necesita el Value máximo para cada uno de los últimos 12 meses desde result , entonces puede usar LINQ:
var beginDateTime = DateTime.Now.AddMonths(-12); var ans = result.Where(r => r.Date >= beginDateTime).GroupBy(r => r.Date.Month).Select(mg => mg.MaxBy(r => r.Value)).ToList(); Ejecutando un tiempo, entiendo que poner AsParallel después del result cambia el tiempo de ejecución de alrededor de 16 ms (primera ejecución) a alrededor de 32 ms, por lo que en realidad es más lento. Es casi lo mismo después de Where y unos 23 ms después de GroupBy (procesando los 12 grupos en paralelo). Al menos en mi PC, no hay suficientes datos ni operaciones complejas para el paralelismo, pero GroupBy no es el más eficiente.
Usando una matriz y probando cada elemento, obtengo los resultados en aproximadamente 1,2 ms:
var maxMOs = new MyObject[12]; foreach (var r in result.Where(r => r.Date >= beginDateTime)) { var monthIndex = r.Date.Month-1; if (maxMOs[monthIndex] == null || r.Value > maxMOs[monthIndex].Value) maxMOs[monthIndex] = r; } Tenga en cuenta que los resultados no son cronológicos; puede compensar monthIndex con el mes de hoy para ordenar los resultados si lo desea.
var maxMOs = new MyObject[12]; var offset = DateTime.Now.Month-11; foreach (var r in result.Where(r => r.Date >= beginDateTime)) { var monthIndex = r.Date.Month-offset; if (maxMOs[monthIndex] == null || r.Value > maxMOs[monthIndex].Value) maxMOs[monthIndex] = r; }Una microoptimización (principalmente útil en ejecuciones repetidas) es invertir la prueba y usar el operador de propagación nula:
if (!(r.Value <= maxMOs[monthIndex]?.Value))Esto ahorra alrededor de 0,2 ms en la primera ejecución, pero hasta 0,5 ms en las ejecuciones posteriores.