rust 0.7 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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