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