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.
@@ -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: []