Skip to content

Commit 7d85d57

Browse files
authored
Merge pull request #13 from HubertRonald/feature/phase-5-statistics-examples
Add summary statistics helpers and simulation examples
2 parents 7b02149 + a2b7ca4 commit 7d85d57

11 files changed

Lines changed: 360 additions & 14 deletions

CHANGELOG.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,26 @@ This project follows a lightweight changelog format inspired by [Keep a Changelo
88

99
## [Unreleased]
1010

11+
### Added
12+
13+
* Added `mode(array)`.
14+
* Added `range(array)`.
15+
* Added `iqr(array)`.
16+
* Added `percentile(array, p)`.
17+
* Added `summary(array)`.
18+
* Added `examples/weighted_loot_drop.lua`.
19+
* Added `examples/monte_carlo_pi.lua`.
20+
* Added `examples/poisson_arrivals.lua`.
21+
* Added `examples/binomial_coin_flips.lua`.
22+
* Added `examples/bootstrap_mean.lua`.
23+
1124
### Planned
1225

13-
* Continue improving documentation and examples.
14-
* Add manual GitHub Actions CI.
15-
* Add LuaRocks validation and publishing workflow improvements.
16-
* Consider additional statistics helpers and distribution examples.
26+
* Improve GitHub Actions CI with optional automatic checks for pull requests.
27+
* Improve LuaRocks validation and publishing workflows.
28+
* Add more distribution examples and simulation-oriented examples.
29+
* Explore a lightweight cross-reference with LuaHMF as a related pure-Lua math helper project.
30+
* Evaluate future combinatorics helpers such as `factorial`, `combinations`, and `permutations`.
1731

1832
---
1933

README.md

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ The project started around 2014 and was later published under the MIT License. I
4444
* Lua 5.1+ friendly
4545
* Single-file friendly
4646
* Basic descriptive statistics
47+
* Summary statistics helpers
4748
* Sampling utilities
4849
* Discrete and continuous pseudo-random variables
4950
* Compatible with the existing public LuaSF API
@@ -107,6 +108,7 @@ print(stats.mean(values)) -- 3
107108
print(stats.stddev(values)) -- sample standard deviation
108109
print(stats.median(values)) -- 3
109110
print(stats.variance(values)) -- sample variance
111+
print(stats.summary(values).count) -- 5
110112
```
111113

112114
Legacy names are still available:
@@ -138,13 +140,18 @@ print(stats.stvF(values)) -- sample standard deviation
138140
139141
### Additional descriptive statistics
140142

141-
| Function | Description |
142-
| -------------------- | ----------------------------------- |
143-
| `variance(array)` | Sample variance using `n - 1` |
144-
| `median(array)` | Median value |
145-
| `min(array)` | Minimum value |
146-
| `max(array)` | Maximum value |
147-
| `quantile(array, q)` | Quantile using linear interpolation |
143+
| Function | Description |
144+
| ---------------------- | ---------------------------------------------------------------------- |
145+
| `variance(array)` | Sample variance using `n - 1` |
146+
| `median(array)` | Median value |
147+
| `min(array)` | Minimum value |
148+
| `max(array)` | Maximum value |
149+
| `quantile(array, q)` | Quantile using linear interpolation |
150+
| `mode(array)` | Most frequent value |
151+
| `range(array)` | Difference between maximum and minimum |
152+
| `iqr(array)` | Interquartile range |
153+
| `percentile(array, p)` | Percentile where `p` is between `0` and `100` |
154+
| `summary(array)` | Summary table with count, min, max, mean, median, variance, and stddev |
148155

149156
### Sampling utilities
150157

@@ -202,6 +209,23 @@ for i = 1, #frequencies.counts do
202209
end
203210
```
204211

212+
### Summary statistics
213+
214+
```lua
215+
local stats = require("luasf")
216+
217+
local values = {10, 12, 14, 15, 18, 20}
218+
local result = stats.summary(values)
219+
220+
print("Count:", result.count)
221+
print("Min:", result.min)
222+
print("Max:", result.max)
223+
print("Mean:", result.mean)
224+
print("Median:", result.median)
225+
print("Variance:", result.variance)
226+
print("Stddev:", result.stddev)
227+
```
228+
205229
### Normal distribution quality control sample
206230

207231
```lua
@@ -277,6 +301,11 @@ LuaSF/
277301
dice_simulation.lua
278302
normal_quality_control.lua
279303
gamma_distribution.lua
304+
weighted_loot_drop.lua
305+
monte_carlo_pi.lua
306+
poisson_arrivals.lua
307+
binomial_coin_flips.lua
308+
bootstrap_mean.lua
280309
docs/
281310
api.md
282311
.github/
@@ -320,6 +349,11 @@ lua spec/test_sampling.lua
320349
lua examples/dice_simulation.lua
321350
lua examples/normal_quality_control.lua
322351
lua examples/gamma_distribution.lua
352+
lua examples/weighted_loot_drop.lua
353+
lua examples/monte_carlo_pi.lua
354+
lua examples/poisson_arrivals.lua
355+
lua examples/binomial_coin_flips.lua
356+
lua examples/bootstrap_mean.lua
323357
```
324358

325359
---
@@ -342,11 +376,12 @@ lua examples/gamma_distribution.lua
342376

343377
### Planned
344378

345-
* Manual GitHub Actions CI
346-
* LuaRocks package validation workflow
379+
* Improve GitHub Actions CI with optional automatic checks for pull requests
380+
* Improve LuaRocks validation and publishing workflows
347381
* More examples
348382
* More statistical helpers
349-
* Optional documentation generation
383+
* Lightweight cross-reference with LuaHMF
384+
* Future combinatorics helpers such as `factorial`, `combinations`, and `permutations`
350385

351386
---
352387

docs/api.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,101 @@ print(stats.quantile({1, 2, 3, 4, 5}, 0.5)) -- 3
195195

196196
---
197197

198+
### `mode(array)`
199+
200+
Returns the most frequent value in an array.
201+
202+
If multiple values have the same frequency, LuaSF returns the first mode found after sorting the values.
203+
204+
```lua
205+
local stats = require("luasf")
206+
207+
print(stats.mode({1, 2, 2, 3})) -- 2
208+
```
209+
210+
---
211+
212+
### `range(array)`
213+
214+
Returns the difference between the maximum and minimum values.
215+
216+
```lua
217+
local stats = require("luasf")
218+
219+
print(stats.range({3, 1, 10, 2})) -- 9
220+
```
221+
222+
---
223+
224+
### `iqr(array)`
225+
226+
Returns the interquartile range.
227+
228+
It is calculated as:
229+
230+
```text
231+
quantile(array, 0.75) - quantile(array, 0.25)
232+
```
233+
234+
Example:
235+
236+
```lua
237+
local stats = require("luasf")
238+
239+
print(stats.iqr({1, 2, 3, 4, 5})) -- 2
240+
```
241+
242+
---
243+
244+
### `percentile(array, p)`
245+
246+
Returns the `p` percentile.
247+
248+
`p` must be between `0` and `100`.
249+
250+
```lua
251+
local stats = require("luasf")
252+
253+
print(stats.percentile({1, 2, 3, 4, 5}, 50)) -- 3
254+
```
255+
256+
---
257+
258+
### `summary(array)`
259+
260+
Returns a summary table with common descriptive statistics.
261+
262+
The returned table includes:
263+
264+
```lua
265+
{
266+
count = number,
267+
min = number,
268+
max = number,
269+
mean = number,
270+
median = number,
271+
variance = number or nil,
272+
stddev = number or nil
273+
}
274+
```
275+
276+
Example:
277+
278+
```lua
279+
local stats = require("luasf")
280+
281+
local result = stats.summary({1, 2, 3, 4, 5})
282+
283+
print(result.count) -- 5
284+
print(result.min) -- 1
285+
print(result.max) -- 5
286+
print(result.mean) -- 3
287+
print(result.median) -- 3
288+
print(result.variance) -- 2.5
289+
```
290+
291+
---
292+
198293
## Sampling utilities
199294

200295
### `choice(array)`
@@ -673,6 +768,11 @@ stats.median
673768
stats.min
674769
stats.max
675770
stats.quantile
771+
stats.mode
772+
stats.range
773+
stats.iqr
774+
stats.percentile
775+
stats.summary
676776
stats.choice
677777
stats.shuffle
678778
stats.sample

examples/binomial_coin_flips.lua

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
local stats = require("luasf")
2+
3+
local flips = 10
4+
local probability_heads = 0.5
5+
local experiments = 20
6+
7+
for i = 1, experiments do
8+
local heads = stats.binomial(flips, probability_heads)
9+
print("Experiment:", i, "Heads:", heads)
10+
end

examples/bootstrap_mean.lua

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
local stats = require("luasf")
2+
3+
local values = {10, 12, 14, 15, 18, 20}
4+
local bootstrap_means = {}
5+
local bootstrap_runs = 1000
6+
7+
for run = 1, bootstrap_runs do
8+
local sample = {}
9+
10+
for i = 1, #values do
11+
sample[i] = stats.choice(values)
12+
end
13+
14+
bootstrap_means[run] = stats.mean(sample)
15+
end
16+
17+
local result = stats.summary(bootstrap_means)
18+
19+
print("Bootstrap mean summary")
20+
print("Count:", result.count)
21+
print("Min:", result.min)
22+
print("Max:", result.max)
23+
print("Mean:", result.mean)
24+
print("Median:", result.median)
25+
print("Stddev:", result.stddev)

examples/monte_carlo_pi.lua

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
local stats = require("luasf")
2+
3+
local trials = 100000
4+
local inside = 0
5+
6+
for _ = 1, trials do
7+
local x = stats.uniform(-1, 1)
8+
local y = stats.uniform(-1, 1)
9+
10+
if x * x + y * y <= 1 then
11+
inside = inside + 1
12+
end
13+
end
14+
15+
local pi_estimate = 4 * inside / trials
16+
17+
print("Pi estimate:", pi_estimate)

examples/poisson_arrivals.lua

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
local stats = require("luasf")
2+
3+
local lambda = 3
4+
local periods = 20
5+
6+
for period = 1, periods do
7+
local arrivals = stats.poisson(lambda)
8+
print("Period:", period, "Arrivals:", arrivals)
9+
end

examples/weighted_loot_drop.lua

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
local stats = require("luasf")
2+
3+
local items = {"common", "rare", "epic", "legendary"}
4+
local weights = {80, 15, 4, 1}
5+
6+
local drops = {}
7+
8+
for i = 1, 1000 do
9+
drops[i] = stats.weighted_choice(items, weights)
10+
end
11+
12+
local frequencies = stats.frequency(drops)
13+
14+
for i = 1, #frequencies.values do
15+
print(frequencies.values[i], frequencies.counts[i])
16+
end

luasf-0.4.0-1.rockspec

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package = "luasf"
2+
version = "0.4.0-1"
3+
4+
source = {
5+
url = "git://github.com/HubertRonald/LuaSF.git",
6+
tag = "v0.4.0"
7+
}
8+
9+
description = {
10+
summary = "Lua Statistics Functions",
11+
detailed = [[
12+
LuaSF is a lightweight, pure-Lua library for descriptive statistics,
13+
summary statistics, sampling utilities, simulation examples, and random
14+
variable generation.
15+
]],
16+
homepage = "https://github.com/HubertRonald/LuaSF",
17+
license = "MIT",
18+
maintainer = "Hubert Ronald"
19+
}
20+
21+
dependencies = {
22+
"lua >= 5.1"
23+
}
24+
25+
build = {
26+
type = "builtin",
27+
modules = {
28+
luasf = "src/luasf.lua",
29+
LuaSF = "LuaSF.lua",
30+
LuaStat = "LuaStat.lua"
31+
}
32+
}

0 commit comments

Comments
 (0)