Skip to content

Commit 3778434

Browse files
committed
Number: averages
Signed-off-by: Nathaniel Clark <Nathaniel.Clark@misrule.us>
1 parent 48fb0f5 commit 3778434

File tree

3 files changed

+80
-12
lines changed

3 files changed

+80
-12
lines changed

src/fixtures/help.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,10 @@ <h1>RPN-rs is a graphical reverse polish notation (RPN) calculator</h1>
117117
<tr><td>expand</td><td>1</td><td colspan=2>Push all the values from tuple onto the stack</td></tr>
118118

119119
<tr><th colspan=4>Statistical Operations (meaningful on tuples and matrices)</th></tr>
120-
<tr><td>avg | mean</td><td>1</td><td>Find the average value</td><td><i>avg(x)</i></td></tr>
120+
<tr><td>avg | mean | amean | AM</td><td>1</td><td>Find the average value (<a href="https://en.wikipedia.org/wiki/Arithmetic_mean">Arithmetic mean</a>)</td><td><i>avg(x)</i></td></tr>
121+
<tr><td>gmean | GM</td><td>1</td><td>Find the <a href="https://en.wikipedia.org/wiki/Geometic_mean">Geometric mean</a></td><td><i>GM(x)</i></td></tr>
122+
<tr><td>hmean | HM </td><td>1</td><td>Find the <a href="https://en.wikipedia.org/wiki/Harmonic_mean">Harmonic mean</a></td><td><i>HM(x)</i></td></tr>
123+
<tr><td>gmdn</td><td>1</td><td>Conduct one iteration of the <a href="https://xkcd.com/2435/">Geothmetic meandian</a> function</td><td><i>GMDN(x)</i></td></tr>
121124
<tr><td>median</td><td>1</td><td>Find the median value</td><td><i>median(x)</i></td></tr>
122125
<tr><td>(sd)ev | sigma</td><td>1</td><td>Find the <a href="https://en.wikipedia.org/wiki/Standard_deviation">standard deviation</a></td><td><i>sigma(x)</i></td></tr>
123126
<tr><td>sum</td><td>1</td><td colspan=2>Add all values in element togther and return sum</td></tr>

src/main.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,11 @@ fn main() {
234234
"unpush" => stacks[0].try_unary_v(|a| a.try_unpush()),
235235
"pop" => stacks[0].try_unary_v(|a| a.try_pull()),
236236
// Tuple / Stats Operations
237-
"avg" | "mean" => stacks[0].unary(|a| a.mean()),
238-
"median" => stacks[0].unary(|a| a.median()),
237+
"avg" | "mean" | "amean" | "AM" => stacks[0].unary(|a| a.mean().into()),
238+
"gmean" | "GM" => stacks[0].unary(|a| a.geometric_mean().into()),
239+
"hmean" | "HM" => stacks[0].unary(|a| a.harmonic_mean().into()),
240+
"median" => stacks[0].unary(|a| a.median().into()),
241+
"gmdn" => stacks[0].unary(|a| a.gmdn()),
239242
"sort" => stacks[0].unary(|a| a.sort()),
240243
"sum" => stacks[0].unary(|a| a.sum()),
241244
"prod" | "product" => stacks[0].unary(|a| a.product()),

src/numbers.rs

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,34 +1369,96 @@ impl Value {
13691369
}
13701370

13711371
// Stats Functions
1372-
pub(crate) fn mean(self) -> Self {
1372+
pub(crate) fn mean(self) -> Scalar {
13731373
match self {
1374-
Value::Scalar(s) => Value::Scalar(s),
1374+
Value::Scalar(s) => s,
13751375
Value::Tuple(t) => {
13761376
let len = t.len().into();
13771377
let v: Scalar = t.into_iter().sum();
1378-
Value::Scalar(v / len)
1378+
v / len
13791379
}
13801380
Value::Matrix(m) => {
13811381
let len: Scalar = Scalar::from(m.rows()) * m.cols().into();
13821382
let sum: Scalar = m.into_iter().sum();
1383-
Value::Scalar(sum / len)
1383+
sum / len
13841384
}
13851385
}
13861386
}
13871387

1388-
pub(crate) fn median(self) -> Self {
1388+
/// Geometric Mean
1389+
/// n-th root of the product of all the values
1390+
pub(crate) fn geometric_mean(self) -> Scalar {
13891391
match self {
1390-
Value::Scalar(s) => Value::Scalar(s),
1391-
Value::Tuple(t) => Value::Scalar(median(t.into_iter().collect())),
1392+
Value::Scalar(s) => s,
1393+
Value::Tuple(t) => {
1394+
let len = t.len().into();
1395+
let v: Scalar = t.into_iter().map(|x| x.ln()).sum();
1396+
(v / len).exp()
1397+
}
1398+
Value::Matrix(m) => {
1399+
let len: Scalar = Scalar::from(m.rows()) * m.cols().into();
1400+
let sum: Scalar = m.into_iter().map(|x| x.ln()).sum();
1401+
(sum / len).exp()
1402+
}
1403+
}
1404+
}
1405+
1406+
/// Harmonic Mean
1407+
pub(crate) fn harmonic_mean(self) -> Scalar {
1408+
match self {
1409+
Value::Scalar(s) => s,
1410+
Value::Tuple(t) => {
1411+
let len: Scalar = t.len().into();
1412+
let v: Scalar = t.into_iter().map(|x| x.inv()).sum();
1413+
len / v
1414+
}
1415+
Value::Matrix(m) => {
1416+
let len: Scalar = Scalar::from(m.rows()) * m.cols().into();
1417+
let sum: Scalar = m.into_iter().map(|x| x.inv()).sum();
1418+
len / sum
1419+
}
1420+
}
1421+
}
1422+
1423+
pub(crate) fn median(self) -> Scalar {
1424+
match self {
1425+
Value::Scalar(s) => s,
1426+
Value::Tuple(t) => median(t.into_iter().collect()),
13921427
Value::Matrix(m) => {
13931428
let list = m.into_iter().collect::<Vec<_>>();
1394-
let val = median(list);
1395-
Value::Scalar(val)
1429+
median(list)
13961430
}
13971431
}
13981432
}
13991433

1434+
/// Geothmetic Meandian (XKCD #2435)
1435+
///
1436+
/// F(x0, x1, ... xN) -> (Arithmatic Mean, Geometric Mean, Median)
1437+
/// gmdn(...) -> F(F(F(F(...))))) until it converges
1438+
///
1439+
/// This funcition computes a single iteration
1440+
pub(crate) fn gmdn(self) -> Self {
1441+
match self {
1442+
Value::Scalar(s) => Value::Scalar(s),
1443+
Value::Tuple(_) => Value::Tuple(
1444+
[
1445+
self.clone().mean(),
1446+
self.clone().geometric_mean(),
1447+
self.median(),
1448+
]
1449+
.into(),
1450+
),
1451+
Value::Matrix(_) => Value::Tuple(
1452+
[
1453+
self.clone().mean(),
1454+
self.clone().geometric_mean(),
1455+
self.median(),
1456+
]
1457+
.into(),
1458+
),
1459+
}
1460+
}
1461+
14001462
pub(crate) fn sort(self) -> Self {
14011463
match self {
14021464
Value::Scalar(s) => Value::Scalar(s),

0 commit comments

Comments
 (0)