statistics.rb 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d48404847c10c54ef090f41c5dc470e3ca6409ab07918828b6181e54836bc22b
4
- data.tar.gz: 42f80e662e6834081f993c168e96e75451be5a83106bbe8ebe0a2260a47f84c1
3
+ metadata.gz: ca6c39ebb723322893b28fc4cc9310e4004120ae20faa3be0aa502df6bae0d1b
4
+ data.tar.gz: aad65236bd96a97805c7d0cce74e2c922d34e07ee6c95e3ebc3848a6c9eb92b0
5
5
  SHA512:
6
- metadata.gz: eca8442077f05a25a936475b44c5d6cc75b6a1f009ec1ec15e4803d4b43727c554b4036a1c3ae44e674acacb4c5c0d4605aca24783998cdd13962a86bca887f5
7
- data.tar.gz: 14e9b21b4db384c2bd01bed96d7889c88949fb45a1e2c352df721d9af974ff1daf91e020bcac211b6f94639aafd3d63a16ac3d9a0d27f38f65ce87ade157cefd
6
+ metadata.gz: 16f2c79aa61f53f1b7d6ef30d61e54ad2dde96e541056651c165e8955270c8e77290fc9f50e5a7bb752dc97ed3c87da715e0ec55950a352b45d9cb0ed5481780
7
+ data.tar.gz: 4f0129b9ed5fcd74ff7a3940f3e4b578b7d586221b2a514354425a0c6fef2fcf8dfd5b5d8c6992deef0c73b4872805c356e46b3c387cea5e0ca995577dfaadb2
data/CHANGELOG.md ADDED
@@ -0,0 +1,106 @@
1
+ # CHANGELOG
2
+
3
+ ## [0.6.0] - 20260504
4
+ ## Percentile, StandardDeviation, IQR
5
+
6
+ 1. + lib/Statistics/Percentile.rb
7
+ 2. + lib/Statistics/StandardDeviation.rb
8
+ 3. + lib/Statistics/IQR.rb
9
+ 4. + test/Statistics/Percentile\_test.rb
10
+ 5. + test/Statistics/StandardDeviation\_test.rb
11
+ 6. + test/Statistics/IQR\_test.rb
12
+ 7. - Gemfile: As there are no dependencies it isn't doing anything!
13
+ 8. ~ statistics.rb.gemspec: dependencies=() also therefore not used.
14
+ 9. /CHANGELOG/CHANGELOG.md/
15
+
16
+
17
+ ## [0.5.0] - 20260503
18
+ ## Bin.bin\_for\_value, Bin split, gem scaffolding
19
+
20
+ 1. + Bin.bin\_for\_value (wraps index\_for\_value, now private)
21
+ 2. ~ Histogram#allocate\_values: Use Bin.bin\_for\_value
22
+ 3. ~ Bin split out to lib/Statistics/Histogram/Bin.rb
23
+ 4. + lib/statistics.rb
24
+ 5. + lib/Statistics/VERSION.rb
25
+ 6. + statistics.rb.gemspec
26
+ 7. + Gemfile
27
+ 8. + Rakefile
28
+ 9. + README.md
29
+ 10. + LICENSE
30
+ 11. + test/Statistics/Histogram\_test.rb
31
+ 12. + test/Statistics/Histogram/Bin\_test.rb
32
+
33
+
34
+ ## [0.4.0] - 20260502
35
+ ## Bin instances and consolidated class methods
36
+
37
+ 1. + Bin instances (count-tracking via increment, attr\_reader :interval)
38
+ 2. ~ Bin.width: handles bin\_width, bin\_count, zero-range, and method selection
39
+ 3. + Bin.boundaries (from 0.3.0)
40
+ 4. + Bin.index\_for\_value (was Histogram#index\_for\_value)
41
+ 5. + Bin.data\_range
42
+ 6. ~ class << self for class methods with private boundary
43
+ 7. ~ attr\_reader :count replaces hand-written method
44
+
45
+
46
+ ## [0.3.0] - 20260417
47
+ ## Hash-based bins, Bin as class-methods-only
48
+
49
+ 1. + Bin.boundaries class method
50
+ 2. ~ initialize: delegates to Bin.width and Bin.boundaries
51
+ 3. ~ Bin: hash-based bins (no Bin instances), Bin is class-methods-only
52
+ 4. - index\_for\_value (inlined back into allocate\_values)
53
+ 5. - determine\_bin\_width (replaced by Bin.width delegation)
54
+ 6. - zero-range guard
55
+
56
+
57
+ ## [0.2.0] - 20260417
58
+ ## Count-tracking, interval, zero-range guard
59
+
60
+ 1. ~ Bin: count-tracking via increment instead of storing values in array
61
+ 2. ~ Bin: attr\_reader :interval instead of :range
62
+ 3. + Bin#empty? checks @count == 0 instead of @values.empty?
63
+ 4. + determine\_bin\_width: zero-range guard
64
+ 5. + index\_for\_value extracted from allocate\_values
65
+
66
+
67
+ ## [0.1.1] - 20260417
68
+ ## Extract determine\_bin\_width
69
+
70
+ 1. ~ initialize: extracted determine\_bin\_width from one-liner
71
+ 2. /compute\_boundaries/calculate\_boundaries/
72
+
73
+
74
+ ## [0.1.0] - 20260417
75
+ ## Bin class
76
+
77
+ 1. + Statistics::Histogram::Bin
78
+ 2. ~ allocate\_values: creates Bin instances instead of hash entries
79
+
80
+
81
+ ## [0.0.0] - 20260417
82
+ ## Statistics::Histogram
83
+
84
+ 1. + lib/Statistics/Histogram.rb
85
+ 2. - lib/BinWidth.rb
86
+
87
+
88
+ ## 20260416
89
+ ## BinWidth 0.1.0 to 0.2.0: Reintroduce all named strategies
90
+
91
+ 1. /0.1.0/0.2.0/
92
+ 2. Reintroduce all named strategies.
93
+
94
+
95
+ ## 20260416
96
+ ## BinWidth 0.0.0 to 0.1.0: Tuneable root generalisation
97
+
98
+ 1. /0.0.0/0.1.0/
99
+ 2. + tuneable\_root as the general form
100
+ 3. ~ square\_root rewritten as range * n^(-1/2) form
101
+
102
+
103
+ ## 20260416
104
+ ## BinWidth 0.0.0
105
+
106
+ 1. + lib/BinWidth.rb
data/README.md CHANGED
@@ -68,11 +68,37 @@ h.bins.first.width # => 2.57
68
68
  h.bins.first.empty? # => false
69
69
  ```
70
70
 
71
+ ### Percentile
72
+
73
+ These methods employ linear interpolation. See Hyndman and Fan method 7.
74
+
75
+ ```ruby
76
+ require 'statistics.rb'
77
+
78
+ values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
79
+
80
+ Statistics::Percentile.of(values, 75) # => 7.75
81
+ Statistics::Percentile.q25(values) # => 3.25
82
+ Statistics::Percentile.q75(values) # => 7.75
83
+ ```
84
+
85
+ ### Standard Deviation
86
+
87
+ ```ruby
88
+ Statistics::StandardDeviation.of(values) # => population (default)
89
+ Statistics::StandardDeviation.of(values, sample: true) # => sample (Bessel's correction)
90
+ ```
91
+
92
+ ### Interquartile Range
93
+
94
+ ```ruby
95
+ Statistics::IQR.of(values) # => 4.5
96
+ ```
97
+
71
98
  ## Roadmap
72
99
 
73
100
  - Optional per-bin value storage
74
101
  - Additional bin width methods (Freedman-Diaconis, Scott, Sturges, cube root, tuneable root)
75
- - Composable statistical primitives (Percentile, StandardDeviation, IQR)
76
102
  - Aligned/neat bin boundaries
77
103
 
78
104
  ## License
@@ -0,0 +1,14 @@
1
+ # Statistics/IQR.rb
2
+ # Statistics::IQR
3
+
4
+ require_relative './Percentile'
5
+
6
+ module Statistics
7
+ module IQR
8
+ module_function
9
+
10
+ def of(values)
11
+ Percentile.q75(values) - Percentile.q25(values)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,26 @@
1
+ # Statistics/Percentile.rb
2
+ # Statistics::Percentile
3
+
4
+ # These methods employ linear interpolation. See Hyndman and Fan method 7.
5
+
6
+ module Statistics
7
+ module Percentile
8
+ module_function
9
+
10
+ def of(values, p)
11
+ sorted = values.map(&:to_f).sort
12
+ k = (p / 100.0) * (sorted.size - 1)
13
+ lower = sorted[k.floor]
14
+ upper = sorted[k.ceil]
15
+ lower + (upper - lower) * (k - k.floor)
16
+ end
17
+
18
+ def q25(values)
19
+ of(values, 25)
20
+ end
21
+
22
+ def q75(values)
23
+ of(values, 75)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ # Statistics/StandardDeviation.rb
2
+ # Statistics::StandardDeviation
3
+
4
+ module Statistics
5
+ module StandardDeviation
6
+ module_function
7
+
8
+ def of(values, sample: false)
9
+ floats = values.map(&:to_f)
10
+ mean = floats.sum / floats.size
11
+ denominator = sample ? floats.size - 1 : floats.size
12
+ variance = floats.map{|v| (v - mean) ** 2}.sum / denominator
13
+ Math.sqrt(variance)
14
+ end
15
+ end
16
+ end
@@ -2,5 +2,5 @@
2
2
  # Statistics::VERSION
3
3
 
4
4
  module Statistics
5
- VERSION = '0.5.0'
5
+ VERSION = '0.6.0'
6
6
  end
data/lib/statistics.rb CHANGED
@@ -1,4 +1,7 @@
1
1
  # statistics.rb
2
2
 
3
- require_relative './Statistics/VERSION'
4
3
  require_relative './Statistics/Histogram'
4
+ require_relative './Statistics/IQR'
5
+ require_relative './Statistics/Percentile'
6
+ require_relative './Statistics/StandardDeviation'
7
+ require_relative './Statistics/VERSION'
@@ -1,10 +1,6 @@
1
1
  require_relative './lib/Statistics/VERSION'
2
2
 
3
3
  class Gem::Specification
4
- def dependencies=(gems)
5
- gems.each{|gem| add_dependency(*gem)}
6
- end
7
-
8
4
  def development_dependencies=(gems)
9
5
  gems.each{|gem| add_development_dependency(*gem)}
10
6
  end
@@ -12,12 +8,10 @@ end
12
8
 
13
9
  Gem::Specification.new do |spec|
14
10
  spec.name = 'statistics.rb'
15
-
16
11
  spec.version = Statistics::VERSION
17
- spec.date = '2026-05-03'
18
12
 
19
13
  spec.summary = "A statistics library for Ruby."
20
- spec.description = "A composable statistics library for Ruby. Histogram with automatic bin width calculation and composable Bin class."
14
+ spec.description = "A composable statistics library for Ruby. Histogram, Percentile, StandardDeviation, IQR."
21
15
 
22
16
  spec.author = 'thoran'
23
17
  spec.email = 'code@thoran.com'
@@ -29,8 +23,7 @@ Gem::Specification.new do |spec|
29
23
  spec.files = [
30
24
  Dir['lib/**/*.rb'],
31
25
  Dir['test/**/*.rb'],
32
- 'CHANGELOG',
33
- 'Gemfile',
26
+ 'CHANGELOG.md',
34
27
  'LICENSE',
35
28
  'Rakefile',
36
29
  'README.md',
@@ -0,0 +1,30 @@
1
+ # test/Statistics/IQR_test.rb
2
+
3
+ require 'minitest/autorun'
4
+
5
+ require_relative '../../lib/Statistics/IQR'
6
+
7
+ describe Statistics::IQR do
8
+ describe '.of' do
9
+ it 'returns the difference between q75 and q25' do
10
+ values = [1, 2, 3, 4, 5]
11
+ expected = Statistics::Percentile.q75(values) - Statistics::Percentile.q25(values)
12
+ _(Statistics::IQR.of(values)).must_equal expected
13
+ end
14
+
15
+ it 'returns zero for identical values' do
16
+ _(Statistics::IQR.of([5, 5, 5, 5])).must_equal 0.0
17
+ end
18
+
19
+ it 'handles a two-value dataset' do
20
+ values = [10, 20]
21
+ _(Statistics::IQR.of(values)).must_be_close_to 5.0
22
+ end
23
+
24
+ it 'handles unsorted input' do
25
+ sorted = [1, 2, 3, 4, 5, 6, 7, 8]
26
+ shuffled = [5, 1, 8, 3, 7, 2, 6, 4]
27
+ _(Statistics::IQR.of(shuffled)).must_equal Statistics::IQR.of(sorted)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,52 @@
1
+ # test/Statistics/Percentile_test.rb
2
+
3
+ require 'minitest/autorun'
4
+
5
+ require_relative '../../lib/Statistics/Percentile'
6
+
7
+ describe Statistics::Percentile do
8
+ describe '.of' do
9
+ it 'returns the minimum for the 0th percentile' do
10
+ values = [1, 2, 3, 4, 5]
11
+ _(Statistics::Percentile.of(values, 0)).must_equal 1.0
12
+ end
13
+
14
+ it 'returns the maximum for the 100th percentile' do
15
+ values = [1, 2, 3, 4, 5]
16
+ _(Statistics::Percentile.of(values, 100)).must_equal 5.0
17
+ end
18
+
19
+ it 'returns the median for the 50th percentile' do
20
+ values = [1, 2, 3, 4, 5]
21
+ _(Statistics::Percentile.of(values, 50)).must_equal 3.0
22
+ end
23
+
24
+ it 'interpolates between values' do
25
+ values = [10, 20, 30, 40]
26
+ _(Statistics::Percentile.of(values, 25)).must_equal 17.5
27
+ end
28
+
29
+ it 'handles a single value' do
30
+ _(Statistics::Percentile.of([42], 50)).must_equal 42.0
31
+ end
32
+
33
+ it 'handles unsorted input' do
34
+ values = [5, 1, 3, 2, 4]
35
+ _(Statistics::Percentile.of(values, 50)).must_equal 3.0
36
+ end
37
+ end
38
+
39
+ describe '.q25' do
40
+ it 'returns the 25th percentile' do
41
+ values = [1, 2, 3, 4, 5]
42
+ _(Statistics::Percentile.q25(values)).must_equal Statistics::Percentile.of(values, 25)
43
+ end
44
+ end
45
+
46
+ describe '.q75' do
47
+ it 'returns the 75th percentile' do
48
+ values = [1, 2, 3, 4, 5]
49
+ _(Statistics::Percentile.q75(values)).must_equal Statistics::Percentile.of(values, 75)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,34 @@
1
+ # test/Statistics/StandardDeviation_test.rb
2
+
3
+ require 'minitest/autorun'
4
+
5
+ require_relative '../../lib/Statistics/StandardDeviation'
6
+
7
+ describe Statistics::StandardDeviation do
8
+ describe '.of' do
9
+ it 'returns zero for identical values' do
10
+ _(Statistics::StandardDeviation.of([5, 5, 5, 5])).must_equal 0.0
11
+ end
12
+
13
+ it 'calculates population standard deviation by default' do
14
+ values = [2, 4, 4, 4, 5, 5, 7, 9]
15
+ _(Statistics::StandardDeviation.of(values)).must_be_close_to 2.0
16
+ end
17
+
18
+ it 'calculates sample standard deviation when requested' do
19
+ values = [2, 4, 4, 4, 5, 5, 7, 9]
20
+ sample_sd = Statistics::StandardDeviation.of(values, sample: true)
21
+ population_sd = Statistics::StandardDeviation.of(values)
22
+ _(sample_sd).must_be :>, population_sd
23
+ end
24
+
25
+ it 'handles a single value' do
26
+ _(Statistics::StandardDeviation.of([42])).must_equal 0.0
27
+ end
28
+
29
+ it 'handles two values' do
30
+ values = [0, 10]
31
+ _(Statistics::StandardDeviation.of(values)).must_be_close_to 5.0
32
+ end
33
+ end
34
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statistics.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - thoran
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-05-03 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rake
@@ -37,25 +37,30 @@ dependencies:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0'
40
- description: A composable statistics library for Ruby. Histogram with automatic bin
41
- width calculation and composable Bin class.
40
+ description: A composable statistics library for Ruby. Histogram, Percentile, StandardDeviation,
41
+ IQR.
42
42
  email: code@thoran.com
43
43
  executables: []
44
44
  extensions: []
45
45
  extra_rdoc_files: []
46
46
  files:
47
- - CHANGELOG
48
- - Gemfile
47
+ - CHANGELOG.md
49
48
  - LICENSE
50
49
  - README.md
51
50
  - Rakefile
52
51
  - lib/Statistics/Histogram.rb
53
52
  - lib/Statistics/Histogram/Bin.rb
53
+ - lib/Statistics/IQR.rb
54
+ - lib/Statistics/Percentile.rb
55
+ - lib/Statistics/StandardDeviation.rb
54
56
  - lib/Statistics/VERSION.rb
55
57
  - lib/statistics.rb
56
58
  - statistics.rb.gemspec
57
59
  - test/Statistics/Histogram/Bin_test.rb
58
60
  - test/Statistics/Histogram_test.rb
61
+ - test/Statistics/IQR_test.rb
62
+ - test/Statistics/Percentile_test.rb
63
+ - test/Statistics/StandardDeviation_test.rb
59
64
  homepage: http://github.com/thoran/statistics
60
65
  licenses:
61
66
  - MIT
data/CHANGELOG DELETED
@@ -1,92 +0,0 @@
1
- # CHANGELOG
2
-
3
- ## [0.5.0] - 20260503
4
- ## Bin.bin_for_value, Bin split, gem scaffolding
5
-
6
- 1. + Bin.bin_for_value (wraps index_for_value, now private)
7
- 2. ~ Histogram#allocate_values: Use Bin.bin_for_value
8
- 3. ~ Bin split out to lib/Statistics/Histogram/Bin.rb
9
- 4. + lib/statistics.rb
10
- 5. + lib/Statistics/VERSION.rb
11
- 6. + statistics.rb.gemspec
12
- 7. + Gemfile
13
- 8. + Rakefile
14
- 9. + README.md
15
- 10. + LICENSE
16
- 11. + test/Statistics/Histogram_test.rb
17
- 12. + test/Statistics/Histogram/Bin_test.rb
18
-
19
-
20
- ## [0.4.0] - 20260502
21
- ## Bin instances and consolidated class methods
22
-
23
- 1. + Bin instances (count-tracking via increment, attr_reader :interval)
24
- 2. ~ Bin.width: handles bin_width, bin_count, zero-range, and method selection
25
- 3. + Bin.boundaries (from 0.3.0)
26
- 4. + Bin.index_for_value (was Histogram#index_for_value)
27
- 5. + Bin.data_range
28
- 6. ~ class << self for class methods with private boundary
29
- 7. ~ attr_reader :count replaces hand-written method
30
-
31
-
32
- ## [0.3.0] - 20260417
33
- ## Hash-based bins, Bin as class-methods-only
34
-
35
- 1. + Bin.boundaries class method
36
- 2. ~ initialize: delegates to Bin.width and Bin.boundaries
37
- 3. ~ Bin: hash-based bins (no Bin instances), Bin is class-methods-only
38
- 4. - index_for_value (inlined back into allocate_values)
39
- 5. - determine_bin_width (replaced by Bin.width delegation)
40
- 6. - zero-range guard
41
-
42
-
43
- ## [0.2.0] - 20260417
44
- ## Count-tracking, interval, zero-range guard
45
-
46
- 1. ~ Bin: count-tracking via increment instead of storing values in array
47
- 2. ~ Bin: attr_reader :interval instead of :range
48
- 3. + Bin#empty? checks @count == 0 instead of @values.empty?
49
- 4. + determine_bin_width: zero-range guard
50
- 5. + index_for_value extracted from allocate_values
51
-
52
-
53
- ## [0.1.1] - 20260417
54
- ## Extract determine_bin_width
55
-
56
- 1. ~ initialize: extracted determine_bin_width from one-liner
57
- 2. /compute_boundaries/calculate_boundaries/
58
-
59
-
60
- ## [0.1.0] - 20260417
61
- ## Bin class
62
-
63
- 1. + Statistics::Histogram::Bin
64
- 2. ~ allocate_values: creates Bin instances instead of hash entries
65
-
66
-
67
- ## [0.0.0] - 20260417
68
- ## Statistics::Histogram
69
-
70
- 1. + lib/Statistics/Histogram.rb
71
- 2. - lib/BinWidth.rb
72
-
73
-
74
- ## 20260416
75
- ## BinWidth 0.1.0 to 0.2.0: Reintroduce all named strategies
76
-
77
- 1. /0.1.0/0.2.0/
78
- 2. Reintroduce all named strategies.
79
-
80
-
81
- ## 20260416
82
- ## BinWidth 0.0.0 to 0.1.0: Tuneable root generalisation
83
-
84
- 1. /0.0.0/0.1.0/
85
- 2. + tuneable_root as the general form
86
- 3. ~ square_root rewritten as range * n^(-1/2) form
87
-
88
-
89
- ## 20260416
90
- ## BinWidth 0.0.0
91
-
92
- 1. + lib/BinWidth.rb
data/Gemfile DELETED
@@ -1,3 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec