rust 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ require_relative 'rust-tests'
2
+ require_relative 'rust-effsize'
3
+ require_relative 'rust-descriptive'
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: []