diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs
index 0a16b3a3155..62bebc4d212 100644
--- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs
+++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs
@@ -34,7 +34,7 @@ public sealed class GenericMeasureInfo : MeasureInfo
///
public GenericMeasureInfo()
{
- Average = Sum = Maximum = Minimum = null;
+ Average = Sum = Maximum = Minimum = StandardDeviation = null;
}
///
@@ -71,6 +71,11 @@ public GenericMeasureInfo()
///
///
public double? Minimum { get; set; }
+
+ ///
+ /// The Standard Deviation of property values.
+ ///
+ public double? StandardDeviation { get; set; }
}
///
@@ -90,7 +95,7 @@ public sealed class GenericObjectMeasureInfo : MeasureInfo
///
public GenericObjectMeasureInfo()
{
- Average = Sum = null;
+ Average = Sum = StandardDeviation = null;
Maximum = Minimum = null;
}
@@ -128,6 +133,11 @@ public GenericObjectMeasureInfo()
///
///
public object Minimum { get; set; }
+
+ ///
+ /// The Standard Deviation of property values.
+ ///
+ public double? StandardDeviation { get; set; }
}
///
@@ -225,6 +235,8 @@ private class Statistics
// Generic/Numeric statistics
internal double sum = 0.0;
+ internal double sumPrevious = 0.0;
+ internal double variance = 0.0;
internal object max = null;
internal object min = null;
@@ -263,6 +275,24 @@ public MeasureObjectCommand()
#endregion Common parameters in both sets
+ ///
+ /// Set to true if Standard Deviation is to be returned.
+ ///
+ [Parameter(ParameterSetName = GenericParameterSet)]
+ public SwitchParameter StandardDeviation
+ {
+ get
+ {
+ return _measureStandardDeviation;
+ }
+ set
+ {
+ _measureStandardDeviation = value;
+ }
+ }
+
+ private bool _measureStandardDeviation;
+
///
/// Set to true is Sum is to be returned
///
@@ -525,7 +555,7 @@ private void AnalyzeValue(string propertyName, object objValue)
AnalyzeString(strValue, stat);
}
- if (_measureAverage || _measureSum)
+ if (_measureAverage || _measureSum || _measureStandardDeviation)
{
double numValue = 0.0;
if (!LanguagePrimitives.TryConvertTo(objValue, out numValue))
@@ -708,8 +738,19 @@ private void AnalyzeString(string strValue, Statistics stat)
///
private void AnalyzeNumber(double numValue, Statistics stat)
{
- if (_measureSum || _measureAverage)
+ if (_measureSum || _measureAverage || _measureStandardDeviation)
+ {
+ stat.sumPrevious = stat.sum;
stat.sum += numValue;
+ }
+ if (_measureStandardDeviation && stat.count > 1)
+ {
+ // Based off of iterative method of calculating variance on
+ // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm
+ double avgPrevious = stat.sumPrevious / (stat.count - 1);
+ stat.variance *= (stat.count - 2.0) / (stat.count - 1);
+ stat.variance += (numValue - avgPrevious) * (numValue - avgPrevious) / stat.count;
+ }
}
///
@@ -790,6 +831,7 @@ private MeasureInfo CreateGenericMeasureInfo(Statistics stat, bool shouldUseGene
{
double? sum = null;
double? average = null;
+ double? StandardDeviation = null;
object max = null;
object min = null;
@@ -797,8 +839,14 @@ private MeasureInfo CreateGenericMeasureInfo(Statistics stat, bool shouldUseGene
{
if (_measureSum)
sum = stat.sum;
+
if (_measureAverage && stat.count > 0)
average = stat.sum / stat.count;
+
+ if (_measureStandardDeviation)
+ {
+ StandardDeviation = Math.Sqrt(stat.variance);
+ }
}
if (_measureMax)
@@ -835,6 +883,7 @@ private MeasureInfo CreateGenericMeasureInfo(Statistics stat, bool shouldUseGene
gmi.Count = stat.count;
gmi.Sum = sum;
gmi.Average = average;
+ gmi.StandardDeviation = StandardDeviation;
if (null != max)
{
gmi.Maximum = (double)max;
@@ -887,7 +936,7 @@ private TextMeasureInfo CreateTextMeasureInfo(Statistics stat)
///
/// Whether or not a numeric conversion error occurred.
- /// If true, then average/sum will not be output.
+ /// If true, then average/sum/standard deviation will not be output.
///
private bool _nonNumericError = false;
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Measure-Object.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Measure-Object.Tests.ps1
index 6791d790a1a..0a5b4af2a93 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Measure-Object.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Measure-Object.Tests.ps1
@@ -3,6 +3,7 @@
Describe "Measure-Object" -Tags "CI" {
BeforeAll {
$testObject = 1,3,4
+ $testObject2 = 1..100
}
It "Should be able to be called without error" {
@@ -17,6 +18,51 @@ Describe "Measure-Object" -Tags "CI" {
$($testObject | Measure-Object).Count | Should Be $testObject.Length
}
+ It "Should calculate Standard Deviation" {
+ $actual = ($testObject | Measure-Object -StandardDeviation)
+ # We check this way since .StandardDeviation returns a double value
+ # 1.52752523165195 was calculated outside powershell using formula from
+ # http://mathworld.wolfram.com/StandardDeviation.html
+ [Math]::abs($actual.StandardDeviation - 1.52752523165195) | Should -BeLessThan .00000000000001
+ }
+
+
+ It "Should calculate Standard Deviation" {
+ $actual = ($testObject2 | Measure-Object -StandardDeviation)
+ # We check this way since .StandardDeviation returns a double value
+ # 29.011491975882 was calculated outside powershell using formula from
+ # http://mathworld.wolfram.com/StandardDeviation.html
+ [Math]::abs($actual.StandardDeviation - 29.011491975882) | Should -BeLessThan .0000000000001
+ }
+
+ It "Should calculate Standard Deviation with -Sum" {
+ $actual = ($testObject | Measure-Object -Sum -StandardDeviation)
+ # We check this way since .StandardDeviation returns a double value
+ $actual.Sum | Should Be 8
+ # 1.52752523165195 was calculated outside powershell using formula from
+ # http://mathworld.wolfram.com/StandardDeviation.html
+ [Math]::abs($actual.StandardDeviation - 1.52752523165195) | Should -BeLessThan .00000000000001
+ }
+
+ It "Should calculate Standard Deviation with -Average" {
+ $actual = ($testObject | Measure-Object -Average -StandardDeviation)
+ # We check this way since .StandardDeviation returns a double value
+ [Math]::abs($actual.Average - 2.66666666666667) | Should -BeLessThan .00000000000001
+ # 1.52752523165195 was calculated outside powershell using formula from
+ # http://mathworld.wolfram.com/StandardDeviation.html
+ [Math]::abs($actual.StandardDeviation - 1.52752523165195) | Should -BeLessThan .00000000000001
+ }
+
+ It "Should calculate Standard Deviation with -Sum -Average" {
+ $actual = ($testObject2 | Measure-Object -Sum -Average -StandardDeviation)
+ # We check this way since .StandardDeviation returns a double value
+ $actual.Sum | Should Be 5050
+ $actual.Average | Should Be 50.5
+ # 29.011491975882 was calculated outside powershell using formula from
+ # http://mathworld.wolfram.com/StandardDeviation.html
+ [Math]::abs($actual.StandardDeviation - 29.011491975882) | Should -BeLessThan .0000000000001
+ }
+
It "Should be able to count using the Property switch" {
$expected = $(Get-ChildItem $TestDrive).Length
$actual = $(Get-ChildItem $TestDrive | Measure-Object -Property Length).Count