Browse Source

Merge branch 'pyrokar-median_quartiles'

master
Arthur POULET 3 years ago
parent
commit
93a1c0841c
No known key found for this signature in database GPG Key ID: FC154EA63FF5D8BC
  1. 39
      README.md
  2. 2
      shard.yml
  3. 16
      spec/math/median.cr
  4. 65
      spec/math/quartile.cr
  5. 13
      src/lib/math/median.cr
  6. 65
      src/lib/math/quartile.cr

39
README.md

@ -1,7 +1,7 @@
# stats
An expressive implementation of statistical distributions.
Compatible with crystal v0.24.1
Compatible with crystal v0.26.1
## Installation
@ -20,12 +20,13 @@ dependencies:
```crystal
require "stats"
include Stats
```
### Normal distribution
```crystal
NormaleDistribution::between # less_than, greater_than
NormaleDistribution.between # less_than, greater_than
standard_deviation: 15,
esperance: 100,
min: 85,
@ -82,6 +83,40 @@ Math.factorial(4) # => 24
[1,2,3,4].correlation_coef [-14,14,101,-100] + 1 > 1.5 # => false
```
### Median
```crystal
[1, 2, 5].median # => 2.0
[42, 1337].median # => 685.5
```
### Quartiles & Boxplot
```crystal
[1, 3, 5].first_quartile # => 2.0 (alias of lower_quartile)
[1, 3, 5].second_quartile # => 3.0 (alias of median)
[1, 3, 5].third_quartile # => 4.0 (alias of upper_quartile)
```
```crystal
arr = [-23, -5, 2, 5, 5, 6, 7, 8, 14, 15, 42, 1337]
arr.first_quartile # => 3.5 (Q1)
arr.second_quartile # => 6.5 (Q2)
arr.third_quartile # => 14.5 (Q3)
arr.interquartile_range # => 11.0 (alias of iqr) (IQR = Q3 - Q1)
# Tukey's fences with k = 1.5 (default parameter value)
arr.lower_fence # => -13.0 (Q1 - 1.5 * IQR)
arr.upper_fence # => 31 (Q3 + 1.5 * IQR)
arr.lower_outliers # => [-23]
arr.upper_outliers # => [42, 1337]
# Tukey's fences with k = 3 for "far out" outliers
arr.upper_fence(3) # => 47.5 (Q3 + 3 * IQR)
arr.upper_outliers(3) # => [1337]
```
## Development
- The lib is adapted to be usable with BigInt and BigFloat values

2
shard.yml

@ -1,5 +1,5 @@
name: stats
version: 0.1.6
version: 0.2.2
authors:
- Arthur Poulet <arthur.poulet@mailoo.org>

16
spec/math/median.cr

@ -0,0 +1,16 @@
describe Math::Median do
it "test trivia" do
arr = ([]of Int32)
arr.median.should eq 0.0
end
it "test basic" do
[1.0, 2.0].median.should eq 1.5
[42, 1337].median.should eq 689.5
[1, 2, 5].median.should eq 2.0
[2, 5, 1].median.should eq 2.0
[4, 1, 1, 1, 2].median.should eq 1.0
end
end

65
spec/math/quartile.cr

@ -0,0 +1,65 @@
module Math::Quartile
it "trivial" do
arr = [1, 3, 5]
arr.first_quartile.should eq 2.0
arr.second_quartile.should eq 3.0
arr.third_quartile.should eq 4.0
arr.iqr.should eq 2.0
end
it "odd size input" do
arr = [6, 7, 15, 36, 39, 40, 41, 42, 43, 47, 49]
arr.first_quartile.should eq 25.5
arr.second_quartile.should eq 40
arr.third_quartile.should eq 42.5
arr.iqr.should eq 17.0
end
it "even size input" do
arr = [7, 15, 36, 39, 40, 41]
arr.first_quartile.should eq 15.0
arr.second_quartile.should eq 37.5
arr.third_quartile.should eq 40.0
arr.iqr.should eq 25.0
end
it "complex" do
arr = [7, 7, 31, 31, 47, 75, 87, 115, 116, 119, 119, 155, 177]
arr.first_quartile.should eq 31
arr.second_quartile.should eq 87
arr.third_quartile.should eq 119
arr.iqr.should eq 88
end
it "complex 2" do
# https://en.wikipedia.org/wiki/Quartile#Example_1
arr = [6, 7, 15, 36, 39, 40, 41, 42, 43, 47, 49]
arr.first_quartile.should eq 25.5
arr.second_quartile.should eq 40
arr.third_quartile.should eq 42.5
arr.iqr.should eq 17
end
it "boxplot values" do
arr = [-23, -5, 2, 5, 5, 6, 7, 8, 14, 15, 42, 1337]
arr.first_quartile.should eq 3.5
arr.second_quartile.should eq 6.5
arr.third_quartile.should eq 14.5
arr.iqr.should eq 11.0
arr.lower_fence.should eq -13.0
arr.upper_fence.should eq 31
arr.lower_outliers.should eq [-23]
arr.upper_outliers.should eq [42, 1337]
arr.upper_fence(3).should eq 47.5
arr.upper_outliers(3).should eq [1337]
end
end

13
src/lib/math/median.cr

@ -0,0 +1,13 @@
module Math::Median
def median : Float64
return 0.0_f64 if empty?
sorted = sort
size = size()
return sorted[(size - 1) / 2].to_f64 if size.odd?
(sorted[(size / 2) - 1] + sorted[size / 2]) / 2.0
end
end
module Enumerable(T)
include Math::Median
end

65
src/lib/math/quartile.cr

@ -0,0 +1,65 @@
# There are several methods for computing the quartiles.
#
# This library utilizes the method proposed by John Tukey
# https://en.wikipedia.org/wiki/Quartile#Method_2
#
module Math::Quartile
def lower_quartile : Float64
return 0.0_f64 if empty?
m = self.median
lower_half = self.select { |i| i <= m }
lower_half.median
end
# alias
def first_quartile : Float64
lower_quartile
end
# alias
def second_quartile : Float64
median
end
def upper_quartile : Float64
return 0.0_f64 if empty?
m = self.median
upper_half = self.select { |i| i >= m }
upper_half.median
end
def third_quartile : Float64
upper_quartile
end
def iqr : Float64
third_quartile - first_quartile
end
# alias
def interquartile_range : Float64
iqr
end
def lower_fence(k : Number = 1.5) : Float64
lower_quartile - k * iqr
end
def upper_fence(k : Number = 1.5) : Float64
upper_quartile + k * iqr
end
def lower_outliers(k : Number = 1.5) : Array
lf = lower_fence k
self.select { |i| i < lf }
end
def upper_outliers(k : Number = 1.5) : Array
uf = upper_fence k
self.select { |i| i > uf }
end
end
module Enumerable(T)
include Math::Quartile
end
Loading…
Cancel
Save