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.
- checksums.yaml +4 -4
- data/bin/ruby-rust +3 -0
- data/lib/{rust-csv.rb → rust/core/csv.rb} +2 -1
- data/lib/rust/core/rust.rb +157 -0
- data/lib/rust/core/types/all.rb +4 -0
- data/lib/{rust-core.rb → rust/core/types/dataframe.rb} +17 -335
- data/lib/rust/core/types/datatype.rb +161 -0
- data/lib/rust/core/types/factor.rb +131 -0
- data/lib/rust/core/types/language.rb +166 -0
- data/lib/rust/core/types/list.rb +81 -0
- data/lib/rust/core/types/matrix.rb +132 -0
- data/lib/rust/core/types/s4class.rb +59 -0
- data/lib/rust/core/types/utils.rb +109 -0
- data/lib/rust/core.rb +7 -0
- data/lib/rust/models/all.rb +4 -0
- data/lib/rust/models/anova.rb +60 -0
- data/lib/rust/models/regression.rb +205 -0
- data/lib/rust/plots/all.rb +4 -0
- data/lib/rust/plots/basic-plots.rb +111 -0
- data/lib/{rust-plots.rb → rust/plots/core.rb} +1 -169
- data/lib/rust/plots/distribution-plots.rb +62 -0
- data/lib/rust/stats/all.rb +4 -0
- data/lib/{rust-basics.rb → rust/stats/correlation.rb} +2 -2
- data/lib/{rust-descriptive.rb → rust/stats/descriptive.rb} +24 -4
- data/lib/{rust-effsize.rb → rust/stats/effsize.rb} +7 -13
- data/lib/{rust-probabilities.rb → rust/stats/probabilities.rb} +1 -1
- data/lib/{rust-tests.rb → rust/stats/tests.rb} +84 -90
- data/lib/rust.rb +4 -9
- metadata +31 -13
- data/lib/rust-calls.rb +0 -80
@@ -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,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,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
|