rust 0.2 → 0.3
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/lib/rust-basics.rb +126 -0
- data/lib/rust-calls.rb +69 -0
- data/lib/rust-core.rb +303 -99
- data/lib/rust-csv.rb +95 -0
- data/lib/rust-descriptive.rb +3 -3
- data/lib/rust-effsize.rb +14 -14
- data/lib/rust-plots.rb +351 -0
- data/lib/rust-tests.rb +41 -20
- data/lib/rust.rb +5 -0
- metadata +6 -2
data/lib/rust-csv.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require_relative 'rust-core'
|
2
|
+
|
3
|
+
module Rust
|
4
|
+
class CSV
|
5
|
+
def self.read_all(pattern, **options)
|
6
|
+
result = {}
|
7
|
+
Dir.glob(pattern).each do |filename|
|
8
|
+
result[filename] = CSV.read(filename, **options)
|
9
|
+
end
|
10
|
+
return result
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.read(filename, **options)
|
14
|
+
hash = {}
|
15
|
+
labels = nil
|
16
|
+
|
17
|
+
infer_numbers = options.has_key?(:infer_numbers) ? options.delete(:infer_numbers) : true
|
18
|
+
infer_integers = options.delete(:infer_integers)
|
19
|
+
|
20
|
+
::CSV.foreach(filename, **options) do |row|
|
21
|
+
# TODO fix this ugly patch
|
22
|
+
unless options[:headers]
|
23
|
+
options[:headers] = (1..row.size).to_a.map { |e| "X#{e}" }
|
24
|
+
|
25
|
+
return CSV.read(filename, **options)
|
26
|
+
end
|
27
|
+
|
28
|
+
unless labels
|
29
|
+
labels = row.headers
|
30
|
+
labels.each do |label|
|
31
|
+
hash[label] = []
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
labels.each do |label|
|
36
|
+
hash[label] << row[label]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
result = Rust::DataFrame.new(hash)
|
41
|
+
if infer_numbers
|
42
|
+
result = self.auto_infer_types(result, infer_integers)
|
43
|
+
end
|
44
|
+
|
45
|
+
return result
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.write(filename, dataframe, **options)
|
49
|
+
raise TypeError, "Expected Rust::DataFrame" unless dataframe.is_a?(Rust::DataFrame)
|
50
|
+
|
51
|
+
write_headers = options[:headers] != false
|
52
|
+
options[:headers] = dataframe.column_names if options[:headers] == nil
|
53
|
+
|
54
|
+
hash = {}
|
55
|
+
labels = nil
|
56
|
+
::CSV.open(filename, 'w', write_headers: write_headers, **options) do |csv|
|
57
|
+
dataframe.each do |row|
|
58
|
+
csv << row
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
return true
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def self.auto_infer_types(dataframe, auto_infer_integers)
|
67
|
+
integer_columns = []
|
68
|
+
float_columns = []
|
69
|
+
dataframe.column_names.each do |column_name|
|
70
|
+
values = dataframe.column(column_name)
|
71
|
+
|
72
|
+
if values.all? { |s| !!Integer(s) rescue false }
|
73
|
+
integer_columns << column_name
|
74
|
+
elsif values.all? { |s| !!Float(s) rescue false }
|
75
|
+
float_columns << column_name
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
unless auto_infer_integers
|
80
|
+
float_columns += integer_columns
|
81
|
+
integer_columns = []
|
82
|
+
end
|
83
|
+
|
84
|
+
integer_columns.each do |numeric_column|
|
85
|
+
dataframe.transform_column!(numeric_column) { |v| v.to_i }
|
86
|
+
end
|
87
|
+
|
88
|
+
float_columns.each do |numeric_column|
|
89
|
+
dataframe.transform_column!(numeric_column) { |v| v.to_f }
|
90
|
+
end
|
91
|
+
|
92
|
+
return dataframe
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/rust-descriptive.rb
CHANGED
@@ -45,10 +45,10 @@ module Rust::Descriptive
|
|
45
45
|
raise "Percentiles outside the range: #{percentiles}" if percentiles.any? { |e| !e.between?(0, 1) }
|
46
46
|
|
47
47
|
Rust.exclusive do
|
48
|
-
Rust
|
49
|
-
Rust
|
48
|
+
Rust['descriptive.data'] = data
|
49
|
+
Rust['descriptive.percs'] = percentiles
|
50
50
|
|
51
|
-
call_result = Rust._pull("quantile(data, percs)")
|
51
|
+
call_result = Rust._pull("quantile(descriptive.data, descriptive.percs)")
|
52
52
|
assert { call_result.is_a?(Array) }
|
53
53
|
assert { call_result.size == percentiles.size }
|
54
54
|
|
data/lib/rust-effsize.rb
CHANGED
@@ -25,17 +25,17 @@ module Rust::EffectSize::CliffDelta
|
|
25
25
|
raise TypeError, "Expecting Array of numerics" if !d2.is_a?(Array) || !d2.all? { |e| e.is_a?(Numeric) }
|
26
26
|
|
27
27
|
Rust.exclusive do
|
28
|
-
Rust
|
29
|
-
Rust
|
28
|
+
Rust['effsize.a'] = d1
|
29
|
+
Rust['effsize.b'] = d2
|
30
30
|
|
31
|
-
Rust._eval("result = cliff.delta(a, b)")
|
31
|
+
Rust._eval("effsize.result = cliff.delta(effsize.a, effsize.b)")
|
32
32
|
|
33
33
|
result = Rust::EffectSize::Result.new
|
34
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
|
35
|
+
result.estimate = Rust._pull("effsize.result$estimate")
|
36
|
+
result.confidence_interval = Range.new(*Rust._pull("effsize.result$conf.int"))
|
37
|
+
result.confidence_level = Rust._pull("effsize.result$conf.level")
|
38
|
+
result.magnitude = Rust._pull("as.character(effsize.result$magnitude)").to_sym
|
39
39
|
|
40
40
|
return result
|
41
41
|
end
|
@@ -50,17 +50,17 @@ module Rust::EffectSize::CohenD
|
|
50
50
|
raise TypeError, "Expecting Array of numerics" if !d2.is_a?(Array) || !d2.all? { |e| e.is_a?(Numeric) }
|
51
51
|
|
52
52
|
Rust.exclusive do
|
53
|
-
Rust
|
54
|
-
Rust
|
53
|
+
Rust['effsize.a'] = d1
|
54
|
+
Rust['effsize.b'] = d2
|
55
55
|
|
56
|
-
Rust._eval("result = cohen.d(a, b)")
|
56
|
+
Rust._eval("effsize.result = cohen.d(effsize.a, effsize.b)")
|
57
57
|
|
58
58
|
result = Rust::EffectSize::Result.new
|
59
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
|
60
|
+
result.estimate = Rust._pull("effsize.result$estimate")
|
61
|
+
result.confidence_interval = Range.new(*Rust._pull("effsize.result$conf.int"))
|
62
|
+
result.confidence_level = Rust._pull("effsize.result$conf.level")
|
63
|
+
result.magnitude = Rust._pull("as.character(effsize.result$magnitude)").to_sym
|
64
64
|
|
65
65
|
return result
|
66
66
|
end
|
data/lib/rust-plots.rb
ADDED
@@ -0,0 +1,351 @@
|
|
1
|
+
require_relative 'rust-core'
|
2
|
+
require_relative 'rust-calls'
|
3
|
+
|
4
|
+
module Rust::Plots
|
5
|
+
class BasePlot
|
6
|
+
def initialize
|
7
|
+
@plugins = []
|
8
|
+
@options = Rust::Options.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def x_label(label)
|
12
|
+
@options['xlab'] = label
|
13
|
+
|
14
|
+
return self
|
15
|
+
end
|
16
|
+
|
17
|
+
def y_label(label)
|
18
|
+
@options['ylab'] = label
|
19
|
+
|
20
|
+
return self
|
21
|
+
end
|
22
|
+
|
23
|
+
def x_range(range)
|
24
|
+
@options['xlim'] = range
|
25
|
+
|
26
|
+
return self
|
27
|
+
end
|
28
|
+
|
29
|
+
def y_range(range)
|
30
|
+
@options['ylim'] = range
|
31
|
+
|
32
|
+
return self
|
33
|
+
end
|
34
|
+
|
35
|
+
def axis(axis)
|
36
|
+
@options['xaxt'] = 'n'
|
37
|
+
@options['yaxt'] = 'n'
|
38
|
+
|
39
|
+
self.plug(axis)
|
40
|
+
|
41
|
+
return self
|
42
|
+
end
|
43
|
+
|
44
|
+
def title(title)
|
45
|
+
@options['main'] = title
|
46
|
+
|
47
|
+
return self
|
48
|
+
end
|
49
|
+
|
50
|
+
def color(color)
|
51
|
+
@options['col'] = color
|
52
|
+
|
53
|
+
return self
|
54
|
+
end
|
55
|
+
|
56
|
+
def plug(plugin)
|
57
|
+
raise TypeError, "Expected Plugin" unless plugin.is_a?(Plugin)
|
58
|
+
@plugins << plugin
|
59
|
+
|
60
|
+
return self
|
61
|
+
end
|
62
|
+
|
63
|
+
def []=(option, value)
|
64
|
+
@options[option] = value
|
65
|
+
end
|
66
|
+
|
67
|
+
def show()
|
68
|
+
Rust.exclusive do
|
69
|
+
self._show
|
70
|
+
self._run_plugins
|
71
|
+
end
|
72
|
+
|
73
|
+
return self
|
74
|
+
end
|
75
|
+
|
76
|
+
def pdf(path, **options)
|
77
|
+
pdf_function = Rust::Function.new("pdf")
|
78
|
+
pdf_function.options = Rust::Options.from_hash(options)
|
79
|
+
pdf_function.options['file'] = path
|
80
|
+
|
81
|
+
|
82
|
+
Rust.exclusive do
|
83
|
+
pdf_function.call
|
84
|
+
self._show
|
85
|
+
self._run_plugins
|
86
|
+
Rust._eval("dev.off()")
|
87
|
+
end
|
88
|
+
|
89
|
+
return self
|
90
|
+
end
|
91
|
+
|
92
|
+
protected
|
93
|
+
def _show()
|
94
|
+
raise "You are trying to show a BasePlot"
|
95
|
+
end
|
96
|
+
|
97
|
+
def _run_plugins()
|
98
|
+
@plugins.each do |plugin|
|
99
|
+
plugin._run()
|
100
|
+
end
|
101
|
+
|
102
|
+
return self
|
103
|
+
end
|
104
|
+
|
105
|
+
def _augmented_options(options={})
|
106
|
+
result = @options.clone
|
107
|
+
|
108
|
+
options.each do |key, value|
|
109
|
+
result[key] = value
|
110
|
+
end
|
111
|
+
|
112
|
+
result.select! { |k, v| v != nil }
|
113
|
+
|
114
|
+
return result
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class ScatterPlot < BasePlot
|
119
|
+
def initialize(x, y)
|
120
|
+
super()
|
121
|
+
@x = x
|
122
|
+
@y = y
|
123
|
+
end
|
124
|
+
|
125
|
+
def thickness(t)
|
126
|
+
self['lwd'] = t
|
127
|
+
|
128
|
+
return self
|
129
|
+
end
|
130
|
+
|
131
|
+
def lines()
|
132
|
+
self['type'] = "l"
|
133
|
+
|
134
|
+
return self
|
135
|
+
end
|
136
|
+
|
137
|
+
def points()
|
138
|
+
self['type'] = "p"
|
139
|
+
|
140
|
+
return self
|
141
|
+
end
|
142
|
+
|
143
|
+
def lines_and_points()
|
144
|
+
self['type'] = "b"
|
145
|
+
|
146
|
+
return self
|
147
|
+
end
|
148
|
+
|
149
|
+
protected
|
150
|
+
def _show()
|
151
|
+
Rust["plotter.x"] = @x
|
152
|
+
Rust["plotter.y"] = @y
|
153
|
+
|
154
|
+
function = Rust::Function.new("plot")
|
155
|
+
function.options = self._augmented_options
|
156
|
+
function.arguments << Rust::Variable.new("plotter.x")
|
157
|
+
function.arguments << Rust::Variable.new("plotter.y")
|
158
|
+
|
159
|
+
function.call
|
160
|
+
|
161
|
+
return self
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
class DistributionPlot < BasePlot
|
166
|
+
def initialize
|
167
|
+
super()
|
168
|
+
@series = []
|
169
|
+
end
|
170
|
+
|
171
|
+
def series(data, **options)
|
172
|
+
@series << [data, options]
|
173
|
+
|
174
|
+
return self
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
class DensityPlot < DistributionPlot
|
179
|
+
protected
|
180
|
+
def _show()
|
181
|
+
first = true
|
182
|
+
@series.each do |data, options|
|
183
|
+
Rust["plotter.series"] = data
|
184
|
+
|
185
|
+
if first
|
186
|
+
first = false
|
187
|
+
command = "plot"
|
188
|
+
else
|
189
|
+
command = "lines"
|
190
|
+
end
|
191
|
+
|
192
|
+
function = Rust::Function.new(command)
|
193
|
+
function.options = self._augmented_options({"col" => options[:color]})
|
194
|
+
function.arguments << Rust::Variable.new("density(plotter.series)")
|
195
|
+
function.call
|
196
|
+
end
|
197
|
+
|
198
|
+
return self
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
class BoxPlot < DistributionPlot
|
203
|
+
protected
|
204
|
+
def _show()
|
205
|
+
function = Rust::Function.new("boxplot")
|
206
|
+
|
207
|
+
names = []
|
208
|
+
@series.each_with_index do |data, i|
|
209
|
+
series, options = *data
|
210
|
+
varname = "plotter.series#{i}"
|
211
|
+
Rust[varname] = series
|
212
|
+
function.arguments << Rust::Variable.new(varname)
|
213
|
+
names << (options[:name] || (i+1).to_s)
|
214
|
+
end
|
215
|
+
|
216
|
+
function.options = self._augmented_options({'names' => names})
|
217
|
+
|
218
|
+
function.call
|
219
|
+
|
220
|
+
return self
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
class Plugin
|
225
|
+
def initialize
|
226
|
+
@options = Rust::Options.new
|
227
|
+
end
|
228
|
+
|
229
|
+
def []=(option, value)
|
230
|
+
@options[option] = value
|
231
|
+
|
232
|
+
return self
|
233
|
+
end
|
234
|
+
|
235
|
+
protected
|
236
|
+
def _run()
|
237
|
+
raise "You are trying to run an abstract Plugin"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
class Axis < Plugin
|
242
|
+
BELOW = 1
|
243
|
+
LEFT = 2
|
244
|
+
ABOVE = 3
|
245
|
+
RIGHT = 4
|
246
|
+
|
247
|
+
def initialize(side)
|
248
|
+
super()
|
249
|
+
|
250
|
+
self['side'] = side
|
251
|
+
self.at(nil)
|
252
|
+
self.labels(true)
|
253
|
+
end
|
254
|
+
|
255
|
+
def at(values)
|
256
|
+
self['at'] = values
|
257
|
+
|
258
|
+
return self
|
259
|
+
end
|
260
|
+
|
261
|
+
def vertical_labels
|
262
|
+
self['las'] = 2
|
263
|
+
|
264
|
+
return self
|
265
|
+
end
|
266
|
+
|
267
|
+
def horizontal_labels
|
268
|
+
self['las'] = 1
|
269
|
+
|
270
|
+
return self
|
271
|
+
end
|
272
|
+
|
273
|
+
def labels(value)
|
274
|
+
self['labels'] = value
|
275
|
+
|
276
|
+
return self
|
277
|
+
end
|
278
|
+
|
279
|
+
def _run()
|
280
|
+
function = Rust::Function.new("axis")
|
281
|
+
function.options = @options
|
282
|
+
|
283
|
+
function.call
|
284
|
+
|
285
|
+
return self
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
class Grid < Plugin
|
290
|
+
def initialize
|
291
|
+
super()
|
292
|
+
|
293
|
+
@x = Float::NAN
|
294
|
+
@y = Float::NAN
|
295
|
+
end
|
296
|
+
|
297
|
+
def x(value)
|
298
|
+
@x = value
|
299
|
+
|
300
|
+
return self
|
301
|
+
end
|
302
|
+
|
303
|
+
def y(value)
|
304
|
+
@y = value
|
305
|
+
|
306
|
+
return self
|
307
|
+
end
|
308
|
+
|
309
|
+
def auto_x
|
310
|
+
@x = nil
|
311
|
+
|
312
|
+
return self
|
313
|
+
end
|
314
|
+
|
315
|
+
def auto_y
|
316
|
+
@y = nil
|
317
|
+
|
318
|
+
return self
|
319
|
+
end
|
320
|
+
|
321
|
+
def hide_x
|
322
|
+
@x = Float::NAN
|
323
|
+
|
324
|
+
return self
|
325
|
+
end
|
326
|
+
|
327
|
+
def hide_y
|
328
|
+
@y = Float::NAN
|
329
|
+
|
330
|
+
return self
|
331
|
+
end
|
332
|
+
|
333
|
+
def _run()
|
334
|
+
function = Rust::Function.new("grid")
|
335
|
+
|
336
|
+
function.arguments << @x
|
337
|
+
function.arguments << @y
|
338
|
+
function.options = @options
|
339
|
+
|
340
|
+
function.call
|
341
|
+
|
342
|
+
return self
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
module Rust::RBindings
|
348
|
+
def plot(x, y)
|
349
|
+
Rust::Plots::ScatterPlot.new(x, y).show
|
350
|
+
end
|
351
|
+
end
|