rust 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/rust-core.rb +66 -0
- data/lib/rust-descriptive.rb +59 -0
- data/lib/rust-effsize.rb +79 -0
- data/lib/rust-tests.rb +144 -0
- data/lib/rust.rb +3 -0
- metadata +87 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fca0e67fdbbd71271d8baf0ab5c1b7cee895dd6f546699ca420bd0fa9ceb8a83
|
4
|
+
data.tar.gz: d0c30431caacfcd06744d8973ee2938d3e8b44297624d6dcb514fde1280a43cd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5004a2514605db69f616eeb25c3d89eb7ac6b6702be97a15202c0b4bc9731e4e0b55347a256dc7123c85c7342be3af096689a88a07b1c0489f88a86aacfef982
|
7
|
+
data.tar.gz: 1e969dca0511a2ac803c1e4369b6471641af4850b53663293dd69cd1009ecec352570173a484fe91b9ed6c822052592fde7a6cb165dc01eaed6efabea3a693cd
|
data/lib/rust-core.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'code-assertions'
|
2
|
+
require 'stringio'
|
3
|
+
require 'rinruby'
|
4
|
+
|
5
|
+
module Rust
|
6
|
+
CLIENT_MUTEX = Mutex.new
|
7
|
+
R_MUTEX = Mutex.new
|
8
|
+
|
9
|
+
R_ENGINE = RinRuby.new(echo: false)
|
10
|
+
|
11
|
+
@@in_client_mutex = false
|
12
|
+
|
13
|
+
def self.exclusive
|
14
|
+
CLIENT_MUTEX.synchronize do
|
15
|
+
@@in_client_mutex = true
|
16
|
+
yield
|
17
|
+
@@in_client_mutex = false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self._pull(r_command, return_warnings = false)
|
22
|
+
R_MUTEX.synchronize do
|
23
|
+
assert("This command must be executed in an exclusive block") { @@in_client_mutex }
|
24
|
+
|
25
|
+
$stdout = StringIO.new
|
26
|
+
if return_warnings
|
27
|
+
R_ENGINE.echo(true, true)
|
28
|
+
else
|
29
|
+
R_ENGINE.echo(false, false)
|
30
|
+
end
|
31
|
+
result = R_ENGINE.pull(r_command)
|
32
|
+
R_ENGINE.echo(false, false)
|
33
|
+
warnings = $stdout.string
|
34
|
+
$stdout = STDOUT
|
35
|
+
|
36
|
+
if return_warnings
|
37
|
+
return result, warnings.lines.map { |w| w.strip.chomp }
|
38
|
+
else
|
39
|
+
return result
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self._eval(r_command, return_warnings = false)
|
45
|
+
R_MUTEX.synchronize do
|
46
|
+
assert("This command must be executed in an exclusive block") { @@in_client_mutex }
|
47
|
+
|
48
|
+
$stdout = StringIO.new
|
49
|
+
if return_warnings
|
50
|
+
R_ENGINE.echo(true, true)
|
51
|
+
else
|
52
|
+
R_ENGINE.echo(false, false)
|
53
|
+
end
|
54
|
+
result = R_ENGINE.eval(r_command)
|
55
|
+
R_ENGINE.echo(false, false)
|
56
|
+
warnings = $stdout.string
|
57
|
+
$stdout = STDOUT
|
58
|
+
|
59
|
+
if return_warnings
|
60
|
+
return result, warnings.lines.map { |w| w.strip.chomp }
|
61
|
+
else
|
62
|
+
return result
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'code-assertions'
|
2
|
+
|
3
|
+
require_relative 'rust-core'
|
4
|
+
|
5
|
+
module Rust::Descriptive
|
6
|
+
class << self
|
7
|
+
def mean(data)
|
8
|
+
raise TypeError, "Expecting Array of numerics" if !data.is_a?(Array) || !data.all? { |e| e.is_a?(Numeric) }
|
9
|
+
|
10
|
+
return data.sum.to_f / data.size
|
11
|
+
end
|
12
|
+
|
13
|
+
def standard_deviation(data)
|
14
|
+
raise TypeError, "Expecting Array of numerics" if !data.is_a?(Array) || !data.all? { |e| e.is_a?(Numeric) }
|
15
|
+
|
16
|
+
# TODO implement
|
17
|
+
end
|
18
|
+
alias :sd :standard_deviation
|
19
|
+
alias :stddev :standard_deviation
|
20
|
+
|
21
|
+
def variance(data)
|
22
|
+
raise TypeError, "Expecting Array of numerics" if !data.is_a?(Array) || !data.all? { |e| e.is_a?(Numeric) }
|
23
|
+
|
24
|
+
# TODO implement
|
25
|
+
end
|
26
|
+
alias :var :variance
|
27
|
+
|
28
|
+
def median(data)
|
29
|
+
raise TypeError, "Expecting Array of numerics" if !data.is_a?(Array) || !data.all? { |e| e.is_a?(Numeric) }
|
30
|
+
|
31
|
+
sorted = data.sort
|
32
|
+
if data.size == 0
|
33
|
+
return Float::NAN
|
34
|
+
elsif data.size.odd?
|
35
|
+
return sorted[data.size / 2]
|
36
|
+
else
|
37
|
+
i = (data.size / 2)
|
38
|
+
return (sorted[i - 1] + sorted[i]) / 2.0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def quantile(data, percentiles=[0.0, 0.25, 0.5, 0.75, 1.0])
|
43
|
+
raise TypeError, "Expecting Array of numerics" if !data.is_a?(Array) || !data.all? { |e| e.is_a?(Numeric) }
|
44
|
+
raise TypeError, "Expecting Array of numerics" if !percentiles.is_a?(Array) || !percentiles.all? { |e| e.is_a?(Numeric) }
|
45
|
+
raise "Percentiles outside the range: #{percentiles}" if percentiles.any? { |e| !e.between?(0, 1) }
|
46
|
+
|
47
|
+
Rust.exclusive do
|
48
|
+
Rust::R_ENGINE.data = data
|
49
|
+
Rust::R_ENGINE.percs = percentiles
|
50
|
+
|
51
|
+
call_result = Rust._pull("quantile(data, percs)")
|
52
|
+
assert { call_result.is_a?(Array) }
|
53
|
+
assert { call_result.size == percentiles.size }
|
54
|
+
|
55
|
+
return percentiles.zip(call_result).to_h
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/rust-effsize.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'code-assertions'
|
2
|
+
|
3
|
+
Rust.exclusive do
|
4
|
+
Rust._eval("library(effsize)")
|
5
|
+
end
|
6
|
+
|
7
|
+
module Rust::EffectSize
|
8
|
+
class Result
|
9
|
+
attr_accessor :name
|
10
|
+
attr_accessor :estimate
|
11
|
+
attr_accessor :confidence_interval
|
12
|
+
attr_accessor :confidence_level
|
13
|
+
attr_accessor :magnitude
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
return "#{name} = #{estimate} (#{magnitude}) [#{confidence_interval.min}, #{confidence_interval.max}]"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Rust::EffectSize::CliffDelta
|
22
|
+
class << self
|
23
|
+
def compute(d1, d2)
|
24
|
+
raise TypeError, "Expecting Array of numerics" if !d1.is_a?(Array) || !d1.all? { |e| e.is_a?(Numeric) }
|
25
|
+
raise TypeError, "Expecting Array of numerics" if !d2.is_a?(Array) || !d2.all? { |e| e.is_a?(Numeric) }
|
26
|
+
|
27
|
+
Rust.exclusive do
|
28
|
+
Rust::R_ENGINE.a = d1
|
29
|
+
Rust::R_ENGINE.b = d2
|
30
|
+
|
31
|
+
Rust._eval("result = cliff.delta(a, b)")
|
32
|
+
|
33
|
+
result = Rust::EffectSize::Result.new
|
34
|
+
result.name = "Cliff's delta"
|
35
|
+
result.estimate = Rust._pull("result$estimate")
|
36
|
+
result.confidence_interval = Range.new(*Rust._pull("result$conf.int"))
|
37
|
+
result.confidence_level = Rust._pull("result$conf.level")
|
38
|
+
result.magnitude = Rust._pull("as.character(result$magnitude)").to_sym
|
39
|
+
|
40
|
+
return result
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module Rust::EffectSize::CohenD
|
47
|
+
class << self
|
48
|
+
def compute(d1, d2)
|
49
|
+
raise TypeError, "Expecting Array of numerics" if !d1.is_a?(Array) || !d1.all? { |e| e.is_a?(Numeric) }
|
50
|
+
raise TypeError, "Expecting Array of numerics" if !d2.is_a?(Array) || !d2.all? { |e| e.is_a?(Numeric) }
|
51
|
+
|
52
|
+
Rust.exclusive do
|
53
|
+
Rust::R_ENGINE.a = d1
|
54
|
+
Rust::R_ENGINE.b = d2
|
55
|
+
|
56
|
+
Rust._eval("result = cohen.d(a, b)")
|
57
|
+
|
58
|
+
result = Rust::EffectSize::Result.new
|
59
|
+
result.name = "Cohen's d"
|
60
|
+
result.estimate = Rust._pull("result$estimate")
|
61
|
+
result.confidence_interval = Range.new(*Rust._pull("result$conf.int"))
|
62
|
+
result.confidence_level = Rust._pull("result$conf.level")
|
63
|
+
result.magnitude = Rust._pull("as.character(result$magnitude)").to_sym
|
64
|
+
|
65
|
+
return result
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module Rust::RBindings
|
72
|
+
def cliff_delta(d1, d2)
|
73
|
+
Rust::EffectSize::CliffDelta.compute(d1, d2)
|
74
|
+
end
|
75
|
+
|
76
|
+
def cohen_d(d1, d2, **args)
|
77
|
+
Rust::EffectSize::CohenD.compute(d1, d2)
|
78
|
+
end
|
79
|
+
end
|
data/lib/rust-tests.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
require_relative 'rust-core'
|
2
|
+
|
3
|
+
module Rust::StatisticalTests
|
4
|
+
class Result
|
5
|
+
attr_accessor :name
|
6
|
+
attr_accessor :statistics
|
7
|
+
attr_accessor :pvalue
|
8
|
+
attr_accessor :exact
|
9
|
+
attr_accessor :alpha
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@statistics = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](name)
|
16
|
+
return @statistics[name.to_sym]
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(name, value)
|
20
|
+
@statistics[name.to_sym] = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def significant
|
24
|
+
pvalue < alpha
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
return "#{name}. P-value = #{pvalue} " +
|
29
|
+
"(#{significant ? "significant" : "not significant"} w/ alpha = #{alpha}); " +
|
30
|
+
"#{ statistics.map { |k, v| k.to_s + " -> " + v.to_s }.join(", ") }." +
|
31
|
+
(!exact ? " P-value is not exact." : "")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Rust::StatisticalTests::Wilcoxon
|
37
|
+
class << self
|
38
|
+
def paired(d1, d2, alpha = 0.05)
|
39
|
+
raise TypeError, "Expecting Array of numerics" if !d1.is_a?(Array) || !d1.all? { |e| e.is_a?(Numeric) }
|
40
|
+
raise TypeError, "Expecting Array of numerics" if !d2.is_a?(Array) || !d2.all? { |e| e.is_a?(Numeric) }
|
41
|
+
raise "The two distributions have different size" if d1.size != d2.size
|
42
|
+
|
43
|
+
Rust.exclusive do
|
44
|
+
Rust::R_ENGINE.a = d1
|
45
|
+
Rust::R_ENGINE.b = d2
|
46
|
+
|
47
|
+
_, warnings = Rust._eval("result = wilcox.test(a, b, alternative='two.sided', paired=T)", true)
|
48
|
+
result = Rust::StatisticalTests::Result.new
|
49
|
+
result.name = "Wilcoxon Signed-Rank test"
|
50
|
+
result.pvalue = Rust._pull("result$p.value")
|
51
|
+
result[:w] = Rust._pull("result$statistic")
|
52
|
+
result.exact = !warnings.include?("cannot compute exact p-value with zeroes")
|
53
|
+
result.alpha = alpha
|
54
|
+
|
55
|
+
return result
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def unpaired(d1, d2, alpha = 0.05)
|
60
|
+
raise TypeError, "Expecting Array of numerics" if !d1.is_a?(Array) || !d1.all? { |e| e.is_a?(Numeric) }
|
61
|
+
raise TypeError, "Expecting Array of numerics" if !d2.is_a?(Array) || !d2.all? { |e| e.is_a?(Numeric) }
|
62
|
+
|
63
|
+
Rust.exclusive do
|
64
|
+
Rust::R_ENGINE.a = d1
|
65
|
+
Rust::R_ENGINE.b = d2
|
66
|
+
|
67
|
+
_, warnings = Rust._eval("result = wilcox.test(a, b, alternative='two.sided', paired=F)", true)
|
68
|
+
result = Rust::StatisticalTests::Result.new
|
69
|
+
result.name = "Mann–Whitney U test, Wilcoxon Ranked-Sum test"
|
70
|
+
result.pvalue = Rust._pull("result$p.value")
|
71
|
+
result[:w] = Rust._pull("result$statistic")
|
72
|
+
result.exact = !warnings.include?("cannot compute exact p-value with ties")
|
73
|
+
result.alpha = alpha
|
74
|
+
|
75
|
+
return result
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module Rust::StatisticalTests::T
|
82
|
+
class << self
|
83
|
+
def paired(d1, d2, alpha = 0.05)
|
84
|
+
raise TypeError, "Expecting Array of numerics" if !d1.is_a?(Array) || !d1.all? { |e| e.is_a?(Numeric) }
|
85
|
+
raise TypeError, "Expecting Array of numerics" if !d2.is_a?(Array) || !d2.all? { |e| e.is_a?(Numeric) }
|
86
|
+
raise "The two distributions have different size" if d1.size != d2.size
|
87
|
+
|
88
|
+
Rust.exclusive do
|
89
|
+
Rust::R_ENGINE.a = d1
|
90
|
+
Rust::R_ENGINE.b = d2
|
91
|
+
|
92
|
+
warnings = Rust._eval("result = t.test(a, b, alternative='two.sided', paired=T)")
|
93
|
+
result = Rust::StatisticalTests::Result.new
|
94
|
+
result.name = "Paired t-test"
|
95
|
+
result.pvalue = Rust._pull("result$p.value")
|
96
|
+
result[:t] = Rust._pull("result$statistic")
|
97
|
+
result.exact = true
|
98
|
+
result.alpha = alpha
|
99
|
+
|
100
|
+
return result
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def unpaired(d1, d2, alpha = 0.05)
|
105
|
+
raise TypeError, "Expecting Array of numerics" if !d1.is_a?(Array) || !d1.all? { |e| e.is_a?(Numeric) }
|
106
|
+
raise TypeError, "Expecting Array of numerics" if !d2.is_a?(Array) || !d2.all? { |e| e.is_a?(Numeric) }
|
107
|
+
|
108
|
+
Rust.exclusive do
|
109
|
+
Rust::R_ENGINE.a = d1
|
110
|
+
Rust::R_ENGINE.b = d2
|
111
|
+
|
112
|
+
Rust._eval("result = t.test(a, b, alternative='two.sided', paired=F)")
|
113
|
+
result = Rust::StatisticalTests::Result.new
|
114
|
+
result.name = "Welch Two Sample t-test"
|
115
|
+
result.pvalue = Rust._pull("result$p.value")
|
116
|
+
result[:t] = Rust._pull("result$statistic")
|
117
|
+
result.exact = true
|
118
|
+
result.alpha = alpha
|
119
|
+
|
120
|
+
return result
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
module Rust::RBindings
|
127
|
+
def wilcox_test(d1, d2, **args)
|
128
|
+
paired = args[:paired] || false
|
129
|
+
if paired
|
130
|
+
return Rust::StatisticalTests::Wilcoxon.paired(d1, d2)
|
131
|
+
else
|
132
|
+
return Rust::StatisticalTests::Wilcoxon.unpaired(d1, d2)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def t_test(d1, d2, **args)
|
137
|
+
paired = args[:paired] || false
|
138
|
+
if paired
|
139
|
+
return Rust::StatisticalTests::T.paired(d1, d2)
|
140
|
+
else
|
141
|
+
return Rust::StatisticalTests::T.unpaired(d1, d2)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
data/lib/rust.rb
ADDED
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rust
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Simone Scalabrino
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-08-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rinruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.1.0
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 2.1.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.1.0
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.1.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: code-assertions
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 1.1.2
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.1.2
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 1.1.2
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.1.2
|
53
|
+
description: Ruby advanced statistical library based on RinRuby
|
54
|
+
email: s.scalabrino9@gmail.com
|
55
|
+
executables: []
|
56
|
+
extensions: []
|
57
|
+
extra_rdoc_files: []
|
58
|
+
files:
|
59
|
+
- lib/rust-core.rb
|
60
|
+
- lib/rust-descriptive.rb
|
61
|
+
- lib/rust-effsize.rb
|
62
|
+
- lib/rust-tests.rb
|
63
|
+
- lib/rust.rb
|
64
|
+
homepage: https://github.com/intersimone999/ruby-rust
|
65
|
+
licenses:
|
66
|
+
- GPL-3.0-only
|
67
|
+
metadata: {}
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
requirements: []
|
83
|
+
rubygems_version: 3.1.4
|
84
|
+
signing_key:
|
85
|
+
specification_version: 4
|
86
|
+
summary: Ruby advanced statistical library
|
87
|
+
test_files: []
|