rust 0.1
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 +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: []
|