rust 0.11 → 0.13
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/core/csv.rb +4 -4
- data/lib/rust/core/manual.rb +89 -0
- data/lib/rust/core/rust.rb +28 -5
- data/lib/rust/core/types/dataframe.rb +69 -2
- data/lib/rust/core/types/datatype.rb +2 -2
- data/lib/rust/core/types/factor.rb +4 -0
- data/lib/rust/core/types/language.rb +33 -1
- data/lib/rust/core/types/list.rb +2 -0
- data/lib/rust/core/types/matrix.rb +8 -0
- data/lib/rust/core.rb +50 -0
- data/lib/rust/external/ggplot2/core.rb +171 -0
- data/lib/rust/external/ggplot2/geoms.rb +83 -0
- data/lib/rust/external/ggplot2/plot_builder.rb +292 -0
- data/lib/rust/external/ggplot2/scale.rb +12 -0
- data/lib/rust/external/ggplot2/themes.rb +458 -0
- data/lib/rust/external/ggplot2.rb +116 -0
- data/lib/rust/models/regression.rb +113 -10
- data/lib/rust/stats/probabilities.rb +22 -1
- data/lib/rust.rb +19 -0
- metadata +10 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56e795fb0a8893df45abd976e2ed91344156f3c3dd4a68e17afd1a0fb317ece3
|
4
|
+
data.tar.gz: 406416738f1ab84fca06edd5cb59efdc623b12cefe03cd51b1a2cd840e218647
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56854c3ff1bbd64ca8ff9d1201bc16fd37f4d3d465527217ab5c49d5cee0d6f4f34998bdf8a3ebdedf7ea8379909ca0db75d05da1bd46460c3e7066ee882ba7b
|
7
|
+
data.tar.gz: 6b21ba70c7d144384d1647dfa76f8894457bf4d65b74ce32d4e775cc3ecdc660f4596f61edd81e91b08c9d2b3def3491cebe10b7923b9f2988d072f7c5d25674
|
data/lib/rust/core/csv.rb
CHANGED
@@ -90,9 +90,9 @@ module Rust
|
|
90
90
|
dataframe.column_names.each do |column_name|
|
91
91
|
values = dataframe.column(column_name)
|
92
92
|
|
93
|
-
if values.all? { |s| !!Integer(s) rescue false }
|
93
|
+
if values.all? { |s| s == nil || !!Integer(s) rescue false }
|
94
94
|
integer_columns << column_name
|
95
|
-
elsif values.all? { |s| !!Float(s) rescue false }
|
95
|
+
elsif values.all? { |s| s == nil || !!Float(s) rescue false }
|
96
96
|
float_columns << column_name
|
97
97
|
end
|
98
98
|
end
|
@@ -103,11 +103,11 @@ module Rust
|
|
103
103
|
end
|
104
104
|
|
105
105
|
integer_columns.each do |numeric_column|
|
106
|
-
dataframe.transform_column!(numeric_column) { |v| v.to_i }
|
106
|
+
dataframe.transform_column!(numeric_column) { |v| v != nil ? v.to_i : v }
|
107
107
|
end
|
108
108
|
|
109
109
|
float_columns.each do |numeric_column|
|
110
|
-
dataframe.transform_column!(numeric_column) { |v| v.to_f }
|
110
|
+
dataframe.transform_column!(numeric_column) { |v| v != nil ? v.to_f : v }
|
111
111
|
end
|
112
112
|
|
113
113
|
return dataframe
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require_relative 'rust'
|
2
|
+
|
3
|
+
module Rust
|
4
|
+
class Manual
|
5
|
+
@@manuals = {}
|
6
|
+
|
7
|
+
def self.about
|
8
|
+
puts "Manuals available:"
|
9
|
+
@@manuals.each do |category, manual|
|
10
|
+
puts "\t- #{manual.name} (:#{category}) → #{manual.description}"
|
11
|
+
end
|
12
|
+
|
13
|
+
return nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.for(category)
|
17
|
+
category = category.to_sym
|
18
|
+
raise "No manual found for '#{category}'." unless @@manuals[category]
|
19
|
+
|
20
|
+
return @@manuals[category]
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.register(category, name, description)
|
24
|
+
category = category.to_sym
|
25
|
+
|
26
|
+
@@manuals[category] = Manual.new(name, description)
|
27
|
+
|
28
|
+
return nil
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :name
|
32
|
+
attr_reader :description
|
33
|
+
|
34
|
+
def initialize(name, description)
|
35
|
+
@name = name
|
36
|
+
@description = description
|
37
|
+
@voices = {}
|
38
|
+
end
|
39
|
+
|
40
|
+
def lookup(query)
|
41
|
+
@voices.each do |key, value|
|
42
|
+
if query.match(key[1])
|
43
|
+
puts "*** #{key[0]} ***"
|
44
|
+
puts value
|
45
|
+
return
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
puts "Voice not found"
|
50
|
+
|
51
|
+
return nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def n_voices
|
55
|
+
@voices.size
|
56
|
+
end
|
57
|
+
|
58
|
+
def about
|
59
|
+
puts "****** Manual for #@name ******"
|
60
|
+
puts @description
|
61
|
+
puts "Voices in manual #@name:"
|
62
|
+
@voices.keys.each do |key, matcher|
|
63
|
+
puts "\t- #{key}"
|
64
|
+
end
|
65
|
+
|
66
|
+
return nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def register(voice, matcher, description)
|
70
|
+
@voices[[voice, matcher]] = description
|
71
|
+
end
|
72
|
+
|
73
|
+
def inspect
|
74
|
+
return "Manual for #@name with #{self.n_voices} voices"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
module Rust::RBindings
|
80
|
+
def rust_help(category = nil, query = nil)
|
81
|
+
if !category
|
82
|
+
return Rust::Manual.about
|
83
|
+
elsif !query
|
84
|
+
return Rust::Manual.for(category).about
|
85
|
+
else
|
86
|
+
return Rust::Manual.for(category).lookup(query)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/rust/core/rust.rb
CHANGED
@@ -102,7 +102,10 @@ module Rust
|
|
102
102
|
end
|
103
103
|
|
104
104
|
def self._rexec(r_command, return_warnings = false)
|
105
|
-
|
105
|
+
if @@debugging
|
106
|
+
puts "Calling _rexec with command: #{r_command}"
|
107
|
+
puts "\t" + Kernel.caller.select { |v| !v.include?("irb") }.last(3).map { |v| v.sub(/^.*gems\//, "")}.join("\n\t")
|
108
|
+
end
|
106
109
|
R_MUTEX.synchronize do
|
107
110
|
assert("This command must be executed in an exclusive block") { @@in_client_mutex }
|
108
111
|
|
@@ -154,10 +157,17 @@ module Rust
|
|
154
157
|
|
155
158
|
##
|
156
159
|
# Installs the given +name+ library and its dependencies.
|
160
|
+
# +github+ indicates whether the package is in GitHub.
|
157
161
|
|
158
|
-
def self.install_library(name)
|
162
|
+
def self.install_library(name, github = false)
|
163
|
+
self.prerequisite("remotes") if github
|
164
|
+
|
159
165
|
self.exclusive do
|
160
|
-
|
166
|
+
if github
|
167
|
+
self._eval("remotes::install_github(\"#{name}\", dependencies=TRUE)")
|
168
|
+
else
|
169
|
+
self._eval("install.packages(\"#{name}\", dependencies = TRUE)")
|
170
|
+
end
|
161
171
|
end
|
162
172
|
|
163
173
|
return nil
|
@@ -165,9 +175,15 @@ module Rust
|
|
165
175
|
|
166
176
|
##
|
167
177
|
# Installs the +library+ library if it is not available and loads it.
|
178
|
+
# +github+ indicates whether the package appears in GitHub.
|
168
179
|
|
169
|
-
def self.prerequisite(library)
|
170
|
-
|
180
|
+
def self.prerequisite(library, github = false)
|
181
|
+
full_library = library
|
182
|
+
library = library.split("/").last if github
|
183
|
+
|
184
|
+
unless self.check_library(library)
|
185
|
+
self.install_library(full_library, github)
|
186
|
+
end
|
171
187
|
self.load_library(library)
|
172
188
|
end
|
173
189
|
|
@@ -218,4 +234,11 @@ def bind_r!
|
|
218
234
|
include Rust::RBindings
|
219
235
|
end
|
220
236
|
|
237
|
+
##
|
238
|
+
# Shortcut for requiring rust external libraries
|
239
|
+
|
240
|
+
def require_rust(name)
|
241
|
+
require "rust/external/#{name}"
|
242
|
+
end
|
243
|
+
|
221
244
|
bind_r! if ENV['RUBY_RUST_BINDING'] == '1'
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'datatype'
|
2
|
+
require 'tempfile'
|
2
3
|
|
3
4
|
module Rust
|
4
5
|
|
@@ -355,8 +356,25 @@ module Rust
|
|
355
356
|
end
|
356
357
|
|
357
358
|
def load_in_r_as(variable_name)
|
358
|
-
|
359
|
+
tempfile = Tempfile.new('rust.dfport')
|
360
|
+
tempfile.close
|
361
|
+
|
362
|
+
Rust::CSV.write(tempfile.path, self)
|
363
|
+
Rust._eval("#{variable_name} <- read.csv(\"#{tempfile.path}\", header=T)")
|
364
|
+
|
365
|
+
if Rust.debug?
|
366
|
+
FileUtils.cp(tempfile.path, tempfile.path + ".debug.csv")
|
367
|
+
puts "Debug CSV port file available at: #{tempfile.path + ".debug.csv"}"
|
368
|
+
end
|
369
|
+
|
370
|
+
tempfile.unlink
|
359
371
|
|
372
|
+
return true
|
373
|
+
end
|
374
|
+
|
375
|
+
def directly_load_in_r_as(variable_name)
|
376
|
+
command = []
|
377
|
+
|
360
378
|
command << "#{variable_name} <- data.frame()"
|
361
379
|
row_index = 1
|
362
380
|
self.each do |row|
|
@@ -374,6 +392,10 @@ module Rust
|
|
374
392
|
end
|
375
393
|
|
376
394
|
Rust._eval_big(command)
|
395
|
+
|
396
|
+
tempfile.unlink
|
397
|
+
|
398
|
+
return true
|
377
399
|
end
|
378
400
|
|
379
401
|
def inspect
|
@@ -408,16 +430,39 @@ module Rust
|
|
408
430
|
return result
|
409
431
|
end
|
410
432
|
|
433
|
+
##
|
434
|
+
# Merges this data-frame with +other+ in terms of the +by+ column(s) (Array or String). Keeps all the rows in this data frame.
|
435
|
+
# +first_alias+ and +second_alias+ allow to specify the prefix that should be used for the columns not in +by+
|
436
|
+
# for this and the +other+ data-frame, respectively.
|
437
|
+
|
438
|
+
def left_merge(other, by, first_alias, second_alias, **options)
|
439
|
+
options[:keep_right] = true
|
440
|
+
options[:keep_left] = false
|
441
|
+
return other.merge(self, by, first_alias, second_alias, **options)
|
442
|
+
end
|
443
|
+
|
444
|
+
##
|
445
|
+
# Merges this data-frame with +other+ in terms of the +by+ column(s) (Array or String). Keeps all the rows in the other data frame.
|
446
|
+
# +first_alias+ and +second_alias+ allow to specify the prefix that should be used for the columns not in +by+
|
447
|
+
# for this and the +other+ data-frame, respectively.
|
448
|
+
|
449
|
+
def right_merge(other, by, first_alias, second_alias, **options)
|
450
|
+
options[:keep_right] = true
|
451
|
+
options[:keep_left] = false
|
452
|
+
return self.merge(other, by, first_alias, second_alias, **options)
|
453
|
+
end
|
454
|
+
|
411
455
|
##
|
412
456
|
# Merges this data-frame with +other+ in terms of the +by+ column(s) (Array or String).
|
413
457
|
# +first_alias+ and +second_alias+ allow to specify the prefix that should be used for the columns not in +by+
|
414
458
|
# for this and the +other+ data-frame, respectively.
|
415
459
|
|
416
|
-
def merge(other, by, first_alias = "x", second_alias = "y")
|
460
|
+
def merge(other, by, first_alias = "x", second_alias = "y", **options)
|
417
461
|
raise TypeError, "Expected Rust::DataFrame" unless other.is_a?(DataFrame)
|
418
462
|
raise TypeError, "Expected list of strings" if !by.is_a?(Array) || !by.all? { |e| e.is_a?(String) }
|
419
463
|
raise "This dataset should have all the columns in #{by}" unless (by & self.column_names).size == by.size
|
420
464
|
raise "The passed dataset should have all the columns in #{by}" unless (by & other.column_names).size == by.size
|
465
|
+
raise "Either keep_right or keep_left should be provided as options, not both" if options[:keep_right] && options[:keep_left]
|
421
466
|
|
422
467
|
if first_alias == second_alias
|
423
468
|
if first_alias == ""
|
@@ -473,6 +518,28 @@ module Rust
|
|
473
518
|
end
|
474
519
|
|
475
520
|
result << to_add
|
521
|
+
|
522
|
+
elsif options[:keep_right]
|
523
|
+
to_add = {}
|
524
|
+
|
525
|
+
by.each do |colname|
|
526
|
+
to_add[colname] = other_row[colname]
|
527
|
+
end
|
528
|
+
|
529
|
+
merged_column_self.each do |colname|
|
530
|
+
to_add["#{first_alias}#{colname}"] = nil
|
531
|
+
end
|
532
|
+
|
533
|
+
merged_column_other.each do |colname|
|
534
|
+
to_add["#{second_alias}#{colname}"] = other_row[colname]
|
535
|
+
end
|
536
|
+
|
537
|
+
result << to_add
|
538
|
+
|
539
|
+
elsif options[:keep_left]
|
540
|
+
options[:keep_left] = false
|
541
|
+
options[:keep_right] = true
|
542
|
+
return other.merge(self, by, first_alias, second_alias, **options)
|
476
543
|
end
|
477
544
|
end
|
478
545
|
|
@@ -36,7 +36,7 @@ module Rust
|
|
36
36
|
if candidates.size > 0
|
37
37
|
type = candidates.max_by { |c| c.pull_priority }
|
38
38
|
|
39
|
-
puts "Using #{type} to pull #{variable}" if Rust.debug?
|
39
|
+
puts "Using #{type} to pull #{variable} (candidates: #{candidates.map { |c| c.to_s + "=>" + c.pull_priority.to_s}.join(", ")})" if Rust.debug?
|
40
40
|
return type.pull_variable(variable, r_type, r_class)
|
41
41
|
else
|
42
42
|
if Rust._pull("length(#{variable})") == 0
|
@@ -80,7 +80,7 @@ module Rust
|
|
80
80
|
def r_mirror
|
81
81
|
varname = self.mirrored_R_variable_name
|
82
82
|
|
83
|
-
if !Rust._pull("exists(\"#{varname}\")") || Rust
|
83
|
+
if !Rust._pull("exists(\"#{varname}\")") || Rust["#{varname}.hash"] != self.r_hash
|
84
84
|
puts "Loading #{varname}" if Rust.debug?
|
85
85
|
Rust[varname] = self
|
86
86
|
Rust["#{varname}.hash"] = self.r_hash
|
@@ -137,7 +137,7 @@ module Rust
|
|
137
137
|
# Sets the +arguments+ (Arguments type) of the function.
|
138
138
|
|
139
139
|
def arguments=(arguments)
|
140
|
-
raise TypeError, "Expected Arguments" unless
|
140
|
+
raise TypeError, "Expected Arguments" unless arguments.is_a?(Arguments)
|
141
141
|
|
142
142
|
@arguments = arguments
|
143
143
|
end
|
@@ -171,6 +171,22 @@ module Rust
|
|
171
171
|
end
|
172
172
|
end
|
173
173
|
|
174
|
+
##
|
175
|
+
# Represents a verbatim R expression
|
176
|
+
|
177
|
+
class Verbatim
|
178
|
+
##
|
179
|
+
#Creates a verbatim R expression
|
180
|
+
|
181
|
+
def initialize(expression)
|
182
|
+
@expression = expression
|
183
|
+
end
|
184
|
+
|
185
|
+
def to_R
|
186
|
+
@expression
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
174
190
|
##
|
175
191
|
# Represents the arguments of a function in R. Works as an Array of objects.
|
176
192
|
|
@@ -196,4 +212,20 @@ module Rust
|
|
196
212
|
return options
|
197
213
|
end
|
198
214
|
end
|
215
|
+
|
216
|
+
def self.verbatim(expression)
|
217
|
+
Verbatim.new(expression)
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.variable(variable)
|
221
|
+
Variable.new(variable)
|
222
|
+
end
|
223
|
+
|
224
|
+
def self.function(name)
|
225
|
+
Function.new(name)
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.formula(left_part, right_part)
|
229
|
+
Formula.new(left_part, right_part)
|
230
|
+
end
|
199
231
|
end
|
data/lib/rust/core/types/list.rb
CHANGED
data/lib/rust/core.rb
CHANGED
@@ -1,7 +1,57 @@
|
|
1
1
|
require_relative 'core/rust'
|
2
2
|
require_relative 'core/csv'
|
3
|
+
require_relative 'core/manual'
|
3
4
|
|
4
5
|
self_path = File.expand_path(__FILE__)
|
5
6
|
Dir.glob(File.join(File.dirname(self_path), "core/types/*.rb")).each do |lib|
|
6
7
|
require_relative lib
|
7
8
|
end
|
9
|
+
|
10
|
+
Rust::Manual.register(:base, "Quick intro", "Core philosophy behind Rust.")
|
11
|
+
Rust::Manual.for(:base).register('Introduction', /intro/,
|
12
|
+
<<-EOS
|
13
|
+
Rust is a statistical library. Rust wraps R and its libraries to achieve this goal.
|
14
|
+
Rust aims at:
|
15
|
+
- Making easier for Ruby developers make all the kinds of operations that are straightforward in R;
|
16
|
+
- Providing an object-oriented interface, more familiar than the one in R.
|
17
|
+
|
18
|
+
Rust can be used in two ways:
|
19
|
+
- By using the object-oriented interface (advised if you are writing a script);
|
20
|
+
- By using the R bindings, that allow to use Ruby pretty much like R (handful if you are using it from IRB).
|
21
|
+
|
22
|
+
Rust provides wrappers for many elements, including types (e.g., data frames), statistical hypothesis tests, plots, and so on.
|
23
|
+
Under the hood, Rust creates an R environment (through rinruby), through which Rust can perform the most advanced operations,
|
24
|
+
for which a re-implementation would be impractical.
|
25
|
+
EOS
|
26
|
+
)
|
27
|
+
|
28
|
+
Rust::Manual.for(:base).register('Types', /type/,
|
29
|
+
<<-EOS
|
30
|
+
Rust provides wrappers for the most commonly-found types in R. Specifically, the following types are available:
|
31
|
+
- Data frames → Rust::DataFrame
|
32
|
+
- Factors → Rust::Factor
|
33
|
+
- Matrices → Rust::Matrix
|
34
|
+
- Lists → Rust::List
|
35
|
+
- S4 classes → Rust::S4Class
|
36
|
+
- Formulas → Rust::Formula
|
37
|
+
|
38
|
+
Note that some of them (e.g., data frames and matrices) are not just wrappers, but complete re-implementations of the R
|
39
|
+
types (for performance reasons).
|
40
|
+
EOS
|
41
|
+
)
|
42
|
+
|
43
|
+
Rust::Manual.for(:base).register('CSVs', /csv/,
|
44
|
+
<<-EOS
|
45
|
+
Rust allows to read and write CSV files, mostly like in R.
|
46
|
+
To read a CSV file, you can use:
|
47
|
+
Rust::CSV.read(filename)
|
48
|
+
|
49
|
+
It returns a data frame. You can also specify the option "headers" to tell if the first row in the CSV contains the headers
|
50
|
+
(column names for the data frame). Other options get directly passed to the R function "read.csv".
|
51
|
+
|
52
|
+
To write a CSV file, you can use:
|
53
|
+
Rust::CSV.write(filename, data_frame)
|
54
|
+
|
55
|
+
It writes the given data frame on the file at filename.
|
56
|
+
EOS
|
57
|
+
)
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require_relative '../../../rust'
|
2
|
+
|
3
|
+
Rust.prerequisite("ggplot2")
|
4
|
+
|
5
|
+
module Rust::Plots::GGPlot
|
6
|
+
def self.default_theme
|
7
|
+
@@theme
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.default_theme=(theme)
|
11
|
+
@@theme = theme.freeze
|
12
|
+
end
|
13
|
+
|
14
|
+
class Layer
|
15
|
+
def initialize(function_name, **options)
|
16
|
+
@function_name = function_name
|
17
|
+
@arguments = Rust::Arguments.new
|
18
|
+
@options = Rust::Options.from_hash(options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def option(key, value)
|
22
|
+
@options[key] = value
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_R
|
26
|
+
if !block_given?
|
27
|
+
options, arguments = @options, @arguments
|
28
|
+
else
|
29
|
+
options, arguments = yield(@options, @arguments)
|
30
|
+
end
|
31
|
+
|
32
|
+
options = Rust::Options.from_hash(options) unless options.is_a?(Rust::Options)
|
33
|
+
|
34
|
+
function = Rust::Function.new(@function_name)
|
35
|
+
function.arguments = arguments if arguments
|
36
|
+
function.options = options if options
|
37
|
+
return function.to_R
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Aes
|
42
|
+
def initialize(**options)
|
43
|
+
options = options.map { |k, v| [k, v.is_a?(Symbol) ? Rust::Variable.new(v) : v] }.to_h
|
44
|
+
@options = Rust::Options.from_hash(options)
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_R
|
48
|
+
function = Rust::Function.new("aes")
|
49
|
+
function.options = @options if @options
|
50
|
+
return function.to_R
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Plot
|
55
|
+
attr_accessor :theme
|
56
|
+
attr_accessor :aes
|
57
|
+
|
58
|
+
def initialize(data, aes = nil)
|
59
|
+
@layers = []
|
60
|
+
|
61
|
+
@data = data
|
62
|
+
@aes = aes
|
63
|
+
@theme = Rust::Plots::GGPlot.default_theme
|
64
|
+
end
|
65
|
+
|
66
|
+
def layer(layer)
|
67
|
+
@layers << layer
|
68
|
+
end
|
69
|
+
|
70
|
+
def show()
|
71
|
+
Rust.exclusive do
|
72
|
+
dataset_name = nil
|
73
|
+
if @data
|
74
|
+
dataset_name = "ggplotter.data"
|
75
|
+
@data.load_in_r_as(dataset_name)
|
76
|
+
end
|
77
|
+
r = self.to_R(dataset_name)
|
78
|
+
Rust._eval(r)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def save(filename, **options)
|
83
|
+
Rust.exclusive do
|
84
|
+
dataset_name = nil
|
85
|
+
if @data
|
86
|
+
dataset_name = "ggplotter.data"
|
87
|
+
@data.load_in_r_as(dataset_name)
|
88
|
+
end
|
89
|
+
r = self.to_R(dataset_name)
|
90
|
+
Rust._eval("ggplot.latest <- #{r}")
|
91
|
+
save = Rust::Function.new("ggsave")
|
92
|
+
save.arguments = Rust::Arguments.new([Rust::Variable.new("ggplot.latest")])
|
93
|
+
save.options = Rust::Options.from_hash({file: filename}.merge(options))
|
94
|
+
save.call
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_R(data_set_name="ggplotter.data")
|
99
|
+
function = Rust::Function.new("ggplot")
|
100
|
+
function.arguments = Rust::Arguments.new
|
101
|
+
function.arguments << (data_set_name ? Rust::Variable.new(data_set_name) : nil)
|
102
|
+
function.arguments << @aes if @aes
|
103
|
+
|
104
|
+
result = function.to_R
|
105
|
+
result += " + " + @theme.to_R
|
106
|
+
@layers.each do |layer|
|
107
|
+
result += " + " + layer.to_R
|
108
|
+
end
|
109
|
+
|
110
|
+
return result
|
111
|
+
end
|
112
|
+
|
113
|
+
def <<(others)
|
114
|
+
if others.is_a?(Array) && others.all? { |o| o.is_a?(Layer) }
|
115
|
+
@layers += others
|
116
|
+
elsif others.is_a?(Layer)
|
117
|
+
@layers << others
|
118
|
+
else
|
119
|
+
raise ArgumentError, "Expected Layer or Array of Layers"
|
120
|
+
end
|
121
|
+
|
122
|
+
return self
|
123
|
+
end
|
124
|
+
|
125
|
+
def +(others)
|
126
|
+
copy = self.deep_clone
|
127
|
+
copy << others
|
128
|
+
return copy
|
129
|
+
end
|
130
|
+
|
131
|
+
def inspect(show = true)
|
132
|
+
self.show if show
|
133
|
+
return super()
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class Labels < Layer
|
138
|
+
def initialize(**options)
|
139
|
+
super("labs", **options)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class FlipCoordinates < Layer
|
144
|
+
def initialize(**options)
|
145
|
+
super("coord_flip", **options)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
module Rust::RBindings
|
151
|
+
def ggplot(*arguments)
|
152
|
+
Rust::Plots::GGPlot::Plot.new(*arguments)
|
153
|
+
end
|
154
|
+
|
155
|
+
def aes(**options)
|
156
|
+
Rust::Plots::GGPlot::Aes.new(**options)
|
157
|
+
end
|
158
|
+
|
159
|
+
def labs(**options)
|
160
|
+
Rust::Plots::GGPlot::Labels.new(**options)
|
161
|
+
end
|
162
|
+
alias :labels :labs
|
163
|
+
|
164
|
+
def coord_flip(**options)
|
165
|
+
Rust::Plots::GGPlot::FlipCoordinates.new(**options)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def bind_ggplot!
|
170
|
+
include Rust::Plots::GGPlot
|
171
|
+
end
|