rust 0.7 → 0.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,258 @@
1
+ require_relative '../core'
2
+ require_relative '../stats/descriptive'
3
+ require_relative '../stats/correlation'
4
+
5
+ module Rust::Models
6
+ end
7
+
8
+ ##
9
+ # Contains classes that allow to run regression models.
10
+
11
+ module Rust::Models::Regression
12
+
13
+ ##
14
+ # Generic regression model in R.
15
+
16
+ class RegressionModel < Rust::RustDatatype
17
+ def self.can_pull?(type, klass)
18
+ # Can only pull specific sub-types
19
+ return false
20
+ end
21
+
22
+ def load_in_r_as(variable_name)
23
+ @model.load_in_r_as(variable_name)
24
+ end
25
+
26
+ ##
27
+ # Generates a new regression model. +object_type+ is the Ruby class of the model object; +model_type+ represents
28
+ # the type of model at hand; +dependent_variable+ and +independent_variables+ are directly used as part of the
29
+ # model formula. +data+ represents the dataset to be used. +options+ can be specified and directly passed to the
30
+ # model.
31
+
32
+ def self.generate(object_type, model_type, dependent_variable, independent_variables, data, **options)
33
+ mapped = ""
34
+ if options.size > 0
35
+ mapped = options.map { |k, v| "#{k}=#{v}" }.join(", ")
36
+ mapped = ", " + mapped
37
+ end
38
+
39
+ formula = Rust::Formula.new(dependent_variable, independent_variables.join(" + "))
40
+
41
+ Rust.exclusive do
42
+ Rust["#{model_type}.data"] = data
43
+
44
+ Rust._eval("#{model_type}.model.result <- #{model_type}(#{formula.to_R}, data=#{model_type}.data#{mapped})")
45
+ result = Rust["#{model_type}.model.result"]
46
+ result.r_mirror_to("#{model_type}.model.result")
47
+
48
+ return result
49
+ end
50
+ end
51
+
52
+ ##
53
+ # Creates a new +model+.
54
+
55
+ def initialize(model)
56
+ raise StandardError if model.is_a?(RegressionModel)
57
+ @model = model
58
+ end
59
+
60
+ def model
61
+ @model
62
+ end
63
+
64
+ ##
65
+ # Returns the residuals of the model.
66
+
67
+ def residuals
68
+ Rust.exclusive do
69
+ @residuals = Rust["residuals(#{self.r_mirror})"] unless @residuals
70
+ end
71
+
72
+ return @residuals
73
+ end
74
+
75
+ ##
76
+ # Returns the fitted values of the model.
77
+
78
+ def fitted
79
+ Rust.exclusive do
80
+ @fitted = Rust["fitted(#{self.r_mirror})"] unless @fitted
81
+ end
82
+
83
+ return @fitted
84
+ end
85
+
86
+ ##
87
+ # Returns the actual values in the dataset.
88
+
89
+ def actuals
90
+ return self.fitted.zip(self.residuals).map { |couple| couple.sum }
91
+ end
92
+
93
+ ##
94
+ # Returns the r-squared of the model.
95
+
96
+ def r_2
97
+ return self.summary|"r.squared"
98
+ end
99
+
100
+ ##
101
+ # Returns the adjusted r-squared of the model.
102
+
103
+ def r_2_adjusted
104
+ return self.summary|"adj.r.squared"
105
+ end
106
+
107
+ ##
108
+ # Returns the mean squared error of the model.
109
+
110
+ def mse
111
+ Rust::Descriptive.variance(self.residuals)
112
+ end
113
+
114
+ ##
115
+ # Returns the coefficients of the model.
116
+
117
+ def coefficients
118
+ a = self.summary|"coefficients"
119
+ end
120
+
121
+ def method_missing(name, *args)
122
+ return model|name.to_s
123
+ end
124
+
125
+ ##
126
+ # Returns a summary for the model using the summary function in R.
127
+
128
+ def summary
129
+ unless @summary
130
+ Rust.exclusive do
131
+ @summary = Rust["summary(#{self.r_mirror})"]
132
+ end
133
+ end
134
+
135
+ return @summary
136
+ end
137
+
138
+ def r_hash
139
+ @model.r_hash
140
+ end
141
+ end
142
+
143
+ ##
144
+ # Represents a linear regression model in R.
145
+
146
+ class LinearRegressionModel < RegressionModel
147
+ def self.can_pull?(type, klass)
148
+ return type == "list" && klass == "lm"
149
+ end
150
+
151
+ def self.pull_variable(variable, type, klass)
152
+ model = Rust::RustDatatype.pull_variable(variable, Rust::List)
153
+
154
+ return LinearRegressionModel.new(model)
155
+ end
156
+
157
+ ##
158
+ # Generates a linear regression model, given its +dependent_variable+ and +independent_variables+ and its +data+.
159
+ # +options+ can be specified and directly passed to the model.
160
+
161
+ def self.generate(dependent_variable, independent_variables, data, **options)
162
+ RegressionModel.generate(
163
+ LinearRegressionModel,
164
+ "lm",
165
+ dependent_variable,
166
+ independent_variables,
167
+ data,
168
+ **options
169
+ )
170
+ end
171
+ end
172
+
173
+ ##
174
+ # Represents a linear mixed effects model in R.
175
+
176
+ class LinearMixedEffectsModel < RegressionModel
177
+ def self.can_pull?(type, klass)
178
+ return type == "S4" && klass == "lmerModLmerTest"
179
+ end
180
+
181
+ def self.pull_priority
182
+ 1
183
+ end
184
+
185
+ def self.pull_variable(variable, type, klass)
186
+ model = Rust::RustDatatype.pull_variable(variable, Rust::S4Class)
187
+
188
+ return LinearMixedEffectsModel.new(model)
189
+ end
190
+
191
+ def summary
192
+ unless @summary
193
+ Rust.exclusive do
194
+ Rust._eval("tmp.summary <- summary(#{self.r_mirror})")
195
+ Rust._eval("mode(tmp.summary$objClass) <- \"list\"")
196
+ Rust._eval("tmp.summary$logLik <- attributes(tmp.summary$logLik)")
197
+ @summary = Rust["tmp.summary"]
198
+ end
199
+ end
200
+
201
+ return @summary
202
+ end
203
+
204
+ ##
205
+ # Generates a linear mixed effects model, given its +dependent_variable+ and +independent_variables+ and its +data+.
206
+ # +options+ can be specified and directly passed to the model.
207
+
208
+ def self.generate(dependent_variable, fixed_effects, random_effects, data, **options)
209
+ Rust.prerequisite("lmerTest")
210
+ Rust.prerequisite("rsq")
211
+
212
+ random_effects = random_effects.map { |effect| "(1|#{effect})" }
213
+
214
+ RegressionModel.generate(
215
+ LinearMixedEffectsModel,
216
+ "lmer",
217
+ dependent_variable,
218
+ fixed_effects + random_effects,
219
+ data,
220
+ **options
221
+ )
222
+ end
223
+
224
+ def r_2
225
+ Rust.exclusive do
226
+ Rust._eval("tmp.rsq <- rsq(#{self.r_mirror}, adj=F)")
227
+ return Rust['tmp.rsq']
228
+ end
229
+ end
230
+
231
+ def r_2_adjusted
232
+ Rust.exclusive do
233
+ Rust._eval("tmp.rsq <- rsq(#{self.r_mirror}, adj=T)")
234
+ return Rust['tmp.rsq']
235
+ end
236
+ end
237
+ end
238
+ end
239
+
240
+ module Rust::RBindings
241
+ def lm(formula, data, **options)
242
+ independent = formula.right_part.split("+").map { |v| v.strip }
243
+ return LinearRegressionModel.generate(formula.left_part, independent, data, **options)
244
+ end
245
+
246
+ def lmer(formula, data, **options)
247
+ independent = formula.right_part.split("+").map { |v| v.strip }
248
+
249
+ RegressionModel.generate(
250
+ LinearMixedEffectsModel,
251
+ "lmer",
252
+ formula.left_part,
253
+ independent,
254
+ data,
255
+ **options
256
+ )
257
+ end
258
+ 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,143 @@
1
+ require_relative 'core'
2
+
3
+ module Rust::Plots
4
+
5
+ ##
6
+ # Allows to create one or many scatter plots.
7
+
8
+ class ScatterPlot < BasePlot
9
+
10
+ ##
11
+ # Creates a new scatter plot, given two arrays of values +x+ and +y+ for the respective axes (optional).
12
+ # +options+ can be specified and directly passed to the plot function in R.
13
+
14
+ def initialize(x = nil, y = nil, **options)
15
+ super()
16
+ @series = []
17
+ if x && y
18
+ self.series(x, y, **options)
19
+ end
20
+ end
21
+
22
+ ##
23
+ # Adds a new data series, given the values for the +x+ and +y+ axes.
24
+ # +options+ can be specified and directly passed to the plot function in R.
25
+
26
+ def series(x, y, **options)
27
+ @series << [x, y, options]
28
+
29
+ return self
30
+ end
31
+
32
+ ##
33
+ # Sets the thickness of the plot lines.
34
+
35
+ def thickness(t)
36
+ self['lwd'] = t
37
+
38
+ return self
39
+ end
40
+
41
+ ##
42
+ # Changes the plot type to lines.
43
+
44
+ def lines()
45
+ self['type'] = "l"
46
+
47
+ return self
48
+ end
49
+
50
+ ##
51
+ # Changes the plot type to points.
52
+
53
+ def points()
54
+ self['type'] = "p"
55
+
56
+ return self
57
+ end
58
+
59
+ ##
60
+ # Changes the plot type to lines and points.
61
+
62
+ def lines_and_points()
63
+ self['type'] = "b"
64
+
65
+ return self
66
+ end
67
+
68
+ protected
69
+ def _show()
70
+ first = true
71
+ palette = self.palette(@series.size)
72
+ i = 0
73
+
74
+ base_options = {}
75
+ unless @options['xlim']
76
+ x_values = @series.map { |v| v[0] }.flatten
77
+ y_values = @series.map { |v| v[1] }.flatten
78
+
79
+ base_options[:xlim] = [x_values.min, x_values.max]
80
+ base_options[:ylim] = [y_values.min, y_values.max]
81
+ end
82
+
83
+ @series.each do |x, y, options|
84
+ options = options.merge(base_options)
85
+ Rust["plotter.x"] = x
86
+ Rust["plotter.y"] = y
87
+
88
+ function = nil
89
+ if first
90
+ function = Rust::Function.new("plot")
91
+ first = false
92
+ else
93
+ function = Rust::Function.new("lines")
94
+ end
95
+
96
+ augmented_options = {}
97
+ augmented_options['col'] = options[:color] || palette[i]
98
+ augmented_options['xlim'] = options[:xlim] if options[:xlim]
99
+ augmented_options['ylim'] = options[:ylim] if options[:ylim]
100
+
101
+ function.options = self._augmented_options(augmented_options)
102
+ function.arguments << Rust::Variable.new("plotter.x")
103
+ function.arguments << Rust::Variable.new("plotter.y")
104
+
105
+ function.call
106
+
107
+ i += 1
108
+ end
109
+
110
+ return self
111
+ end
112
+ end
113
+
114
+ ##
115
+ # Represents a bar plot in R.
116
+
117
+ class BarPlot < BasePlot
118
+
119
+ ##
120
+ # Creates a new bar plot with the given +bars+ values.
121
+
122
+ def initialize(bars)
123
+ super()
124
+ @bars = bars
125
+ end
126
+
127
+ protected
128
+ def _show()
129
+ Rust["plotter.bars"] = @bars.values
130
+ Rust["plotter.labels"] = @bars.keys
131
+
132
+ Rust._eval("names(plotter.bars) <- plotter.labels")
133
+
134
+ function = Rust::Function.new("barplot")
135
+ function.options = self._augmented_options
136
+ function.arguments << Rust::Variable.new("plotter.bars")
137
+
138
+ function.call
139
+
140
+ return self
141
+ end
142
+ end
143
+ end