rust 0.7 → 0.9

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,59 @@
1
+ require_relative 'datatype'
2
+
3
+ module Rust
4
+ class S4Class < RustDatatype
5
+ def self.can_pull?(type, klass)
6
+ return type == "S4"
7
+ end
8
+
9
+ def self.pull_variable(variable, type, klass)
10
+ slots = [Rust._pull("names(getSlots(\"#{klass}\"))")].flatten
11
+
12
+ return S4Class.new(variable, klass, slots)
13
+ end
14
+
15
+ def load_in_r_as(variable_name)
16
+ Rust._eval("#{variable_name} <- #{self.r_mirror}")
17
+ end
18
+
19
+ def r_hash
20
+ "immutable"
21
+ end
22
+
23
+ def initialize(variable_name, klass, slots)
24
+ @klass = klass
25
+ @slots = slots
26
+
27
+ self.r_mirror_to(variable_name)
28
+ end
29
+
30
+ def [](key)
31
+ raise ArgumentError, "Unknown slot `#{key}` for class `#@klass`" unless @slots.include?(key)
32
+
33
+ Rust.exclusive do
34
+ return Rust["#{self.r_mirror}@#{key}"]
35
+ end
36
+ end
37
+ alias :| :[]
38
+
39
+ def []=(key, value)
40
+ raise ArgumentError, "Unknown slot `#{key}` for class `#@klass`" unless @slots.include?(key)
41
+
42
+ Rust.exclusive do
43
+ return Rust["#{self.r_mirror}@#{key}"] = value
44
+ end
45
+ end
46
+
47
+ def slots
48
+ @slots
49
+ end
50
+
51
+ def class_name
52
+ @klass
53
+ end
54
+
55
+ def inspect
56
+ return "<S4 instance of #@klass, with slots #@slots>"
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,109 @@
1
+ require_relative 'datatype'
2
+
3
+ module Rust
4
+ class Sequence < RustDatatype
5
+ attr_reader :min
6
+ attr_reader :max
7
+
8
+ def self.can_pull?(type, klass)
9
+ return false
10
+ end
11
+
12
+ def initialize(min, max, step=1)
13
+ @min = min
14
+ @max = max
15
+ @step = step
16
+ end
17
+
18
+ def step(step)
19
+ @step = step
20
+ end
21
+
22
+ def each
23
+ (@min..@max).step(@step) do |v|
24
+ yield v
25
+ end
26
+ end
27
+
28
+ def to_a
29
+ result = []
30
+ self.each do |v|
31
+ result << v
32
+ end
33
+ return result
34
+ end
35
+
36
+ def to_R
37
+ "seq(from=#@min, to=#@max, by=#@step)"
38
+ end
39
+
40
+ def load_in_r_as(variable_name)
41
+ Rust._eval("#{variable_name} <- #{self.to_R}")
42
+ end
43
+ end
44
+
45
+ class MathArray < Array
46
+ def -(other)
47
+ raise ArgumentError, "Expected array or numeric" if !other.is_a?(::Array) && !other.is_a?(Numeric)
48
+ raise ArgumentError, "The two arrays must have the same size" if other.is_a?(::Array) && self.size != other.size
49
+
50
+ result = self.clone
51
+ other = [other] * self.size if other.is_a?(Numeric)
52
+ for i in 0...self.size
53
+ result[i] -= other[i]
54
+ end
55
+
56
+ return result
57
+ end
58
+
59
+ def *(other)
60
+ raise ArgumentError, "Expected array or numeric" if !other.is_a?(::Array) && !other.is_a?(Numeric)
61
+ raise ArgumentError, "The two arrays must have the same size" if other.is_a?(::Array) && self.size != other.size
62
+
63
+ result = self.clone
64
+ other = [other] * self.size if other.is_a?(Numeric)
65
+ for i in 0...self.size
66
+ result[i] *= other[i]
67
+ end
68
+
69
+ return result
70
+ end
71
+
72
+ def +(other)
73
+ raise ArgumentError, "Expected array or numeric" if !other.is_a?(::Array) && !other.is_a?(Numeric)
74
+ raise ArgumentError, "The two arrays must have the same size" if other.is_a?(::Array) && self.size != other.size
75
+
76
+ result = self.clone
77
+ other = [other] * self.size if other.is_a?(Numeric)
78
+ for i in 0...self.size
79
+ result[i] += other[i]
80
+ end
81
+
82
+ return result
83
+ end
84
+
85
+ def /(other) #/# <- this comment is just to recover the syntax highlighting bug in Kate
86
+ raise ArgumentError, "Expected array or numeric" if !other.is_a?(::Array) && !other.is_a?(Numeric)
87
+ raise ArgumentError, "The two arrays must have the same size" if other.is_a?(::Array) && self.size != other.size
88
+
89
+ result = self.clone
90
+ other = [other] * self.size if other.is_a?(Numeric)
91
+ for i in 0...self.size
92
+ result[i] /= other[i]
93
+ end
94
+
95
+ return result
96
+ end
97
+
98
+ def **(other)
99
+ raise ArgumentError, "Expected numeric" if !other.is_a?(Numeric)
100
+
101
+ result = self.clone
102
+ for i in 0...self.size
103
+ result[i] = result[i] ** other
104
+ end
105
+
106
+ return result
107
+ end
108
+ end
109
+ end
data/lib/rust/core.rb ADDED
@@ -0,0 +1,7 @@
1
+ require_relative 'core/rust'
2
+ require_relative 'core/csv'
3
+
4
+ self_path = File.expand_path(__FILE__)
5
+ Dir.glob(File.join(File.dirname(self_path), "core/types/*.rb")).each do |lib|
6
+ require_relative lib
7
+ end
@@ -0,0 +1,4 @@
1
+ self_path = File.expand_path(__FILE__)
2
+ Dir.glob(File.dirname(self_path) + "/*.rb").each do |lib|
3
+ require_relative lib unless lib == self_path
4
+ end
@@ -0,0 +1,60 @@
1
+ require_relative '../core'
2
+
3
+ module Rust
4
+ class ANOVAModel < RustDatatype
5
+ def self.can_pull?(type, klass)
6
+ return type == "list" && [klass].flatten.include?("aov")
7
+ end
8
+
9
+ def self.pull_variable(variable, type, klass)
10
+ model = RustDatatype.pull_variable(variable, Rust::List)
11
+
12
+ return ANOVAModel.new(model)
13
+ end
14
+
15
+ def load_in_r_as(variable_name)
16
+ @model.load_in_r_as(variable_name)
17
+ end
18
+
19
+ def self.generate(formula, data, **options)
20
+ mapped = ""
21
+ if options.size > 0
22
+ mapped = options.map { |k, v| "#{k}=#{v}" }.join(", ")
23
+ mapped = ", " + mapped
24
+ end
25
+
26
+ Rust.exclusive do
27
+ Rust["aov.data"] = data
28
+ Rust._eval("aov.model.result <- aov(#{formula.to_R}, data=aov.data#{mapped})")
29
+ result = ANOVAModel.new(Rust["aov.model.result"])
30
+ result.r_mirror_to("aov.model.result")
31
+ return result
32
+ end
33
+ end
34
+
35
+ def initialize(model)
36
+ @model = model
37
+ end
38
+
39
+ def model
40
+ @model
41
+ end
42
+
43
+ def summary
44
+ unless @summary
45
+ Rust.exclusive do
46
+ Rust._eval("aov.smr <- summary(#{self.r_mirror})")
47
+ @summary = Rust['aov.smr']
48
+ end
49
+ end
50
+
51
+ return @summary
52
+ end
53
+ end
54
+ end
55
+
56
+ module Rust::RBindings
57
+ def aov(formula, data, **options)
58
+ return ANOVAModel.generate(formula, data, **options)
59
+ end
60
+ end
@@ -0,0 +1,205 @@
1
+ require_relative '../core'
2
+ require_relative '../stats/descriptive'
3
+ require_relative '../stats/correlation'
4
+
5
+ module Rust::Models
6
+ end
7
+
8
+ module Rust::Models::Regression
9
+ class RegressionModel < Rust::RustDatatype
10
+ def self.can_pull?(type, klass)
11
+ # Can only pull specific sub-types
12
+ return false
13
+ end
14
+
15
+ def load_in_r_as(variable_name)
16
+ @model.load_in_r_as(variable_name)
17
+ end
18
+
19
+
20
+ def self.generate(object_type, model_type, dependent_variable, independent_variables, data, **options)
21
+ mapped = ""
22
+ if options.size > 0
23
+ mapped = options.map { |k, v| "#{k}=#{v}" }.join(", ")
24
+ mapped = ", " + mapped
25
+ end
26
+
27
+ formula = Rust::Formula.new(dependent_variable, independent_variables.join(" + "))
28
+
29
+ Rust.exclusive do
30
+ Rust["#{model_type}.data"] = data
31
+
32
+ Rust._eval("#{model_type}.model.result <- #{model_type}(#{formula.to_R}, data=#{model_type}.data#{mapped})")
33
+ result = Rust["#{model_type}.model.result"]
34
+ result.r_mirror_to("#{model_type}.model.result")
35
+
36
+ return result
37
+ end
38
+ end
39
+
40
+ def initialize(model)
41
+ raise StandardError if model.is_a?(RegressionModel)
42
+ @model = model
43
+ end
44
+
45
+ def model
46
+ @model
47
+ end
48
+
49
+ def residuals
50
+ Rust.exclusive do
51
+ @residuals = Rust["residuals(#{self.r_mirror})"] unless @residuals
52
+ end
53
+
54
+ return @residuals
55
+ end
56
+
57
+ def fitted
58
+ Rust.exclusive do
59
+ @fitted = Rust["fitted(#{self.r_mirror})"] unless @fitted
60
+ end
61
+
62
+ return @fitted
63
+ end
64
+
65
+ def actuals
66
+ return self.fitted.zip(self.residuals).map { |couple| couple.sum }
67
+ end
68
+
69
+ def r_2
70
+ return self.summary|"r.squared"
71
+ end
72
+
73
+ def r_2_adjusted
74
+ return self.summary|"adj.r.squared"
75
+ end
76
+
77
+ def mse
78
+ Rust::Descriptive.variance(self.residuals)
79
+ end
80
+
81
+ def coefficients
82
+ a = self.summary|"coefficients"
83
+ end
84
+
85
+ def method_missing(name, *args)
86
+ return model|name.to_s
87
+ end
88
+
89
+ def summary
90
+ unless @summary
91
+ Rust.exclusive do
92
+ @summary = Rust["summary(#{self.r_mirror})"]
93
+ end
94
+ end
95
+
96
+ return @summary
97
+ end
98
+
99
+ def r_hash
100
+ @model.r_hash
101
+ end
102
+ end
103
+
104
+ class LinearRegressionModel < RegressionModel
105
+ def self.can_pull?(type, klass)
106
+ return type == "list" && klass == "lm"
107
+ end
108
+
109
+ def self.pull_variable(variable, type, klass)
110
+ model = Rust::RustDatatype.pull_variable(variable, Rust::List)
111
+
112
+ return LinearRegressionModel.new(model)
113
+ end
114
+
115
+ def self.generate(dependent_variable, independent_variables, data, **options)
116
+ RegressionModel.generate(
117
+ LinearRegressionModel,
118
+ "lm",
119
+ dependent_variable,
120
+ independent_variables,
121
+ data,
122
+ **options
123
+ )
124
+ end
125
+ end
126
+
127
+ class LinearMixedEffectsModel < RegressionModel
128
+ def self.can_pull?(type, klass)
129
+ return type == "S4" && klass == "lmerModLmerTest"
130
+ end
131
+
132
+ def self.pull_priority
133
+ 1
134
+ end
135
+
136
+ def self.pull_variable(variable, type, klass)
137
+ model = Rust::RustDatatype.pull_variable(variable, Rust::S4Class)
138
+
139
+ return LinearMixedEffectsModel.new(model)
140
+ end
141
+
142
+ def summary
143
+ unless @summary
144
+ Rust.exclusive do
145
+ Rust._eval("tmp.summary <- summary(#{self.r_mirror})")
146
+ Rust._eval("mode(tmp.summary$objClass) <- \"list\"")
147
+ Rust._eval("tmp.summary$logLik <- attributes(tmp.summary$logLik)")
148
+ @summary = Rust["tmp.summary"]
149
+ end
150
+ end
151
+
152
+ return @summary
153
+ end
154
+
155
+ def self.generate(dependent_variable, fixed_effects, random_effects, data, **options)
156
+ Rust.prerequisite("lmerTest")
157
+ Rust.prerequisite("rsq")
158
+
159
+ random_effects = random_effects.map { |effect| "(1|#{effect})" }
160
+
161
+ RegressionModel.generate(
162
+ LinearMixedEffectsModel,
163
+ "lmer",
164
+ dependent_variable,
165
+ fixed_effects + random_effects,
166
+ data,
167
+ **options
168
+ )
169
+ end
170
+
171
+ def r_2
172
+ Rust.exclusive do
173
+ Rust._eval("tmp.rsq <- rsq(#{self.r_mirror}, adj=F)")
174
+ return Rust['tmp.rsq']
175
+ end
176
+ end
177
+
178
+ def r_2_adjusted
179
+ Rust.exclusive do
180
+ Rust._eval("tmp.rsq <- rsq(#{self.r_mirror}, adj=T)")
181
+ return Rust['tmp.rsq']
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ module Rust::RBindings
188
+ def lm(formula, data, **options)
189
+ independent = formula.right_part.split("+").map { |v| v.strip }
190
+ return LinearRegressionModel.generate(formula.left_part, independent, data, **options)
191
+ end
192
+
193
+ def lmer(formula, data, **options)
194
+ independent = formula.right_part.split("+").map { |v| v.strip }
195
+
196
+ RegressionModel.generate(
197
+ LinearMixedEffectsModel,
198
+ "lmer",
199
+ formula.left_part,
200
+ independent,
201
+ data,
202
+ **options
203
+ )
204
+ end
205
+ end
@@ -0,0 +1,4 @@
1
+ self_path = File.expand_path(__FILE__)
2
+ Dir.glob(File.dirname(self_path) + "/*.rb").each do |lib|
3
+ require_relative lib unless lib == self_path
4
+ end
@@ -0,0 +1,111 @@
1
+ require_relative 'core'
2
+
3
+ module Rust::Plots
4
+ class ScatterPlot < BasePlot
5
+ def initialize(x = nil, y = nil, **options)
6
+ super()
7
+ @series = []
8
+ if x && y
9
+ self.series(x, y, **options)
10
+ end
11
+ end
12
+
13
+ def series(x, y, **options)
14
+ @series << [x, y, options]
15
+
16
+ return self
17
+ end
18
+
19
+ def thickness(t)
20
+ self['lwd'] = t
21
+
22
+ return self
23
+ end
24
+
25
+ def lines()
26
+ self['type'] = "l"
27
+
28
+ return self
29
+ end
30
+
31
+ def points()
32
+ self['type'] = "p"
33
+
34
+ return self
35
+ end
36
+
37
+ def lines_and_points()
38
+ self['type'] = "b"
39
+
40
+ return self
41
+ end
42
+
43
+ protected
44
+ def _show()
45
+ first = true
46
+ palette = self.palette(@series.size)
47
+ i = 0
48
+
49
+ base_options = {}
50
+ unless @options['xlim']
51
+ x_values = @series.map { |v| v[0] }.flatten
52
+ y_values = @series.map { |v| v[1] }.flatten
53
+
54
+ base_options[:xlim] = [x_values.min, x_values.max]
55
+ base_options[:ylim] = [y_values.min, y_values.max]
56
+ end
57
+
58
+ @series.each do |x, y, options|
59
+ options = options.merge(base_options)
60
+ Rust["plotter.x"] = x
61
+ Rust["plotter.y"] = y
62
+
63
+ function = nil
64
+ if first
65
+ function = Rust::Function.new("plot")
66
+ first = false
67
+ else
68
+ function = Rust::Function.new("lines")
69
+ end
70
+
71
+ augmented_options = {}
72
+ augmented_options['col'] = options[:color] || palette[i]
73
+ augmented_options['xlim'] = options[:xlim] if options[:xlim]
74
+ augmented_options['ylim'] = options[:ylim] if options[:ylim]
75
+
76
+ function.options = self._augmented_options(augmented_options)
77
+ function.arguments << Rust::Variable.new("plotter.x")
78
+ function.arguments << Rust::Variable.new("plotter.y")
79
+
80
+ function.call
81
+
82
+ i += 1
83
+ end
84
+
85
+ return self
86
+ end
87
+ end
88
+
89
+ class BarPlot < BasePlot
90
+ def initialize(bars)
91
+ super()
92
+ @bars = bars
93
+ end
94
+
95
+ protected
96
+ def _show()
97
+ Rust["plotter.bars"] = @bars.values
98
+ Rust["plotter.labels"] = @bars.keys
99
+
100
+ Rust._eval("names(plotter.bars) <- plotter.labels")
101
+
102
+ function = Rust::Function.new("barplot")
103
+ function.options = self._augmented_options
104
+ function.arguments << Rust::Variable.new("plotter.bars")
105
+
106
+ function.call
107
+
108
+ return self
109
+ end
110
+ end
111
+ end