private static double ToTimeUnits(Time value, TargetTimeUnit target) { double time = value.ConvertTo(target.Unit); if (!double.IsNaN(target.Factor)) { time /= target.Factor; } return(time); }
private static Time FromTimeUnits(double value, TargetTimeUnit target) { double time = Time.ConvertFrom(value, target.Unit); if (!double.IsNaN(target.Factor)) { time *= target.Factor; } return(time); }
public static bool TryParse(string value, out TargetTimeUnit targetTimeUnit) { if (Enum.TryParse(value, out TimeUnit timeUnit)) { targetTimeUnit = new TargetTimeUnit { Unit = timeUnit }; return(true); } switch (value?.ToLowerInvariant()) { case "milliseconds": targetTimeUnit = new TargetTimeUnit { Unit = TimeUnit.Seconds, Factor = SI.Milli }; return(true); case "microseconds": targetTimeUnit = new TargetTimeUnit { Unit = TimeUnit.Seconds, Factor = SI.Micro }; return(true); case "nanoseconds": targetTimeUnit = new TargetTimeUnit { Unit = TimeUnit.Seconds, Factor = SI.Nano }; return(true); } targetTimeUnit = null; return(false); }
// Design philosophy: whenever possible this function should delay source enumeration since source data sets could be very large. private static IEnumerable <DataSourceValue> ExecuteSeriesFunctionOverSource(IEnumerable <DataSourceValue> source, SeriesFunction seriesFunction, string[] parameters, bool isSliceOperation = false) { DataSourceValue[] values; DataSourceValue result = new DataSourceValue(); double lastValue = double.NaN; double lastTime = 0.0D; string lastTarget = null; IEnumerable <double> trackedValues = source.Select(dataValue => { lastTime = dataValue.Time; lastTarget = dataValue.Target; return(dataValue.Value); }); double baseTime, timeStep, value, low, high; bool normalizeTime, lowInclusive, highInclusive; int count; TargetTimeUnit timeUnit; AngleUnit angleUnit; switch (seriesFunction) { case SeriesFunction.Minimum: DataSourceValue minValue = new DataSourceValue { Value = double.MaxValue }; foreach (DataSourceValue dataValue in source) { if (dataValue.Value <= minValue.Value) { minValue = dataValue; } } if (minValue.Time > 0.0D) { yield return(minValue); } break; case SeriesFunction.Maximum: DataSourceValue maxValue = new DataSourceValue { Value = double.MinValue }; foreach (DataSourceValue dataValue in source) { if (dataValue.Value >= maxValue.Value) { maxValue = dataValue; } } if (maxValue.Time > 0.0D) { yield return(maxValue); } break; case SeriesFunction.Average: result.Value = trackedValues.Average(); result.Time = lastTime; result.Target = lastTarget; yield return(result); break; case SeriesFunction.Total: result.Value = trackedValues.Sum(); result.Time = lastTime; result.Target = lastTarget; yield return(result); break; case SeriesFunction.Range: DataSourceValue rangeMin = new DataSourceValue { Value = double.MaxValue }; DataSourceValue rangeMax = new DataSourceValue { Value = double.MinValue }; foreach (DataSourceValue dataValue in source) { if (dataValue.Value <= rangeMin.Value) { rangeMin = dataValue; } if (dataValue.Value >= rangeMax.Value) { rangeMax = dataValue; } } if (rangeMin.Time > 0.0D && rangeMax.Time > 0.0D) { result = rangeMax; result.Value = rangeMax.Value - rangeMin.Value; yield return(result); } break; case SeriesFunction.Count: result.Value = trackedValues.Count(); result.Time = lastTime; result.Target = lastTarget; yield return(result); break; case SeriesFunction.Distinct: foreach (DataSourceValue dataValue in source.DistinctBy(dataValue => dataValue.Value)) { yield return(dataValue); } break; case SeriesFunction.AbsoluteValue: foreach (DataSourceValue dataValue in source.Select(dataValue => new DataSourceValue { Value = Math.Abs(dataValue.Value), Time = dataValue.Time, Target = dataValue.Target })) { yield return(dataValue); } break; case SeriesFunction.Add: value = ParseFloat(parameters[0], source, false, isSliceOperation); foreach (DataSourceValue dataValue in source.Select(dataValue => new DataSourceValue { Value = dataValue.Value + value, Time = dataValue.Time, Target = dataValue.Target })) { yield return(dataValue); } break; case SeriesFunction.Subtract: value = ParseFloat(parameters[0], source, false, isSliceOperation); foreach (DataSourceValue dataValue in source.Select(dataValue => new DataSourceValue { Value = dataValue.Value - value, Time = dataValue.Time, Target = dataValue.Target })) { yield return(dataValue); } break; case SeriesFunction.Multiply: value = ParseFloat(parameters[0], source, false, isSliceOperation); foreach (DataSourceValue dataValue in source.Select(dataValue => new DataSourceValue { Value = dataValue.Value * value, Time = dataValue.Time, Target = dataValue.Target })) { yield return(dataValue); } break; case SeriesFunction.Divide: value = ParseFloat(parameters[0], source, false, isSliceOperation); foreach (DataSourceValue dataValue in source.Select(dataValue => new DataSourceValue { Value = dataValue.Value / value, Time = dataValue.Time, Target = dataValue.Target })) { yield return(dataValue); } break; case SeriesFunction.Round: count = parameters.Length == 0 ? 0 : ParseInt(parameters[0]); foreach (DataSourceValue dataValue in source.Select(dataValue => new DataSourceValue { Value = Math.Round(dataValue.Value, count), Time = dataValue.Time, Target = dataValue.Target })) { yield return(dataValue); } break; case SeriesFunction.Floor: foreach (DataSourceValue dataValue in source.Select(dataValue => new DataSourceValue { Value = Math.Floor(dataValue.Value), Time = dataValue.Time, Target = dataValue.Target })) { yield return(dataValue); } break; case SeriesFunction.Ceiling: foreach (DataSourceValue dataValue in source.Select(dataValue => new DataSourceValue { Value = Math.Ceiling(dataValue.Value), Time = dataValue.Time, Target = dataValue.Target })) { yield return(dataValue); } break; case SeriesFunction.Truncate: foreach (DataSourceValue dataValue in source.Select(dataValue => new DataSourceValue { Value = Math.Truncate(dataValue.Value), Time = dataValue.Time, Target = dataValue.Target })) { yield return(dataValue); } break; case SeriesFunction.StandardDeviation: result.Value = trackedValues.StandardDeviation(parameters.Length > 0 && parameters[0].Trim().ParseBoolean()); result.Time = lastTime; result.Target = lastTarget; yield return(result); break; case SeriesFunction.Median: values = source.Median(); if (values.Length == 0) //-V3080 { yield break; } result = values.Last(); if (values.Length > 1) { result.Value = values.Select(dataValue => dataValue.Value).Average(); } yield return(result); break; case SeriesFunction.Mode: values = source.ToArray(); yield return(values.MajorityBy(values.Last(), dataValue => dataValue.Value, false)); break; case SeriesFunction.Top: values = source.ToArray(); if (values.Length == 0) { yield break; } count = ParseCount(parameters[0], values, isSliceOperation); if (count > values.Length) { count = values.Length; } normalizeTime = parameters.Length == 1 || parameters[1].Trim().ParseBoolean(); baseTime = values[0].Time; timeStep = (values[values.Length - 1].Time - baseTime) / (count - 1).NotZero(1); Array.Sort(values, (a, b) => a.Value < b.Value ? -1 : (a.Value > b.Value ? 1 : 0)); foreach (DataSourceValue dataValue in values.Take(count).Select((dataValue, i) => new DataSourceValue { Value = dataValue.Value, Time = normalizeTime ? baseTime + i * timeStep : dataValue.Time, Target = dataValue.Target })) { yield return(dataValue); } break; case SeriesFunction.Bottom: values = source.ToArray(); if (values.Length == 0) { yield break; } count = ParseCount(parameters[0], values, isSliceOperation); if (count > values.Length) { count = values.Length; } normalizeTime = parameters.Length == 1 || parameters[1].Trim().ParseBoolean(); baseTime = values[0].Time; timeStep = (values[values.Length - 1].Time - baseTime) / (count - 1).NotZero(1); Array.Sort(values, (a, b) => a.Value > b.Value ? -1 : (a.Value < b.Value ? 1 : 0)); foreach (DataSourceValue dataValue in values.Take(count).Select((dataValue, i) => new DataSourceValue { Value = dataValue.Value, Time = normalizeTime ? baseTime + i * timeStep : dataValue.Time, Target = dataValue.Target })) { yield return(dataValue); } break; case SeriesFunction.Random: values = source.ToArray(); if (values.Length == 0) { yield break; } count = ParseCount(parameters[0], values, isSliceOperation); if (count > values.Length) { count = values.Length; } normalizeTime = parameters.Length == 1 || parameters[1].Trim().ParseBoolean(); baseTime = values[0].Time; timeStep = (values[values.Length - 1].Time - baseTime) / (count - 1).NotZero(1); List <int> indexes = new List <int>(Enumerable.Range(0, values.Length)); indexes.Scramble(); foreach (DataSourceValue dataValue in indexes.Take(count).Select((index, i) => new DataSourceValue { Value = values[index].Value, Time = normalizeTime ? baseTime + i * timeStep : values[index].Time, Target = values[index].Target })) { yield return(dataValue); } break; case SeriesFunction.First: values = source.ToArray(); if (values.Length == 0) { yield break; } count = parameters.Length == 0 ? 1 : ParseCount(parameters[0], values, isSliceOperation); if (count > values.Length) { count = values.Length; } for (int i = 0; i < count; i++) { yield return(values[i]); } break; case SeriesFunction.Last: values = source.ToArray(); if (values.Length == 0) { yield break; } count = parameters.Length == 0 ? 1 : ParseCount(parameters[0], values, isSliceOperation); if (count > values.Length) { count = values.Length; } for (int i = 0; i < count; i++) { yield return(values[values.Length - 1 - i]); } break; case SeriesFunction.Percentile: double percent = ParsePercentage(parameters[0]); values = source.ToArray(); if (values.Length == 0) { yield break; } Array.Sort(values, (a, b) => a.Value < b.Value ? -1 : (a.Value > b.Value ? 1 : 0)); count = values.Length; if (percent == 0.0D) { yield return(values.First()); } else if (percent == 100.0D) { yield return(values.Last()); } else { double n = (count - 1) * (percent / 100.0D) + 1.0D; int k = (int)n; DataSourceValue kData = values[k]; double d = n - k; double k0 = values[k - 1].Value; double k1 = kData.Value; result.Value = k0 + d * (k1 - k0); result.Time = kData.Time; result.Target = kData.Target; yield return(result); } break; case SeriesFunction.Difference: foreach (DataSourceValue dataValue in source) { if (lastTime > 0.0D) { yield return new DataSourceValue { Value = dataValue.Value - lastValue, Time = dataValue.Time, Target = lastTarget } } ; lastValue = dataValue.Value; lastTime = dataValue.Time; lastTarget = dataValue.Target; } break; case SeriesFunction.TimeDifference: if (parameters.Length == 0 || !TargetTimeUnit.TryParse(parameters[0], out timeUnit)) { timeUnit = new TargetTimeUnit { Unit = TimeUnit.Seconds } } ; foreach (DataSourceValue dataValue in source) { if (lastTime > 0.0D) { yield return new DataSourceValue { Value = ToTimeUnits((dataValue.Time - lastTime) * SI.Milli, timeUnit), Time = dataValue.Time, Target = lastTarget } } ; lastTime = dataValue.Time; lastTarget = dataValue.Target; } break; case SeriesFunction.Derivative: if (parameters.Length == 0 || !TargetTimeUnit.TryParse(parameters[0], out timeUnit)) { timeUnit = new TargetTimeUnit { Unit = TimeUnit.Seconds } } ; foreach (DataSourceValue dataValue in source) { if (lastTime > 0.0D) { yield return new DataSourceValue { Value = (dataValue.Value - lastValue) / ToTimeUnits((dataValue.Time - lastTime) * SI.Milli, timeUnit), Time = dataValue.Time, Target = lastTarget } } ; lastValue = dataValue.Value; lastTime = dataValue.Time; lastTarget = dataValue.Target; } break; case SeriesFunction.TimeIntegration: if (parameters.Length == 0 || !TargetTimeUnit.TryParse(parameters[0], out timeUnit)) { timeUnit = new TargetTimeUnit { Unit = TimeUnit.Hours } } ; result.Value = 0.0D; foreach (DataSourceValue dataValue in source) { if (lastTime > 0.0D) { result.Value += dataValue.Value * ToTimeUnits((dataValue.Time - lastTime) * SI.Milli, timeUnit); } lastTime = dataValue.Time; lastTarget = dataValue.Target; } if (lastTime > 0.0D) { result.Time = lastTime; result.Target = lastTarget; yield return(result); } break; case SeriesFunction.Interval: if (parameters.Length == 1 || !TargetTimeUnit.TryParse(parameters[1], out timeUnit)) { timeUnit = new TargetTimeUnit { Unit = TimeUnit.Seconds } } ; value = FromTimeUnits(ParseFloat(parameters[0], source, true, isSliceOperation), timeUnit) / SI.Milli; foreach (DataSourceValue dataValue in source) { if (lastTime > 0.0D) { if (dataValue.Time - lastTime > value) { lastTime = dataValue.Time; yield return(dataValue); } } else { lastTime = dataValue.Time; yield return(dataValue); } } break; case SeriesFunction.IncludeRange: low = ParseFloat(parameters[0], source, false, isSliceOperation); high = ParseFloat(parameters[1], source, false, isSliceOperation); lowInclusive = parameters.Length > 2 && parameters[2].Trim().ParseBoolean(); highInclusive = parameters.Length > 3 ? parameters[3].Trim().ParseBoolean() : lowInclusive; foreach (DataSourceValue dataValue in source.Where(dataValue => (lowInclusive ? dataValue.Value >= low : dataValue.Value > low) && (highInclusive ? dataValue.Value <= high : dataValue.Value < high))) { yield return(dataValue); } break; case SeriesFunction.ExcludeRange: low = ParseFloat(parameters[0], source, false, isSliceOperation); high = ParseFloat(parameters[1], source, false, isSliceOperation); lowInclusive = parameters.Length > 2 && parameters[2].Trim().ParseBoolean(); highInclusive = parameters.Length > 3 ? parameters[3].Trim().ParseBoolean() : lowInclusive; foreach (DataSourceValue dataValue in source.Where(dataValue => (lowInclusive ? dataValue.Value <= low : dataValue.Value < low) || (highInclusive ? dataValue.Value >= high : dataValue.Value > high))) { yield return(dataValue); } break; case SeriesFunction.FilterNaN: bool alsoFilterInifinity = parameters.Length == 0 || parameters[0].Trim().ParseBoolean(); foreach (DataSourceValue dataValue in source.Where(dataValue => !(double.IsNaN(dataValue.Value) || alsoFilterInifinity && double.IsInfinity(dataValue.Value)))) //-V3130 { yield return(dataValue); } break; case SeriesFunction.UnwrapAngle: if (parameters.Length == 0 || !Enum.TryParse(parameters[0], true, out angleUnit)) { angleUnit = AngleUnit.Degrees; } values = source.ToArray(); foreach (DataSourceValue dataValue in Angle.Unwrap(values.Select(dataValue => Angle.ConvertFrom(dataValue.Value, angleUnit))).Select((angle, index) => new DataSourceValue { Value = angle.ConvertTo(angleUnit), Time = values[index].Time, Target = values[index].Target })) { yield return(dataValue); } break; case SeriesFunction.WrapAngle: if (parameters.Length == 0 || !Enum.TryParse(parameters[0], true, out angleUnit)) { angleUnit = AngleUnit.Degrees; } foreach (DataSourceValue dataValue in source.Select(dataValue => new DataSourceValue { Value = Angle.ConvertFrom(dataValue.Value, angleUnit).ToRange(-Math.PI, false).ConvertTo(angleUnit), Time = dataValue.Time, Target = dataValue.Target })) { yield return(dataValue); } break; } }