From df15a217827a2bb866f3d053b1f84afc3a061370 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Wed, 30 Dec 2015 14:24:24 +0100 Subject: [PATCH] Statistics: Min/Max Absolute, MagnitudePhase (complex) --- src/Numerics/Numerics.csproj | 1 + .../Statistics/ArrayStatistics.Complex.cs | 167 +++++++++++++ .../Statistics/ArrayStatistics.Single.cs | 48 ++++ src/Numerics/Statistics/ArrayStatistics.cs | 52 +++- src/Numerics/Statistics/Statistics.cs | 124 +++++++++- .../Statistics/StreamingStatistics.cs | 226 +++++++++++++++++- .../StatisticsTests/StatisticsTests.cs | 28 +++ 7 files changed, 637 insertions(+), 9 deletions(-) create mode 100644 src/Numerics/Statistics/ArrayStatistics.Complex.cs diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index f23f0efa..c724bab7 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -230,6 +230,7 @@ + diff --git a/src/Numerics/Statistics/ArrayStatistics.Complex.cs b/src/Numerics/Statistics/ArrayStatistics.Complex.cs new file mode 100644 index 00000000..62a1e351 --- /dev/null +++ b/src/Numerics/Statistics/ArrayStatistics.Complex.cs @@ -0,0 +1,167 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// +// Copyright (c) 2009-2015 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace MathNet.Numerics.Statistics +{ +#if NOSYSNUMERICS + using Complex64 = Numerics.Complex; +#else + using Complex64 = System.Numerics.Complex; +#endif + + public static partial class ArrayStatistics + { + /// + /// Returns the smallest absolute value from the unsorted data array. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample array, no sorting is assumed. + public static Complex64 MinimumMagnitudePhase(Complex64[] data) + { + if (data.Length == 0) + { + return new Complex64(double.NaN, double.NaN); + } + + double minMagnitude = double.PositiveInfinity; + Complex64 min = new Complex64(double.PositiveInfinity, double.PositiveInfinity); + for (int i = 0; i < data.Length; i++) + { + double magnitude = data[i].Magnitude; + if (double.IsNaN(magnitude)) + { + return new Complex64(double.NaN, double.NaN); + } + if (magnitude < minMagnitude || magnitude == minMagnitude && data[i].Phase < min.Phase) + { + minMagnitude = magnitude; + min = data[i]; + } + } + + return min; + } + + /// + /// Returns the smallest absolute value from the unsorted data array. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample array, no sorting is assumed. + public static Complex32 MinimumMagnitudePhase(Complex32[] data) + { + if (data.Length == 0) + { + return new Complex32(float.NaN, float.NaN); + } + + float minMagnitude = float.PositiveInfinity; + Complex32 min = new Complex32(float.PositiveInfinity, float.PositiveInfinity); + for (int i = 0; i < data.Length; i++) + { + float magnitude = data[i].Magnitude; + if (float.IsNaN(magnitude)) + { + return new Complex32(float.NaN, float.NaN); + } + if (magnitude < minMagnitude || magnitude == minMagnitude && data[i].Phase < min.Phase) + { + minMagnitude = magnitude; + min = data[i]; + } + } + + return min; + } + + /// + /// Returns the largest absolute value from the unsorted data array. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample array, no sorting is assumed. + public static Complex64 MaximumMagnitudePhase(Complex64[] data) + { + if (data.Length == 0) + { + return new Complex64(double.NaN, double.NaN); + } + + double maxMagnitude = 0.0d; + Complex64 max = Complex64.Zero; + for (int i = 0; i < data.Length; i++) + { + double magnitude = data[i].Magnitude; + if (double.IsNaN(magnitude)) + { + return new Complex64(double.NaN, double.NaN); + } + if (magnitude > maxMagnitude || magnitude == maxMagnitude && data[i].Phase > max.Phase) + { + maxMagnitude = magnitude; + max = data[i]; + } + } + + return max; + } + + /// + /// Returns the largest absolute value from the unsorted data array. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample array, no sorting is assumed. + public static Complex32 MaximumMagnitudePhase(Complex32[] data) + { + if (data.Length == 0) + { + return new Complex32(float.NaN, float.NaN); + } + + float maxMagnitude = 0.0f; + Complex32 max = Complex32.Zero; + for (int i = 0; i < data.Length; i++) + { + float magnitude = data[i].Magnitude; + if (float.IsNaN(magnitude)) + { + return new Complex32(float.NaN, float.NaN); + } + if (magnitude > maxMagnitude || magnitude == maxMagnitude && data[i].Phase > max.Phase) + { + maxMagnitude = magnitude; + max = data[i]; + } + } + + return max; + } + } +} diff --git a/src/Numerics/Statistics/ArrayStatistics.Single.cs b/src/Numerics/Statistics/ArrayStatistics.Single.cs index d7274f7d..4588c763 100644 --- a/src/Numerics/Statistics/ArrayStatistics.Single.cs +++ b/src/Numerics/Statistics/ArrayStatistics.Single.cs @@ -83,6 +83,54 @@ namespace MathNet.Numerics.Statistics return max; } + /// + /// Returns the smallest absolute value from the unsorted data array. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample array, no sorting is assumed. + public static float MinimumAbsolute(float[] data) + { + if (data.Length == 0) + { + return float.NaN; + } + + float min = float.PositiveInfinity; + for (int i = 0; i < data.Length; i++) + { + if (Math.Abs(data[i]) < min || float.IsNaN(data[i])) + { + min = Math.Abs(data[i]); + } + } + + return min; + } + + /// + /// Returns the largest absolute value from the unsorted data array. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample array, no sorting is assumed. + public static float MaximumAbsolute(float[] data) + { + if (data.Length == 0) + { + return float.NaN; + } + + float max = 0.0f; + for (int i = 0; i < data.Length; i++) + { + if (Math.Abs(data[i]) > max || float.IsNaN(data[i])) + { + max = Math.Abs(data[i]); + } + } + + return max; + } + /// /// Estimates the arithmetic sample mean from the unsorted data array. /// Returns NaN if data is empty or any entry is NaN. diff --git a/src/Numerics/Statistics/ArrayStatistics.cs b/src/Numerics/Statistics/ArrayStatistics.cs index dce356bf..de964c8f 100644 --- a/src/Numerics/Statistics/ArrayStatistics.cs +++ b/src/Numerics/Statistics/ArrayStatistics.cs @@ -57,7 +57,7 @@ namespace MathNet.Numerics.Statistics return double.NaN; } - var min = double.PositiveInfinity; + double min = double.PositiveInfinity; for (int i = 0; i < data.Length; i++) { if (data[i] < min || double.IsNaN(data[i])) @@ -81,7 +81,7 @@ namespace MathNet.Numerics.Statistics return double.NaN; } - var max = double.NegativeInfinity; + double max = double.NegativeInfinity; for (int i = 0; i < data.Length; i++) { if (data[i] > max || double.IsNaN(data[i])) @@ -93,6 +93,54 @@ namespace MathNet.Numerics.Statistics return max; } + /// + /// Returns the smallest absolute value from the unsorted data array. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample array, no sorting is assumed. + public static double MinimumAbsolute(double[] data) + { + if (data.Length == 0) + { + return double.NaN; + } + + double min = double.PositiveInfinity; + for (int i = 0; i < data.Length; i++) + { + if (Math.Abs(data[i]) < min || double.IsNaN(data[i])) + { + min = Math.Abs(data[i]); + } + } + + return min; + } + + /// + /// Returns the largest absolute value from the unsorted data array. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample array, no sorting is assumed. + public static double MaximumAbsolute(double[] data) + { + if (data.Length == 0) + { + return double.NaN; + } + + double max = 0.0d; + for (int i = 0; i < data.Length; i++) + { + if (Math.Abs(data[i]) > max || double.IsNaN(data[i])) + { + max = Math.Abs(data[i]); + } + } + + return max; + } + /// /// Estimates the arithmetic sample mean from the unsorted data array. /// Returns NaN if data is empty or any entry is NaN. diff --git a/src/Numerics/Statistics/Statistics.cs b/src/Numerics/Statistics/Statistics.cs index 2e2cd9ee..1abbe9d4 100644 --- a/src/Numerics/Statistics/Statistics.cs +++ b/src/Numerics/Statistics/Statistics.cs @@ -28,11 +28,17 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using System; +using System.Collections.Generic; +using System.Linq; + namespace MathNet.Numerics.Statistics { - using System; - using System.Collections.Generic; - using System.Linq; +#if NOSYSNUMERICS + using Complex64 = Numerics.Complex; +#else + using Complex64 = System.Numerics.Complex; +#endif /// /// Extension methods to return basic statistics on set of data. @@ -120,6 +126,118 @@ namespace MathNet.Numerics.Statistics return StreamingStatistics.Maximum(data.Where(d => d.HasValue).Select(d => d.Value)); } + /// + /// Returns the minimum absolute value in the sample data. + /// Returns NaN if data is empty or if any entry is NaN. + /// + /// The sample data. + /// The minimum value in the sample data. + public static double MinimumAbsolute(this IEnumerable data) + { + var array = data as double[]; + return array != null + ? ArrayStatistics.MinimumAbsolute(array) + : StreamingStatistics.MinimumAbsolute(data); + } + + /// + /// Returns the minimum absolute value in the sample data. + /// Returns NaN if data is empty or if any entry is NaN. + /// + /// The sample data. + /// The minimum value in the sample data. + public static float MinimumAbsolute(this IEnumerable data) + { + var array = data as float[]; + return array != null + ? ArrayStatistics.MinimumAbsolute(array) + : StreamingStatistics.MinimumAbsolute(data); + } + + /// + /// Returns the maximum absolute value in the sample data. + /// Returns NaN if data is empty or if any entry is NaN. + /// + /// The sample data. + /// The maximum value in the sample data. + public static double MaximumAbsolute(this IEnumerable data) + { + var array = data as double[]; + return array != null + ? ArrayStatistics.MaximumAbsolute(array) + : StreamingStatistics.MaximumAbsolute(data); + } + + /// + /// Returns the maximum absolute value in the sample data. + /// Returns NaN if data is empty or if any entry is NaN. + /// + /// The sample data. + /// The maximum value in the sample data. + public static float MaximumAbsolute(this IEnumerable data) + { + var array = data as float[]; + return array != null + ? ArrayStatistics.MaximumAbsolute(array) + : StreamingStatistics.MaximumAbsolute(data); + } + + /// + /// Returns the minimum magnitude and phase value in the sample data. + /// Returns NaN if data is empty or if any entry is NaN. + /// + /// The sample data. + /// The minimum value in the sample data. + public static Complex64 MinimumMagnitudePhase(this IEnumerable data) + { + var array = data as Complex64[]; + return array != null + ? ArrayStatistics.MinimumMagnitudePhase(array) + : StreamingStatistics.MinimumMagnitudePhase(data); + } + + /// + /// Returns the minimum magnitude and phase value in the sample data. + /// Returns NaN if data is empty or if any entry is NaN. + /// + /// The sample data. + /// The minimum value in the sample data. + public static Complex32 MinimumMagnitudePhase(this IEnumerable data) + { + var array = data as Complex32[]; + return array != null + ? ArrayStatistics.MinimumMagnitudePhase(array) + : StreamingStatistics.MinimumMagnitudePhase(data); + } + + /// + /// Returns the maximum magnitude and phase value in the sample data. + /// Returns NaN if data is empty or if any entry is NaN. + /// + /// The sample data. + /// The minimum value in the sample data. + public static Complex64 MaximumMagnitudePhase(this IEnumerable data) + { + var array = data as Complex64[]; + return array != null + ? ArrayStatistics.MaximumMagnitudePhase(array) + : StreamingStatistics.MaximumMagnitudePhase(data); + } + + /// + /// Returns the maximum magnitude and phase value in the sample data. + /// Returns NaN if data is empty or if any entry is NaN. + /// + /// The sample data. + /// The minimum value in the sample data. + public static Complex32 MaximumMagnitudePhase(this IEnumerable data) + { + var array = data as Complex32[]; + return array != null + ? ArrayStatistics.MaximumMagnitudePhase(array) + : StreamingStatistics.MaximumMagnitudePhase(data); + } + /// /// Evaluates the sample mean, an estimate of the population mean. /// Returns NaN if data is empty or if any entry is NaN. diff --git a/src/Numerics/Statistics/StreamingStatistics.cs b/src/Numerics/Statistics/StreamingStatistics.cs index 4076c66e..8f9b1d6e 100644 --- a/src/Numerics/Statistics/StreamingStatistics.cs +++ b/src/Numerics/Statistics/StreamingStatistics.cs @@ -35,6 +35,12 @@ using MathNet.Numerics.Properties; namespace MathNet.Numerics.Statistics { +#if NOSYSNUMERICS + using Complex64 = Numerics.Complex; +#else + using Complex64 = System.Numerics.Complex; +#endif + /// /// Statistics operating on an IEnumerable in a single pass, without keeping the full data in memory. /// Can be used in a streaming way, e.g. on large datasets not fitting into memory. @@ -51,7 +57,7 @@ namespace MathNet.Numerics.Statistics /// Sample stream, no sorting is assumed. public static double Minimum(IEnumerable stream) { - var min = double.PositiveInfinity; + double min = double.PositiveInfinity; bool any = false; foreach (var d in stream) @@ -74,7 +80,7 @@ namespace MathNet.Numerics.Statistics /// Sample stream, no sorting is assumed. public static float Minimum(IEnumerable stream) { - var min = float.PositiveInfinity; + float min = float.PositiveInfinity; bool any = false; foreach (var d in stream) @@ -97,7 +103,7 @@ namespace MathNet.Numerics.Statistics /// Sample stream, no sorting is assumed. public static double Maximum(IEnumerable stream) { - var max = double.NegativeInfinity; + double max = double.NegativeInfinity; bool any = false; foreach (var d in stream) @@ -120,7 +126,7 @@ namespace MathNet.Numerics.Statistics /// Sample stream, no sorting is assumed. public static float Maximum(IEnumerable stream) { - var max = float.NegativeInfinity; + float max = float.NegativeInfinity; bool any = false; foreach (var d in stream) @@ -136,6 +142,218 @@ namespace MathNet.Numerics.Statistics return any ? max : float.NaN; } + /// + /// Returns the smallest absolute value from the enumerable, in a single pass without memoization. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample stream, no sorting is assumed. + public static double MinimumAbsolute(IEnumerable stream) + { + double min = double.PositiveInfinity; + bool any = false; + + foreach (var d in stream) + { + if (Math.Abs(d) < min || double.IsNaN(d)) + { + min = Math.Abs(d); + } + + any = true; + } + + return any ? min : double.NaN; + } + + /// + /// Returns the smallest absolute value from the enumerable, in a single pass without memoization. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample stream, no sorting is assumed. + public static float MinimumAbsolute(IEnumerable stream) + { + float min = float.PositiveInfinity; + bool any = false; + + foreach (var d in stream) + { + if (Math.Abs(d) < min || float.IsNaN(d)) + { + min = Math.Abs(d); + } + + any = true; + } + + return any ? min : float.NaN; + } + + /// + /// Returns the largest absolute value from the enumerable, in a single pass without memoization. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample stream, no sorting is assumed. + public static double MaximumAbsolute(IEnumerable stream) + { + double max = 0.0d; + bool any = false; + + foreach (var d in stream) + { + if (Math.Abs(d) > max || double.IsNaN(d)) + { + max = Math.Abs(d); + } + + any = true; + } + + return any ? max : double.NaN; + } + + /// + /// Returns the largest absolute value from the enumerable, in a single pass without memoization. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample stream, no sorting is assumed. + public static float MaximumAbsolute(IEnumerable stream) + { + float max = 0.0f; + bool any = false; + + foreach (var d in stream) + { + if (Math.Abs(d) > max || float.IsNaN(d)) + { + max = Math.Abs(d); + } + + any = true; + } + + return any ? max : float.NaN; + } + + /// + /// Returns the smallest absolute value from the enumerable, in a single pass without memoization. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample stream, no sorting is assumed. + public static Complex64 MinimumMagnitudePhase(IEnumerable stream) + { + double minMagnitude = double.PositiveInfinity; + Complex64 min = new Complex64(double.PositiveInfinity, double.PositiveInfinity); + bool any = false; + + foreach (var d in stream) + { + double magnitude = d.Magnitude; + if (double.IsNaN(magnitude)) + { + return new Complex64(double.NaN, double.NaN); + } + if (magnitude < minMagnitude || magnitude == minMagnitude && d.Phase < min.Phase) + { + minMagnitude = magnitude; + min = d; + } + + any = true; + } + + return any ? min : new Complex64(double.NaN, double.NaN); + } + + /// + /// Returns the smallest absolute value from the enumerable, in a single pass without memoization. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample stream, no sorting is assumed. + public static Complex32 MinimumMagnitudePhase(IEnumerable stream) + { + float minMagnitude = float.PositiveInfinity; + Complex32 min = new Complex32(float.PositiveInfinity, float.PositiveInfinity); + bool any = false; + + foreach (var d in stream) + { + float magnitude = d.Magnitude; + if (float.IsNaN(magnitude)) + { + return new Complex32(float.NaN, float.NaN); + } + if (magnitude < minMagnitude || magnitude == minMagnitude && d.Phase < min.Phase) + { + minMagnitude = magnitude; + min = d; + } + + any = true; + } + + return any ? min : new Complex32(float.NaN, float.NaN); + } + + /// + /// Returns the largest absolute value from the enumerable, in a single pass without memoization. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample stream, no sorting is assumed. + public static Complex64 MaximumMagnitudePhase(IEnumerable stream) + { + double maxMagnitude = 0.0d; + Complex64 max = Complex64.Zero; + bool any = false; + + foreach (var d in stream) + { + double magnitude = d.Magnitude; + if (double.IsNaN(magnitude)) + { + return new Complex64(double.NaN, double.NaN); + } + if (magnitude > maxMagnitude || magnitude == maxMagnitude && d.Phase > max.Phase) + { + maxMagnitude = magnitude; + max = d; + } + + any = true; + } + + return any ? max : new Complex64(double.NaN, double.NaN); + } + + /// + /// Returns the largest absolute value from the enumerable, in a single pass without memoization. + /// Returns NaN if data is empty or any entry is NaN. + /// + /// Sample stream, no sorting is assumed. + public static Complex32 MaximumMagnitudePhase(IEnumerable stream) + { + float maxMagnitude = 0.0f; + Complex32 max = Complex32.Zero; + bool any = false; + + foreach (var d in stream) + { + float magnitude = d.Magnitude; + if (float.IsNaN(magnitude)) + { + return new Complex32(float.NaN, float.NaN); + } + if (magnitude > maxMagnitude || magnitude == maxMagnitude && d.Phase > max.Phase) + { + maxMagnitude = magnitude; + max = d; + } + + any = true; + } + + return any ? max : new Complex32(float.NaN, float.NaN); + } + /// /// Estimates the arithmetic sample mean from the enumerable, in a single pass without memoization. /// Returns NaN if data is empty or any entry is NaN. diff --git a/src/UnitTests/StatisticsTests/StatisticsTests.cs b/src/UnitTests/StatisticsTests/StatisticsTests.cs index 73d0dace..3bc03f4f 100644 --- a/src/UnitTests/StatisticsTests/StatisticsTests.cs +++ b/src/UnitTests/StatisticsTests/StatisticsTests.cs @@ -1107,6 +1107,34 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests var data = new double[] { 1, 2, double.NaN }; Assert.That(double.IsNaN(StreamingStatistics.Entropy(data))); } + + [Test] + public void MinimumMagnitudePhase() + { + var a = new[] { new Complex32(1.0f, 2.0f), new Complex32(float.PositiveInfinity, float.NegativeInfinity), new Complex32(-2.0f, 4.0f) }; + Assert.That(ArrayStatistics.MinimumMagnitudePhase(a), Is.EqualTo(a[0])); + } + + [Test] + public void MinimumMagnitudePhaseOfNaNIsNaN() + { + var a = new[] { new Complex32(1.0f, 2.0f), new Complex32(float.NaN, float.NegativeInfinity), new Complex32(-2.0f, 4.0f) }; + Assert.That(ArrayStatistics.MinimumMagnitudePhase(a).IsNaN(), Is.True); + } + + [Test] + public void MaximumMagnitudePhase() + { + var a = new[] { new Complex32(1.0f, 2.0f), new Complex32(float.PositiveInfinity, float.NegativeInfinity), new Complex32(-2.0f, 4.0f) }; + Assert.That(ArrayStatistics.MaximumMagnitudePhase(a), Is.EqualTo(a[1])); + } + + [Test] + public void MaximumMagnitudePhaseOfNaNIsNaN() + { + var a = new[] { new Complex32(1.0f, 2.0f), new Complex32(float.NaN, float.NegativeInfinity), new Complex32(-2.0f, 4.0f) }; + Assert.That(ArrayStatistics.MaximumMagnitudePhase(a).IsNaN(), Is.True); + } } }