rust 0.12 → 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 +32 -0
- 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/plot_builder.rb +112 -8
- data/lib/rust/external/ggplot2/scale.rb +12 -0
- data/lib/rust/external/ggplot2/themes.rb +26 -3
- data/lib/rust/external/ggplot2.rb +112 -1
- data/lib/rust/models/regression.rb +113 -10
- data/lib/rust/stats/probabilities.rb +22 -1
- metadata +5 -4
- data/lib/rust/external/ggplot2/helper.rb +0 -122
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
|
@@ -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
|
+
)
|
@@ -64,14 +64,6 @@ module Rust::Plots::GGPlot
|
|
64
64
|
return self
|
65
65
|
end
|
66
66
|
|
67
|
-
def labeled(value)
|
68
|
-
raise "No context for assigning a label" unless @current_context
|
69
|
-
@label_options[@current_context] = value
|
70
|
-
@current_context = nil
|
71
|
-
|
72
|
-
return self
|
73
|
-
end
|
74
|
-
|
75
67
|
def with_x_label(value)
|
76
68
|
@label_options[:x] = value
|
77
69
|
|
@@ -90,6 +82,76 @@ module Rust::Plots::GGPlot
|
|
90
82
|
return self
|
91
83
|
end
|
92
84
|
|
85
|
+
def scale_x_continuous(**options)
|
86
|
+
raise "No context for assigning a label" unless @current_context
|
87
|
+
@layers << AxisScaler.new(:x, :continuous, **options)
|
88
|
+
|
89
|
+
return self
|
90
|
+
end
|
91
|
+
|
92
|
+
def scale_y_continuous(**options)
|
93
|
+
raise "No context for assigning a label" unless @current_context
|
94
|
+
@layers << AxisScaler.new(:y, :continuous, **options)
|
95
|
+
|
96
|
+
return self
|
97
|
+
end
|
98
|
+
|
99
|
+
def scale_x_discrete(**options)
|
100
|
+
raise "No context for assigning a label" unless @current_context
|
101
|
+
@layers << AxisScaler.new(:x, :discrete, **options)
|
102
|
+
|
103
|
+
return self
|
104
|
+
end
|
105
|
+
|
106
|
+
def scale_y_discrete(**options)
|
107
|
+
raise "No context for assigning a label" unless @current_context
|
108
|
+
@layers << AxisScaler.new(:y, :discrete, **options)
|
109
|
+
|
110
|
+
return self
|
111
|
+
end
|
112
|
+
|
113
|
+
def scale_x_log10(**options)
|
114
|
+
raise "No context for assigning a label" unless @current_context
|
115
|
+
@layers << AxisScaler.new(:x, :log10, **options)
|
116
|
+
|
117
|
+
return self
|
118
|
+
end
|
119
|
+
|
120
|
+
def scale_y_log10(**options)
|
121
|
+
raise "No context for assigning a label" unless @current_context
|
122
|
+
@layers << AxisScaler.new(:y, :log10, **options)
|
123
|
+
|
124
|
+
return self
|
125
|
+
end
|
126
|
+
|
127
|
+
def scale_x_reverse(**options)
|
128
|
+
raise "No context for assigning a label" unless @current_context
|
129
|
+
@layers << AxisScaler.new(:x, :reverse, **options)
|
130
|
+
|
131
|
+
return self
|
132
|
+
end
|
133
|
+
|
134
|
+
def scale_y_reverse(**options)
|
135
|
+
raise "No context for assigning a label" unless @current_context
|
136
|
+
@layers << AxisScaler.new(:y, :reverse, **options)
|
137
|
+
|
138
|
+
return self
|
139
|
+
end
|
140
|
+
|
141
|
+
def scale_x_sqrt(**options)
|
142
|
+
raise "No context for assigning a label" unless @current_context
|
143
|
+
@layers << AxisScaler.new(:x, :sqrt, **options)
|
144
|
+
|
145
|
+
return self
|
146
|
+
end
|
147
|
+
|
148
|
+
def scale_y_sqrt(**options)
|
149
|
+
raise "No context for assigning a label" unless @current_context
|
150
|
+
@layers << AxisScaler.new(:y, :sqrt, **options)
|
151
|
+
|
152
|
+
return self
|
153
|
+
end
|
154
|
+
|
93
155
|
def with_title(value)
|
94
156
|
@label_options[:title] = value
|
95
157
|
|
@@ -160,6 +222,48 @@ module Rust::Plots::GGPlot
|
|
160
222
|
return self
|
161
223
|
end
|
162
224
|
|
225
|
+
def labeled(value)
|
226
|
+
raise "No context for assigning a label" unless @current_context
|
227
|
+
@label_options[@current_context] = value
|
228
|
+
|
229
|
+
return self
|
230
|
+
end
|
231
|
+
|
232
|
+
def scale_continuous(**options)
|
233
|
+
raise "No context for assigning a label" unless @current_context
|
234
|
+
@layers << AxisScaler.new(@current_context, :continuous, **options)
|
235
|
+
|
236
|
+
return self
|
237
|
+
end
|
238
|
+
|
239
|
+
def scale_discrete(**options)
|
240
|
+
raise "No context for assigning a label" unless @current_context
|
241
|
+
@layers << AxisScaler.new(@current_context, :discrete, **options)
|
242
|
+
|
243
|
+
return self
|
244
|
+
end
|
245
|
+
|
246
|
+
def scale_log10(**options)
|
247
|
+
raise "No context for assigning a label" unless @current_context
|
248
|
+
@layers << AxisScaler.new(@current_context, :log10, **options)
|
249
|
+
|
250
|
+
return self
|
251
|
+
end
|
252
|
+
|
253
|
+
def scale_reverse(**options)
|
254
|
+
raise "No context for assigning a label" unless @current_context
|
255
|
+
@layers << AxisScaler.new(@current_context, :reverse, **options)
|
256
|
+
|
257
|
+
return self
|
258
|
+
end
|
259
|
+
|
260
|
+
def scale_sqrt(**options)
|
261
|
+
raise "No context for assigning a label" unless @current_context
|
262
|
+
@layers << AxisScaler.new(@current_context, :sqrt, **options)
|
263
|
+
|
264
|
+
return self
|
265
|
+
end
|
266
|
+
|
163
267
|
def flip_coordinates
|
164
268
|
@layers << FlipCoordinates.new
|
165
269
|
|
@@ -53,7 +53,7 @@ module Rust::Plots::GGPlot
|
|
53
53
|
def to_h
|
54
54
|
options = @options.clone
|
55
55
|
|
56
|
-
options['_starting'] = @starting.sub("theme_", "")
|
56
|
+
options['_starting'] = @starting.sub("theme_", "") if @starting
|
57
57
|
options = options.map do |key, value|
|
58
58
|
[key, value.is_a?(Theme::Element) ? value.to_h : value]
|
59
59
|
end.to_h
|
@@ -78,6 +78,9 @@ module Rust::Plots::GGPlot
|
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
|
+
class ExistingTheme < Layer
|
82
|
+
end
|
83
|
+
|
81
84
|
class Theme::Element
|
82
85
|
attr_reader :options
|
83
86
|
|
@@ -152,6 +155,8 @@ module Rust::Plots::GGPlot
|
|
152
155
|
return value
|
153
156
|
elsif value.is_a?(Hash)
|
154
157
|
return Theme::LineElement.new(**value)
|
158
|
+
elsif !value
|
159
|
+
return Theme::BlankElement.new
|
155
160
|
else
|
156
161
|
raise "Expected line or hash"
|
157
162
|
end
|
@@ -162,6 +167,8 @@ module Rust::Plots::GGPlot
|
|
162
167
|
return value
|
163
168
|
elsif value.is_a?(Hash)
|
164
169
|
return Theme::RectElement.new(**value)
|
170
|
+
elsif !value
|
171
|
+
return Theme::BlankElement.new
|
165
172
|
else
|
166
173
|
raise "Expected rect or hash"
|
167
174
|
end
|
@@ -172,6 +179,8 @@ module Rust::Plots::GGPlot
|
|
172
179
|
return value
|
173
180
|
elsif value.is_a?(Hash)
|
174
181
|
return Theme::TextElement.new(**value)
|
182
|
+
elsif !value
|
183
|
+
return Theme::BlankElement.new
|
175
184
|
else
|
176
185
|
raise "Expected text or hash"
|
177
186
|
end
|
@@ -225,7 +234,7 @@ module Rust::Plots::GGPlot
|
|
225
234
|
end
|
226
235
|
|
227
236
|
class ThemeBuilder < ThemeComponentBuilder
|
228
|
-
def initialize(starting =
|
237
|
+
def initialize(starting = nil)
|
229
238
|
super("plot")
|
230
239
|
@starting = starting
|
231
240
|
end
|
@@ -417,7 +426,21 @@ module Rust::Plots::GGPlot
|
|
417
426
|
end
|
418
427
|
end
|
419
428
|
|
420
|
-
|
429
|
+
class ThemeCollection
|
430
|
+
def self.ggtech(name = "google")
|
431
|
+
Rust.prerequisite("ricardo-bion/ggtech", true)
|
432
|
+
|
433
|
+
return ExistingTheme.new("theme_tech", theme: name)
|
434
|
+
end
|
435
|
+
|
436
|
+
def self.ggdark(style = "classic")
|
437
|
+
Rust.prerequisite("ggdark")
|
438
|
+
|
439
|
+
return ExistingTheme.new("dark_theme_#{style}")
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
self.default_theme = ThemeBuilder.new("bw").
|
421
444
|
title(face: 'bold', size: 12).
|
422
445
|
legend do |legend|
|
423
446
|
legend.background(fill: 'white', size: 4, colour: 'white')
|
@@ -2,4 +2,115 @@ require_relative 'ggplot2/core'
|
|
2
2
|
require_relative 'ggplot2/geoms'
|
3
3
|
require_relative 'ggplot2/themes'
|
4
4
|
require_relative 'ggplot2/plot_builder'
|
5
|
-
require_relative 'ggplot2/
|
5
|
+
require_relative 'ggplot2/scale'
|
6
|
+
|
7
|
+
Rust::Manual.register(:ggplot2, "ggplot2", "Informations on the wrapper of the popular ggplot2 plotting library for R.")
|
8
|
+
|
9
|
+
Rust::Manual.for(:ggplot2).register("Introduction", /intro/,
|
10
|
+
<<-EOS
|
11
|
+
bind_ggplot! # Avoid using long module names to reach Rust::Plots::GGPlot (simply includes this module)
|
12
|
+
|
13
|
+
# Best with a dataframe, but not necessary. If you have it...
|
14
|
+
df = Rust.toothgrowth
|
15
|
+
plot = PlotBuilder.for_dataframe(df). # Use a dataframe (symbols will be variable names)
|
16
|
+
labeled("Example plot"). # "labeled" sets the label to the last set aesthetic item (x, y, or title, in this case)
|
17
|
+
with_x(:len).labeled("X data from df"). # Set all the aesthetics (x, y, ...)
|
18
|
+
with_y(:dose).labeled("Y data from df").
|
19
|
+
draw_points. # Set the geometries to plot (based on the plot type)
|
20
|
+
build # Returns the plot ready to use
|
21
|
+
plot.show # Show the plot in a window
|
22
|
+
plot.save("output.pdf", width: 5, height: 4) # Save the plot, width, height etc. are optional
|
23
|
+
|
24
|
+
# If you don't have a dataframe...
|
25
|
+
plot2 = PlotBuilder.new.
|
26
|
+
with_x([1,2,3]).labeled("X data from df").
|
27
|
+
with_y([3,4,5]).labeled("Y data from df").
|
28
|
+
draw_points.
|
29
|
+
build
|
30
|
+
plot2.show
|
31
|
+
EOS
|
32
|
+
)
|
33
|
+
|
34
|
+
Rust::Manual.for(:ggplot2).register("Scatter plots", /scatter/,
|
35
|
+
<<-EOS
|
36
|
+
bind_ggplot!
|
37
|
+
df = Rust.toothgrowth
|
38
|
+
plot = PlotBuilder.for_dataframe(df).
|
39
|
+
with_x(:len).labeled("X data").
|
40
|
+
with_y(:dose).labeled("Y data").
|
41
|
+
draw_points. # To draw points
|
42
|
+
draw_lines. # To draw lines (keep both to draw both)
|
43
|
+
build
|
44
|
+
plot.show
|
45
|
+
EOS
|
46
|
+
)
|
47
|
+
|
48
|
+
Rust::Manual.for(:ggplot2).register("Bar plots", /bar/,
|
49
|
+
<<-EOS
|
50
|
+
bind_ggplot!
|
51
|
+
df = Rust.toothgrowth
|
52
|
+
plot = PlotBuilder.for_dataframe(df).
|
53
|
+
with_x(:len).labeled("X data").
|
54
|
+
with_fill(:supp).labeled("Legend"). # Use with_fill or with_color for stacked plots
|
55
|
+
draw_bars. # To draw bars
|
56
|
+
build
|
57
|
+
plot.show
|
58
|
+
EOS
|
59
|
+
)
|
60
|
+
|
61
|
+
Rust::Manual.for(:ggplot2).register("Box plots", /box/,
|
62
|
+
<<-EOS
|
63
|
+
bind_ggplot!
|
64
|
+
df = Rust.toothgrowth
|
65
|
+
plot = PlotBuilder.for_dataframe(df).
|
66
|
+
with_y(:len).labeled("Data to boxplot").
|
67
|
+
with_group(:supp).labeled("Groups"). # Groups to plot
|
68
|
+
draw_boxplot.
|
69
|
+
build
|
70
|
+
plot.show
|
71
|
+
EOS
|
72
|
+
)
|
73
|
+
|
74
|
+
Rust::Manual.for(:ggplot2).register("Histograms", /hist/,
|
75
|
+
<<-EOS
|
76
|
+
bind_ggplot!
|
77
|
+
df = Rust.toothgrowth
|
78
|
+
plot = PlotBuilder.for_dataframe(df).
|
79
|
+
with_x(:len).labeled("Data to plot").
|
80
|
+
with_fill(:supp).labeled("Color"). # Use with_fill or with_color for multiple plots
|
81
|
+
draw_histogram.
|
82
|
+
build
|
83
|
+
plot.show
|
84
|
+
EOS
|
85
|
+
)
|
86
|
+
|
87
|
+
Rust::Manual.for(:ggplot2).register("Themes", /them/,
|
88
|
+
<<-EOS
|
89
|
+
bind_ggplot!
|
90
|
+
df = Rust.toothgrowth
|
91
|
+
# The method with_theme allows to change theme options. The method can be called
|
92
|
+
# several times, each time the argument does not overwrite the previous options,
|
93
|
+
# unless they are specified again (in that case, the last specified ones win).
|
94
|
+
plot = PlotBuilder.for_dataframe(df).
|
95
|
+
with_x(:len).labeled("X data").
|
96
|
+
with_y(:dose).labeled("Y data").
|
97
|
+
draw_points.
|
98
|
+
with_theme(
|
99
|
+
ThemeBuilder.new('bw').
|
100
|
+
title(face: 'bold', size: 12). # Each method sets the property for the related element
|
101
|
+
legend do |legend| # Legend and other parts can be set like this
|
102
|
+
legend.position(:left) # Puts the legend on the left
|
103
|
+
end.
|
104
|
+
axis do |axis| # Modifies the axes
|
105
|
+
axis.line(Theme::BlankElement.new) # Hides the lines for the axes
|
106
|
+
axis.text_x(size: 3) # X axis labels
|
107
|
+
end.
|
108
|
+
panel do |panel|
|
109
|
+
panel.grid_major(colour: 'grey70', size: 0.2) # Sets the major ticks grid
|
110
|
+
panel.grid_minor(Theme::BlankElement.new) # Hides the minor ticks grid
|
111
|
+
end.
|
112
|
+
build
|
113
|
+
).build
|
114
|
+
plot.show
|
115
|
+
EOS
|
116
|
+
)
|
@@ -14,6 +14,11 @@ module Rust::Models::Regression
|
|
14
14
|
# Generic regression model in R.
|
15
15
|
|
16
16
|
class RegressionModel < Rust::RustDatatype
|
17
|
+
|
18
|
+
attr_accessor :data
|
19
|
+
attr_accessor :dependent_variable
|
20
|
+
attr_accessor :options
|
21
|
+
|
17
22
|
def self.can_pull?(type, klass)
|
18
23
|
# Can only pull specific sub-types
|
19
24
|
return false
|
@@ -38,22 +43,30 @@ module Rust::Models::Regression
|
|
38
43
|
|
39
44
|
formula = Rust::Formula.new(dependent_variable, independent_variables.join(" + "))
|
40
45
|
|
46
|
+
result = nil
|
41
47
|
Rust.exclusive do
|
42
48
|
Rust["#{model_type}.data"] = data
|
43
49
|
|
44
50
|
Rust._eval("#{model_type}.model.result <- #{model_type}(#{formula.to_R}, data=#{model_type}.data#{mapped})")
|
45
51
|
result = Rust["#{model_type}.model.result"]
|
46
|
-
result.r_mirror_to("#{model_type}.model.result")
|
47
52
|
|
48
|
-
|
53
|
+
raise "An error occurred while building the model" unless result
|
54
|
+
|
55
|
+
result.r_mirror_to("#{model_type}.model.result")
|
49
56
|
end
|
57
|
+
|
58
|
+
result.dependent_variable = dependent_variable
|
59
|
+
result.data = data
|
60
|
+
result.options = options
|
61
|
+
|
62
|
+
return result
|
50
63
|
end
|
51
64
|
|
52
65
|
##
|
53
|
-
# Creates a new +model+.
|
66
|
+
# Creates a new model based on +model+.
|
54
67
|
|
55
68
|
def initialize(model)
|
56
|
-
raise
|
69
|
+
raise "Expected a R list, given a #{model.class}" if !model.is_a?(Rust::List)
|
57
70
|
@model = model
|
58
71
|
end
|
59
72
|
|
@@ -118,6 +131,58 @@ module Rust::Models::Regression
|
|
118
131
|
a = self.summary|"coefficients"
|
119
132
|
end
|
120
133
|
|
134
|
+
##
|
135
|
+
# Returns object variables for the model with basic data (coefficients and p-values). Use the method `coefficients`
|
136
|
+
# to get more data.
|
137
|
+
|
138
|
+
def variables
|
139
|
+
unless @variables
|
140
|
+
coefficients = self.coefficients
|
141
|
+
|
142
|
+
@variables = coefficients.rownames.map do |name|
|
143
|
+
ModelVariable.new(name, coefficients[name, "Estimate"], coefficients[name, "Pr(>|t|)"])
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
return @variables
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Returns only the significant variables as ModelVariable instances. See the method `variables`.
|
152
|
+
|
153
|
+
def significant_variables(a = 0.05)
|
154
|
+
self.variables.select { |v| v.significant?(a) }
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# Runs backward selection (recursively removes a variable until the best model is found).
|
159
|
+
# Returns both the best model and the list of excluded variable at each step
|
160
|
+
# Note: Not fully tested
|
161
|
+
|
162
|
+
def backward_selection(excluded = [])
|
163
|
+
candidates = self.variables.select { |v| !v.intercept? && !v.significant? }.sort_by { |v| v.pvalue }.reverse
|
164
|
+
all = self.variables.select { |v| !v.intercept? }
|
165
|
+
|
166
|
+
candidates.each do |candidate|
|
167
|
+
new_model = RegressionModel.generate(
|
168
|
+
self.class,
|
169
|
+
self.class.r_model_name,
|
170
|
+
self.dependent_variable,
|
171
|
+
(all - [candidate]).map { |v| v.name },
|
172
|
+
self.data,
|
173
|
+
**self.options
|
174
|
+
)
|
175
|
+
|
176
|
+
if new_model.r_2_adjusted >= self.r_2_adjusted
|
177
|
+
puts "Excluded #{candidate}" if Rust.debug?
|
178
|
+
return *new_model.backward_selection(excluded + [candidate])
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
return self, excluded
|
183
|
+
end
|
184
|
+
|
185
|
+
|
121
186
|
def method_missing(name, *args)
|
122
187
|
return model|name.to_s
|
123
188
|
end
|
@@ -145,7 +210,11 @@ module Rust::Models::Regression
|
|
145
210
|
|
146
211
|
class LinearRegressionModel < RegressionModel
|
147
212
|
def self.can_pull?(type, klass)
|
148
|
-
return type == "list" && klass ==
|
213
|
+
return type == "list" && klass == self.r_model_name
|
214
|
+
end
|
215
|
+
|
216
|
+
def self.pull_priority
|
217
|
+
1
|
149
218
|
end
|
150
219
|
|
151
220
|
def self.pull_variable(variable, type, klass)
|
@@ -154,6 +223,10 @@ module Rust::Models::Regression
|
|
154
223
|
return LinearRegressionModel.new(model)
|
155
224
|
end
|
156
225
|
|
226
|
+
def self.r_model_name
|
227
|
+
"lm"
|
228
|
+
end
|
229
|
+
|
157
230
|
##
|
158
231
|
# Generates a linear regression model, given its +dependent_variable+ and +independent_variables+ and its +data+.
|
159
232
|
# +options+ can be specified and directly passed to the model.
|
@@ -161,7 +234,7 @@ module Rust::Models::Regression
|
|
161
234
|
def self.generate(dependent_variable, independent_variables, data, **options)
|
162
235
|
RegressionModel.generate(
|
163
236
|
LinearRegressionModel,
|
164
|
-
|
237
|
+
self.r_model_name,
|
165
238
|
dependent_variable,
|
166
239
|
independent_variables,
|
167
240
|
data,
|
@@ -175,13 +248,17 @@ module Rust::Models::Regression
|
|
175
248
|
|
176
249
|
class LinearMixedEffectsModel < RegressionModel
|
177
250
|
def self.can_pull?(type, klass)
|
178
|
-
return type == "S4" && klass ==
|
251
|
+
return type == "S4" && klass == self.r_model_name
|
179
252
|
end
|
180
253
|
|
181
254
|
def self.pull_priority
|
182
255
|
1
|
183
256
|
end
|
184
257
|
|
258
|
+
def self.r_model_name
|
259
|
+
"lmerModLmerTest"
|
260
|
+
end
|
261
|
+
|
185
262
|
def self.pull_variable(variable, type, klass)
|
186
263
|
model = Rust::RustDatatype.pull_variable(variable, Rust::S4Class)
|
187
264
|
|
@@ -213,7 +290,7 @@ module Rust::Models::Regression
|
|
213
290
|
|
214
291
|
RegressionModel.generate(
|
215
292
|
LinearMixedEffectsModel,
|
216
|
-
|
293
|
+
self.r_model_name,
|
217
294
|
dependent_variable,
|
218
295
|
fixed_effects + random_effects,
|
219
296
|
data,
|
@@ -235,18 +312,44 @@ module Rust::Models::Regression
|
|
235
312
|
end
|
236
313
|
end
|
237
314
|
end
|
315
|
+
|
316
|
+
##
|
317
|
+
# Slim representation for a variable in a model, with just the variable name, its coefficient and its p-value.
|
318
|
+
|
319
|
+
class ModelVariable
|
320
|
+
attr_accessor :name
|
321
|
+
attr_accessor :coefficient
|
322
|
+
attr_accessor :pvalue
|
323
|
+
|
324
|
+
def initialize(name, coefficient, pvalue)
|
325
|
+
@name = name
|
326
|
+
@coefficient = coefficient
|
327
|
+
@pvalue = pvalue
|
328
|
+
end
|
329
|
+
|
330
|
+
def intercept?
|
331
|
+
@name == "(Intercept)"
|
332
|
+
end
|
333
|
+
|
334
|
+
##
|
335
|
+
# Checks whether the variable is significant w.r.t. a given +a+ (0.05 by default)
|
336
|
+
|
337
|
+
def significant?(a = 0.05)
|
338
|
+
@pvalue <= a
|
339
|
+
end
|
340
|
+
end
|
238
341
|
end
|
239
342
|
|
240
343
|
module Rust::RBindings
|
241
344
|
def lm(formula, data, **options)
|
242
345
|
independent = formula.right_part.split("+").map { |v| v.strip }
|
243
|
-
return LinearRegressionModel.generate(formula.left_part, independent, data, **options)
|
346
|
+
return Rust::Models::Regression::LinearRegressionModel.generate(formula.left_part, independent, data, **options)
|
244
347
|
end
|
245
348
|
|
246
349
|
def lmer(formula, data, **options)
|
247
350
|
independent = formula.right_part.split("+").map { |v| v.strip }
|
248
351
|
|
249
|
-
RegressionModel.generate(
|
352
|
+
Rust::Models::Regression::RegressionModel.generate(
|
250
353
|
LinearMixedEffectsModel,
|
251
354
|
"lmer",
|
252
355
|
formula.left_part,
|
@@ -85,6 +85,20 @@ module Rust
|
|
85
85
|
@values.map { |k, v| k*v }.sum
|
86
86
|
end
|
87
87
|
|
88
|
+
##
|
89
|
+
# Returns the variance for this slice.
|
90
|
+
|
91
|
+
def variance
|
92
|
+
@values.map { |k, v| k**2 * v }.sum - (self.expected ** 2)
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Returns the standard deviation for this slice.
|
97
|
+
|
98
|
+
def sd
|
99
|
+
Math.sqrt(self.variance)
|
100
|
+
end
|
101
|
+
|
88
102
|
##
|
89
103
|
# Returns a slice with the values that are greater than +n+.
|
90
104
|
|
@@ -124,7 +138,7 @@ module Rust
|
|
124
138
|
# Returns a slice with the values between +a+ and +b+.
|
125
139
|
|
126
140
|
def between(a, b)
|
127
|
-
self.so_that { |k| k.between(a, b) }
|
141
|
+
self.so_that { |k| k.between?(a, b) }
|
128
142
|
end
|
129
143
|
|
130
144
|
##
|
@@ -133,6 +147,13 @@ module Rust
|
|
133
147
|
def so_that
|
134
148
|
RandomVariableSlice.new(@values.select { |k, v| yield(k) })
|
135
149
|
end
|
150
|
+
|
151
|
+
##
|
152
|
+
# Creates a bar plot of the distribution
|
153
|
+
|
154
|
+
def plot
|
155
|
+
Rust::Plots::BarPlot.new(@values.sort_by { |k, v| k }.to_h)
|
156
|
+
end
|
136
157
|
end
|
137
158
|
|
138
159
|
##
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rust
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.13'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simone Scalabrino
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-11-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rinruby
|
@@ -61,6 +61,7 @@ files:
|
|
61
61
|
- lib/rust.rb
|
62
62
|
- lib/rust/core.rb
|
63
63
|
- lib/rust/core/csv.rb
|
64
|
+
- lib/rust/core/manual.rb
|
64
65
|
- lib/rust/core/rust.rb
|
65
66
|
- lib/rust/core/types/all.rb
|
66
67
|
- lib/rust/core/types/dataframe.rb
|
@@ -74,8 +75,8 @@ files:
|
|
74
75
|
- lib/rust/external/ggplot2.rb
|
75
76
|
- lib/rust/external/ggplot2/core.rb
|
76
77
|
- lib/rust/external/ggplot2/geoms.rb
|
77
|
-
- lib/rust/external/ggplot2/helper.rb
|
78
78
|
- lib/rust/external/ggplot2/plot_builder.rb
|
79
|
+
- lib/rust/external/ggplot2/scale.rb
|
79
80
|
- lib/rust/external/ggplot2/themes.rb
|
80
81
|
- lib/rust/external/robustbase.rb
|
81
82
|
- lib/rust/models/all.rb
|
@@ -110,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
110
111
|
- !ruby/object:Gem::Version
|
111
112
|
version: '0'
|
112
113
|
requirements: []
|
113
|
-
rubygems_version: 3.
|
114
|
+
rubygems_version: 3.5.16
|
114
115
|
signing_key:
|
115
116
|
specification_version: 4
|
116
117
|
summary: Ruby advanced statistical library
|
@@ -1,122 +0,0 @@
|
|
1
|
-
require_relative 'core'
|
2
|
-
|
3
|
-
GGPLOT_EXAMPLES = {}
|
4
|
-
|
5
|
-
GGPLOT_EXAMPLES[["Quick introduction", /intro/]] = <<-EOS
|
6
|
-
bind_ggplot! # Avoid using long module names to reach Rust::Plots::GGPlot (simply includes this module)
|
7
|
-
|
8
|
-
# Best with a dataframe, but not necessary. If you have it...
|
9
|
-
df = Rust.toothgrowth
|
10
|
-
plot = PlotBuilder.for_dataframe(df). # Use a dataframe (symbols will be variable names)
|
11
|
-
labeled("Example plot"). # "labeled" sets the label to the last set aesthetic item (x, y, or title, in this case)
|
12
|
-
with_x(:len).labeled("X data from df"). # Set all the aesthetics (x, y, ...)
|
13
|
-
with_y(:dose).labeled("Y data from df").
|
14
|
-
draw_points. # Set the geometries to plot (based on the plot type)
|
15
|
-
build # Returns the plot ready to use
|
16
|
-
plot.show # Show the plot in a window
|
17
|
-
plot.save("output.pdf", width: 5, height: 4) # Save the plot, width, height etc. are optional
|
18
|
-
|
19
|
-
# If you don't have a dataframe...
|
20
|
-
plot2 = PlotBuilder.new.
|
21
|
-
with_x([1,2,3]).labeled("X data from df").
|
22
|
-
with_y([3,4,5]).labeled("Y data from df").
|
23
|
-
draw_points.
|
24
|
-
build
|
25
|
-
plot2.show
|
26
|
-
EOS
|
27
|
-
|
28
|
-
GGPLOT_EXAMPLES[["Scatter plots", /scatter/]] = <<-EOS
|
29
|
-
bind_ggplot!
|
30
|
-
df = Rust.toothgrowth
|
31
|
-
plot = PlotBuilder.for_dataframe(df).
|
32
|
-
with_x(:len).labeled("X data").
|
33
|
-
with_y(:dose).labeled("Y data").
|
34
|
-
draw_points. # To draw points
|
35
|
-
draw_lines. # To draw lines (keep both to draw both)
|
36
|
-
build
|
37
|
-
plot.show
|
38
|
-
EOS
|
39
|
-
|
40
|
-
GGPLOT_EXAMPLES[["Bar plots", /bar/]] = <<-EOS
|
41
|
-
bind_ggplot!
|
42
|
-
df = Rust.toothgrowth
|
43
|
-
plot = PlotBuilder.for_dataframe(df).
|
44
|
-
with_x(:len).labeled("X data").
|
45
|
-
with_fill(:supp).labeled("Legend"). # Use with_fill or with_color for stacked plots
|
46
|
-
draw_bars. # To draw bars
|
47
|
-
build
|
48
|
-
plot.show
|
49
|
-
EOS
|
50
|
-
|
51
|
-
GGPLOT_EXAMPLES[["Box plots", /box/]] = <<-EOS
|
52
|
-
bind_ggplot!
|
53
|
-
df = Rust.toothgrowth
|
54
|
-
plot = PlotBuilder.for_dataframe(df).
|
55
|
-
with_y(:len).labeled("Data to boxplot").
|
56
|
-
with_group(:supp).labeled("Groups"). # Groups to plot
|
57
|
-
draw_boxplot.
|
58
|
-
build
|
59
|
-
plot.show
|
60
|
-
EOS
|
61
|
-
|
62
|
-
GGPLOT_EXAMPLES[["Histograms", /hist/]] = <<-EOS
|
63
|
-
bind_ggplot!
|
64
|
-
df = Rust.toothgrowth
|
65
|
-
plot = PlotBuilder.for_dataframe(df).
|
66
|
-
with_x(:len).labeled("Data to plot").
|
67
|
-
with_fill(:supp).labeled("Color"). # Use with_fill or with_color for multiple plots
|
68
|
-
draw_histogram.
|
69
|
-
build
|
70
|
-
plot.show
|
71
|
-
EOS
|
72
|
-
|
73
|
-
GGPLOT_EXAMPLES[["Themes", /them/]] = <<-EOS
|
74
|
-
bind_ggplot!
|
75
|
-
df = Rust.toothgrowth
|
76
|
-
# The method with_theme allows to change theme options. The method can be called
|
77
|
-
# several times, each time the argument does not overwrite the previous options,
|
78
|
-
# unless they are specified again (in that case, the last specified ones win).
|
79
|
-
plot = PlotBuilder.for_dataframe(df).
|
80
|
-
with_x(:len).labeled("X data").
|
81
|
-
with_y(:dose).labeled("Y data").
|
82
|
-
draw_points.
|
83
|
-
with_theme(
|
84
|
-
ThemeBuilder.new('bw').
|
85
|
-
title(face: 'bold', size: 12). # Each method sets the property for the related element
|
86
|
-
legend do |legend| # Legend and other parts can be set like this
|
87
|
-
legend.position(:left) # Puts the legend on the left
|
88
|
-
end.
|
89
|
-
axis do |axis| # Modifies the axes
|
90
|
-
axis.line(Theme::BlankElement.new) # Hides the lines for the axes
|
91
|
-
axis.text_x(size: 3) # X axis labels
|
92
|
-
end.
|
93
|
-
panel do |panel|
|
94
|
-
panel.grid_major(colour: 'grey70', size: 0.2) # Sets the major ticks grid
|
95
|
-
panel.grid_minor(Theme::BlankElement.new) # Hides the minor ticks grid
|
96
|
-
end.
|
97
|
-
build
|
98
|
-
).build
|
99
|
-
plot.show
|
100
|
-
EOS
|
101
|
-
|
102
|
-
module Rust::Plots::GGPlot
|
103
|
-
def self.help!(topic = nil)
|
104
|
-
unless topic
|
105
|
-
puts "Topics:"
|
106
|
-
GGPLOT_EXAMPLES.keys.each do |key, matcher|
|
107
|
-
puts "- #{key}"
|
108
|
-
end
|
109
|
-
puts "Call again specifying the topic of interest."
|
110
|
-
else
|
111
|
-
GGPLOT_EXAMPLES.each do |key, value|
|
112
|
-
if topic.match(key[1])
|
113
|
-
puts "*** #{key[0]} ***"
|
114
|
-
puts value
|
115
|
-
return
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
puts "Topic not found"
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|