Skip to content

Commit 8d0729b

Browse files
Add Welford's Variance (TheAlgorithms#358)
1 parent d381c4e commit 8d0729b

File tree

3 files changed

+236
-0
lines changed

3 files changed

+236
-0
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
using Algorithms.Other;
2+
using NUnit.Framework;
3+
4+
namespace Algorithms.Tests.Other
5+
{
6+
public class WelfordsVarianceTest
7+
{
8+
[Test]
9+
public void WelfordVariance_Example1()
10+
{
11+
var welfordsVariance = new WelfordsVariance();
12+
welfordsVariance.AddValue(4);
13+
welfordsVariance.AddValue(7);
14+
welfordsVariance.AddValue(13);
15+
welfordsVariance.AddValue(16);
16+
17+
Assert.AreEqual(4, welfordsVariance.Count);
18+
Assert.AreEqual(10, welfordsVariance.Mean, 0.0000001);
19+
Assert.AreEqual(22.5, welfordsVariance.Variance, 0.0000001);
20+
Assert.AreEqual(30, welfordsVariance.SampleVariance, 0.0000001);
21+
}
22+
23+
[Test]
24+
public void WelfordVariance_Example2()
25+
{
26+
var stats = new WelfordsVariance();
27+
stats.AddValue(100000004);
28+
stats.AddValue(100000007);
29+
stats.AddValue(100000013);
30+
stats.AddValue(100000016);
31+
Assert.AreEqual(4, stats.Count);
32+
Assert.AreEqual(100000010, stats.Mean, 0.0000001);
33+
Assert.AreEqual(22.5, stats.Variance, 0.0000001);
34+
Assert.AreEqual(30, stats.SampleVariance, 0.0000001);
35+
}
36+
37+
[Test]
38+
public void WelfordVariance_Example3()
39+
{
40+
var stats = new WelfordsVariance();
41+
stats.AddValue(1000000004);
42+
stats.AddValue(1000000007);
43+
stats.AddValue(1000000013);
44+
stats.AddValue(1000000016);
45+
Assert.AreEqual(4, stats.Count);
46+
Assert.AreEqual(1000000010, stats.Mean, 0.0000001);
47+
Assert.AreEqual(22.5, stats.Variance, 0.0000001);
48+
Assert.AreEqual(30, stats.SampleVariance, 0.0000001);
49+
}
50+
51+
[Test]
52+
public void WelfordVariance_Example4()
53+
{
54+
var stats = new WelfordsVariance();
55+
stats.AddValue(6);
56+
stats.AddValue(2);
57+
stats.AddValue(3);
58+
stats.AddValue(1);
59+
Assert.AreEqual(4, stats.Count);
60+
Assert.AreEqual(3, stats.Mean, 0.0000001);
61+
Assert.AreEqual(3.5, stats.Variance, 0.0000001);
62+
Assert.AreEqual(4.6666667, stats.SampleVariance, 0.0000001);
63+
}
64+
65+
[Test]
66+
public void WelfordVariance_Example5()
67+
{
68+
var stats = new WelfordsVariance(new double[] { 2, 2, 5, 7 });
69+
Assert.AreEqual(4, stats.Count);
70+
Assert.AreEqual(4, stats.Mean, 0.0000001);
71+
Assert.AreEqual(4.5, stats.Variance, 0.0000001);
72+
Assert.AreEqual(6, stats.SampleVariance, 0.0000001);
73+
}
74+
75+
[Test]
76+
public void WelfordVariance_Example6()
77+
{
78+
var stats = new WelfordsVariance();
79+
stats.AddRange(new double[] { 2, 4, 4, 4, 5, 5, 7, 9 });
80+
Assert.AreEqual(8, stats.Count);
81+
Assert.AreEqual(5, stats.Mean, 0.0000001);
82+
Assert.AreEqual(4, stats.Variance, 0.0000001);
83+
Assert.AreEqual(4.5714286, stats.SampleVariance, 0.0000001);
84+
}
85+
86+
[Test]
87+
public void WelfordVariance_Example7()
88+
{
89+
var stats = new WelfordsVariance();
90+
stats.AddRange(new double[] { 9, 2, 5, 4, 12, 7, 8, 11, 9, 3, 7, 4, 12, 5, 4, 10, 9, 6, 9, 4 });
91+
Assert.AreEqual(20, stats.Count);
92+
Assert.AreEqual(7, stats.Mean, 0.0000001);
93+
Assert.AreEqual(8.9, stats.Variance, 0.0000001);
94+
Assert.AreEqual(9.3684211, stats.SampleVariance, 0.0000001);
95+
}
96+
97+
[Test]
98+
public void WelfordVariance_Example8()
99+
{
100+
var stats = new WelfordsVariance();
101+
stats.AddRange(new [] { 51.3, 55.6, 49.9, 52.0 });
102+
Assert.AreEqual(4, stats.Count);
103+
Assert.AreEqual(52.2, stats.Mean, 0.0000001);
104+
Assert.AreEqual(4.4250000, stats.Variance, 0.0000001);
105+
Assert.AreEqual(5.9000000, stats.SampleVariance, 0.0000001);
106+
}
107+
108+
[Test]
109+
public void WelfordVariance_Example9()
110+
{
111+
var stats = new WelfordsVariance();
112+
stats.AddRange(new double[] { -5, -3, -1, 1, 3 });
113+
Assert.AreEqual(5, stats.Count);
114+
Assert.AreEqual(-1, stats.Mean, 0.0000001);
115+
Assert.AreEqual(8, stats.Variance, 0.0000001);
116+
Assert.AreEqual(10, stats.SampleVariance, 0.0000001);
117+
}
118+
119+
[Test]
120+
public void WelfordVariance_Example10()
121+
{
122+
var stats = new WelfordsVariance();
123+
stats.AddRange(new double[] { -1, 0, 1 });
124+
Assert.AreEqual(3, stats.Count);
125+
Assert.AreEqual(0, stats.Mean, 0.0000001);
126+
Assert.AreEqual(0.6666667, stats.Variance, 0.0000001);
127+
Assert.AreEqual(1, stats.SampleVariance, 0.0000001);
128+
}
129+
130+
[Test]
131+
public void WelfordVariance_NoValue()
132+
{
133+
var stats = new WelfordsVariance();
134+
Assert.AreEqual(0, stats.Count);
135+
Assert.AreEqual(double.NaN, stats.Mean);
136+
Assert.AreEqual(double.NaN, stats.Variance);
137+
Assert.AreEqual(double.NaN, stats.SampleVariance);
138+
}
139+
140+
[Test]
141+
public void WelfordVariance_OneValue()
142+
{
143+
var stats = new WelfordsVariance();
144+
stats.AddValue(1);
145+
Assert.AreEqual(1, stats.Count);
146+
Assert.AreEqual(double.NaN, stats.Mean);
147+
Assert.AreEqual(double.NaN, stats.Variance);
148+
Assert.AreEqual(double.NaN, stats.SampleVariance);
149+
}
150+
151+
[Test]
152+
public void WelfordVariance_TwoValues()
153+
{
154+
var stats = new WelfordsVariance();
155+
stats.AddValue(1);
156+
stats.AddValue(2);
157+
Assert.AreEqual(2, stats.Count);
158+
Assert.AreEqual(1.5, stats.Mean, 0.0000001);
159+
Assert.AreEqual(0.25, stats.Variance, 0.0000001);
160+
Assert.AreEqual(0.5, stats.SampleVariance, 0.0000001);
161+
}
162+
}
163+
}

Algorithms/Other/WelfordsVariance.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Algorithms.Other
8+
{
9+
/// <summary>Implementation of Welford's variance algorithm.
10+
/// </summary>
11+
public class WelfordsVariance
12+
{
13+
/// <summary>
14+
/// Mean accumulates the mean of the entire dataset,
15+
/// m2 aggregates the squared distance from the mean,
16+
/// count aggregates the number of samples seen so far.
17+
/// </summary>
18+
private int count;
19+
20+
public double Count => count;
21+
22+
private double mean;
23+
24+
public double Mean => count > 1 ? mean : double.NaN;
25+
26+
private double m2;
27+
28+
public double Variance => count > 1 ? m2 / count : double.NaN;
29+
30+
public double SampleVariance => count > 1 ? m2 / (count - 1) : double.NaN;
31+
32+
public WelfordsVariance()
33+
{
34+
count = 0;
35+
mean = 0;
36+
}
37+
38+
public WelfordsVariance(double[] values)
39+
{
40+
count = 0;
41+
mean = 0;
42+
AddRange(values);
43+
}
44+
45+
public void AddValue(double newValue)
46+
{
47+
count++;
48+
AddValueToDataset(newValue);
49+
}
50+
51+
public void AddRange(double[] values)
52+
{
53+
var length = values.Length;
54+
for (var i = 1; i <= length; i++)
55+
{
56+
count++;
57+
AddValueToDataset(values[i - 1]);
58+
}
59+
}
60+
61+
private void AddValueToDataset(double newValue)
62+
{
63+
var delta1 = newValue - mean;
64+
var newMean = mean + delta1 / count;
65+
66+
var delta2 = newValue - newMean;
67+
m2 += delta1 * delta2;
68+
69+
mean = newMean;
70+
}
71+
}
72+
}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ find more than one implementation for the same objective but using different alg
184184
* [Pareto Optimization](./Algorithms/Other/ParetoOptimization.cs)
185185
* [Gauss Optimization](./Algorithms/Other/GaussOptimization.cs)
186186
* [Decisions Convolutions](./Algorithms/Other/DecisionsConvolutions.cs)
187+
* [Welford's Variance](./Algorithms/Other/WelfordsVariance.cs)
187188
* [Problems](./Algorithms/Problems)
188189
* [Stable Marriage](./Algorithms/Problems/StableMarriage)
189190
* [Gale-Shapley](./Algorithms/Problems/StableMarriage/GaleShapley.cs)

0 commit comments

Comments
 (0)