rust 0.11 → 0.12

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 77dee9cb8c4a1e2894a8054fab8dbf140b8f6a4aed8ef38a60b41564fb5e8e69
4
- data.tar.gz: c9bf14d2239f464cf73ffaee86bcbfe9155a323269eff1b34b7bcd1272c46d36
3
+ metadata.gz: 8eb6e3759ef38070603a941ef348ac46e4b6c08b4638c493edb2169acf16c793
4
+ data.tar.gz: f855ca774695688ee64d7513ac94c8b98d3bae154bfae56b803ed1da567cb282
5
5
  SHA512:
6
- metadata.gz: a648f440574e7734f979c0ec5d65bc9594202cfe88414517dcc50482e391360809ced88bbc0b825f548dfd23dd2b9dfe47b71cabbd216249d992d8a18d14717c
7
- data.tar.gz: 1b999d94b96c05a0615ff05b3e1588c4a8e822baebba57055ae041a6fe2c7d0355d747e3c88973c9325046c59c4eff013b27336d95297c8c0e1cc06f515b2f27
6
+ metadata.gz: c281de698d8b4750832d77971dac857dbee2c31252b7950abf91777d37763bbb79577098ab48efeee6f8a5ef2a28949ef72816d33703eaad14887d588b4aac32
7
+ data.tar.gz: d44c0532c19ff8eb2f505d4da62e82b4b4f32dada4b39a05cddf57679a3a725dd38ceecf63aaf2d551a8ae691841196bad7cc0694d3613b928a84a9b6291ef6a
@@ -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 options.is_a?(Arguments)
140
+ raise TypeError, "Expected Arguments" unless arguments.is_a?(Arguments)
141
141
 
142
142
  @arguments = arguments
143
143
  end
@@ -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
@@ -0,0 +1,83 @@
1
+ require_relative 'core'
2
+
3
+ module Rust::Plots::GGPlot
4
+ class Geom < Layer
5
+ def initialize(type, arguments = [], **options)
6
+ super("geom_#{type}", **options)
7
+ @type = type
8
+ @arguments = Rust::Arguments.new(arguments)
9
+ end
10
+ end
11
+
12
+ class GeomPoint < Geom
13
+ def initialize(arguments = [], **options)
14
+ super("point", arguments, **options)
15
+ end
16
+ end
17
+
18
+ class GeomLine < Geom
19
+ def initialize(arguments = [], **options)
20
+ super("line", arguments, **options)
21
+ end
22
+ end
23
+
24
+ class GeomCol < Geom
25
+ def initialize(arguments = [], **options)
26
+ super("col", arguments, **options)
27
+ end
28
+ end
29
+
30
+ class GeomBoxplot < Geom
31
+ def initialize(arguments = [], **options)
32
+ super("boxplot", arguments, **options)
33
+ end
34
+ end
35
+
36
+ class GeomBar < Geom
37
+ def initialize(arguments = [], **options)
38
+ super("bar", arguments, **options)
39
+ end
40
+ end
41
+
42
+ class GeomHistogram < Geom
43
+ def initialize(arguments = [], **options)
44
+ super("histogram", arguments, **options)
45
+ end
46
+ end
47
+
48
+ class GeomDensity < Geom
49
+ def initialize(arguments = [], **options)
50
+ super("density", arguments, **options)
51
+ end
52
+ end
53
+ end
54
+
55
+ module Rust::RBindings
56
+ def geom_point(*arguments, **options)
57
+ return Rust::Plots::GGPlot::GeomPoint.new(*arguments, **options)
58
+ end
59
+
60
+ def geom_line(*arguments, **options)
61
+ return Rust::Plots::GGPlot::GeomLine.new(*arguments, **options)
62
+ end
63
+
64
+ def geom_col(*arguments, **options)
65
+ return Rust::Plots::GGPlot::GeomCol.new(*arguments, **options)
66
+ end
67
+
68
+ def geom_bar(*arguments, **options)
69
+ return Rust::Plots::GGPlot::GeomBar.new(*arguments, **options)
70
+ end
71
+
72
+ def geom_boxplot(*arguments, **options)
73
+ return Rust::Plots::GGPlot::GeomBoxplot.new(*arguments, **options)
74
+ end
75
+
76
+ def geom_histogram(*arguments, **options)
77
+ return Rust::Plots::GGPlot::GeomHistogram.new(*arguments, **options)
78
+ end
79
+
80
+ def geom_density(*arguments, **options)
81
+ return Rust::Plots::GGPlot::GeomDensity.new(*arguments, **options)
82
+ end
83
+ end
@@ -0,0 +1,122 @@
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
@@ -0,0 +1,188 @@
1
+ require_relative 'core'
2
+
3
+ Rust.prerequisite("ggplot2")
4
+
5
+ module Rust::Plots::GGPlot
6
+ class PlotBuilder
7
+ def self.for_dataframe(data_frame)
8
+ return PlotBuilder.new(data_frame)
9
+ end
10
+
11
+ def initialize(data=nil)
12
+ @data = data
13
+
14
+ @aes_options = {}
15
+ @label_options = {}
16
+
17
+ @current_context = :title
18
+
19
+ @layers = []
20
+ end
21
+
22
+ def with_x(variable, label = nil)
23
+ variable = variable.to_sym if variable.is_a?(String)
24
+
25
+ @aes_options[:x] = variable
26
+ @current_context = :x
27
+
28
+ return self
29
+ end
30
+
31
+ def with_y(variable)
32
+ variable = variable.to_sym if variable.is_a?(String)
33
+
34
+ @aes_options[:y] = variable
35
+ @current_context = :y
36
+
37
+ return self
38
+ end
39
+
40
+ def with_group(variable)
41
+ variable = variable.to_sym if variable.is_a?(String)
42
+
43
+ @aes_options[:group] = variable
44
+ @current_context = :group
45
+
46
+ return self
47
+ end
48
+
49
+ def with_color(variable)
50
+ variable = variable.to_sym if variable.is_a?(String)
51
+
52
+ @aes_options[:color] = variable
53
+ @current_context = :color
54
+
55
+ return self
56
+ end
57
+
58
+ def with_fill(variable)
59
+ variable = variable.to_sym if variable.is_a?(String)
60
+
61
+ @aes_options[:fill] = variable
62
+ @current_context = :fill
63
+
64
+ return self
65
+ end
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
+ def with_x_label(value)
76
+ @label_options[:x] = value
77
+
78
+ return self
79
+ end
80
+
81
+ def with_y_label(value)
82
+ @label_options[:y] = value
83
+
84
+ return self
85
+ end
86
+
87
+ def with_color_label(value)
88
+ @label_options[:color] = value
89
+
90
+ return self
91
+ end
92
+
93
+ def with_title(value)
94
+ @label_options[:title] = value
95
+
96
+ return self
97
+ end
98
+
99
+ def draw_points(**options)
100
+ @layers << GeomPoint.new(**options)
101
+
102
+ @current_context = nil
103
+
104
+ return self
105
+ end
106
+
107
+ def draw_lines(**options)
108
+ @layers << GeomLine.new(**options)
109
+
110
+ @current_context = nil
111
+
112
+ return self
113
+ end
114
+
115
+ def draw_bars(**options)
116
+ @layers << GeomBar.new(**options)
117
+
118
+ @current_context = nil
119
+
120
+ return self
121
+ end
122
+
123
+ def draw_cols(**options)
124
+ @layers << GeomCol.new(**options)
125
+
126
+ @current_context = nil
127
+
128
+ return self
129
+ end
130
+
131
+ def draw_boxplot(**options)
132
+ @layers << GeomBoxplot.new(**options)
133
+
134
+ @current_context = nil
135
+
136
+ return self
137
+ end
138
+
139
+ def draw_histogram(**options)
140
+ @layers << GeomHistogram.new(**options)
141
+
142
+ @current_context = nil
143
+
144
+ return self
145
+ end
146
+
147
+ def draw_density(**options)
148
+ @layers << GeomDensity.new(**options)
149
+
150
+ @current_context = nil
151
+
152
+ return self
153
+ end
154
+
155
+ def with_theme(theme)
156
+ @layers << theme
157
+
158
+ @current_context = nil
159
+
160
+ return self
161
+ end
162
+
163
+ def flip_coordinates
164
+ @layers << FlipCoordinates.new
165
+
166
+ @current_context = nil
167
+
168
+ return self
169
+ end
170
+
171
+ def build
172
+ plot = Plot.new(@data, Aes.new(**@aes_options))
173
+ plot.theme = @theme if @theme
174
+ plot << @layers if @layers.size > 0
175
+ if @label_options.size > 0
176
+ if @label_options.keys.include?(:group)
177
+ value = @label_options.delete(:group)
178
+ selected = [:x, :y] - @label_options.keys
179
+ @label_options[selected.first] = value if selected.size == 1
180
+ end
181
+
182
+ plot << Labels.new(**@label_options)
183
+ end
184
+
185
+ return plot
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,435 @@
1
+ require 'json'
2
+ require_relative 'core'
3
+
4
+ module Rust::Plots::GGPlot
5
+ class Theme < Layer
6
+ def self.from_h(options)
7
+ starting = options.delete('_starting')
8
+ options = options.map do |key, value|
9
+ if value.is_a?(Hash)
10
+ case value.delete('_type').split("::").last
11
+ when 'TextElement'
12
+ [key, TextElement.new(**value)]
13
+ when 'RectElement'
14
+ [key, RectElement.new(**value)]
15
+ when 'TextElement'
16
+ [key, TextElement.new(**value)]
17
+ when 'LineElement'
18
+ [key, LineElement.new(**value)]
19
+ end
20
+ else
21
+ [key, value]
22
+ end
23
+ end.to_h
24
+
25
+ return Theme.new(starting, **options)
26
+ end
27
+
28
+ def self.load(filename)
29
+ json = JSON.parse(File.read(filename))
30
+ return self.from_h(json.to_h)
31
+ end
32
+
33
+ def initialize(starting, **options)
34
+ super("theme", **options)
35
+ if starting
36
+ @starting = "theme_" + starting
37
+ end
38
+ end
39
+
40
+ def to_R
41
+ result = super do |options, arguments|
42
+ [
43
+ options.map { |k, v| [k.to_s.gsub("_", "."), v] }.to_h,
44
+ arguments
45
+ ]
46
+ end
47
+
48
+ result = Rust::Function.new(@starting).to_R + " + " + result if @starting
49
+
50
+ return result
51
+ end
52
+
53
+ def to_h
54
+ options = @options.clone
55
+
56
+ options['_starting'] = @starting.sub("theme_", "")
57
+ options = options.map do |key, value|
58
+ [key, value.is_a?(Theme::Element) ? value.to_h : value]
59
+ end.to_h
60
+
61
+ return options
62
+ end
63
+
64
+ def save(path, force = false)
65
+ if !force && FileTest.exist?(path)
66
+ raise "File already existing."
67
+ end
68
+
69
+ File.open(path, "w") do |f|
70
+ f.write(
71
+ JSON.pretty_generate(
72
+ self.to_h
73
+ )
74
+ )
75
+ end
76
+
77
+ return true
78
+ end
79
+ end
80
+
81
+ class Theme::Element
82
+ attr_reader :options
83
+
84
+ def initialize(**options)
85
+ @options = options
86
+ end
87
+
88
+ def r_function
89
+ raise "Not implemented for generic theme element"
90
+ end
91
+
92
+ def to_R
93
+ options = @options.map { |k, v| [k.to_s.gsub("_", "."), v] }.to_h
94
+
95
+ function = Rust::Function.new(self.r_function)
96
+ function.options = Rust::Options.from_hash(options)
97
+
98
+ return function.to_R
99
+ end
100
+
101
+ def to_h
102
+ hash = @options.clone
103
+ hash['_type'] = self.class.name
104
+ return hash
105
+ end
106
+ end
107
+
108
+ class Theme::TextElement < Theme::Element
109
+ def r_function
110
+ return "element_text"
111
+ end
112
+ end
113
+
114
+ class Theme::LineElement < Theme::Element
115
+ def r_function
116
+ return "element_line"
117
+ end
118
+ end
119
+
120
+ class Theme::RectElement < Theme::Element
121
+ def r_function
122
+ return "element_rect"
123
+ end
124
+ end
125
+
126
+ class Theme::BlankElement < Theme::Element
127
+ def r_function
128
+ return "element_blank"
129
+ end
130
+ end
131
+
132
+ class ThemeComponentBuilder
133
+ def initialize(namespace=nil)
134
+ @namespace = namespace
135
+ @options = {}
136
+ end
137
+
138
+ def option(key, value)
139
+ key = "#@namespace.#{key}" if @namespace
140
+ @options[key] = value
141
+
142
+ return self
143
+ end
144
+
145
+ def [](key)
146
+ key = "#@namespace.#{key}" if @namespace
147
+ return @options[key]
148
+ end
149
+
150
+ def line_el(value)
151
+ if value.is_a?(Theme::LineElement) || value.is_a?(Theme::BlankElement)
152
+ return value
153
+ elsif value.is_a?(Hash)
154
+ return Theme::LineElement.new(**value)
155
+ else
156
+ raise "Expected line or hash"
157
+ end
158
+ end
159
+
160
+ def rect_el(value)
161
+ if value.is_a?(Theme::RectElement) || value.is_a?(Theme::BlankElement)
162
+ return value
163
+ elsif value.is_a?(Hash)
164
+ return Theme::RectElement.new(**value)
165
+ else
166
+ raise "Expected rect or hash"
167
+ end
168
+ end
169
+
170
+ def text_el(value)
171
+ if value.is_a?(Theme::TextElement) || value.is_a?(Theme::BlankElement)
172
+ return value
173
+ elsif value.is_a?(Hash)
174
+ return Theme::TextElement.new(**value)
175
+ else
176
+ raise "Expected text or hash"
177
+ end
178
+ end
179
+
180
+ def unit_el(value)
181
+ numeric = nil
182
+ unit = nil
183
+
184
+ if input.is_a?(String)
185
+ numeric, unit = *input.scan(/^([0-9.]+)([A-Za-z]+)/).flatten
186
+
187
+ raise "Unclear numeric part in #{input}" unless numeric
188
+ raise "Unclear unit part in #{input}" unless unit
189
+ elsif input.is_a?(Numeric)
190
+ numeric = input
191
+ unit = "npc"
192
+ end
193
+
194
+ raise "Unable to handle #{input}" unless numeric && unit
195
+
196
+ function = Rust::Function.new("units")
197
+ function.arguments = Rust::Arguments.new([numeric, unit])
198
+
199
+ return function.to_R
200
+ end
201
+
202
+ def alignment_el(value)
203
+ if value.is_a?(String) || value.is_a?(Symbol)
204
+ case value.to_s.downcase
205
+ when 'left'
206
+ value = 1
207
+ when 'right'
208
+ value = 0
209
+ else
210
+ value = 1
211
+ end
212
+ end
213
+
214
+ return value
215
+ end
216
+
217
+ def numeric_el(value)
218
+ raise "Expected number" unless value.is_a?(Numeric)
219
+ return value
220
+ end
221
+
222
+ def build
223
+ @options
224
+ end
225
+ end
226
+
227
+ class ThemeBuilder < ThemeComponentBuilder
228
+ def initialize(starting = 'bw')
229
+ super("plot")
230
+ @starting = starting
231
+ end
232
+
233
+ def background(value)
234
+ self.option('background', rect_el(value))
235
+ end
236
+
237
+ def title(value)
238
+ self.option('title', text_el(value))
239
+ end
240
+
241
+ def axis
242
+ builder = ThemeAxisBuilder.new
243
+ yield builder
244
+
245
+ @options.merge!(builder.build)
246
+ return self
247
+ end
248
+
249
+ def legend
250
+ builder = ThemeLegendBuilder.new
251
+ yield builder
252
+
253
+ @options.merge!(builder.build)
254
+ return self
255
+ end
256
+
257
+ def panel
258
+ builder = ThemePanelBuilder.new
259
+ yield builder
260
+
261
+ @options.merge!(builder.build)
262
+ return self
263
+ end
264
+
265
+ def build
266
+ return Theme.new(@starting, **@options)
267
+ end
268
+ end
269
+
270
+ class ThemeAxisBuilder < ThemeComponentBuilder
271
+ def initialize
272
+ super("axis")
273
+ end
274
+
275
+ def line(value)
276
+ self.option('line', line_el(value))
277
+ end
278
+
279
+ def text(value)
280
+ self.option('text', text_el(value))
281
+ end
282
+
283
+ def text_x(value)
284
+ self.option('text.x', text_el(value))
285
+ end
286
+
287
+ def text_y(value)
288
+ self.option('text.y', text_el(value))
289
+ end
290
+
291
+ def title(value)
292
+ self.option('title', text_el(value))
293
+ end
294
+
295
+ def title_x(value)
296
+ self.option('title.x', text_el(value))
297
+ end
298
+
299
+ def title_y(value)
300
+ self.option('title.y', text_el(value))
301
+ end
302
+
303
+ def ticks(value)
304
+ self.option('ticks', line_el(value))
305
+ end
306
+
307
+ def ticks_length(value)
308
+ self.option('ticks.length', unit_el(value))
309
+ end
310
+ end
311
+
312
+ class ThemeLegendBuilder < ThemeComponentBuilder
313
+ def initialize
314
+ super("legend")
315
+ end
316
+
317
+ def position(value)
318
+ self.option('position', value)
319
+ end
320
+
321
+ def justification(value)
322
+ self.option('justification', value)
323
+ end
324
+
325
+ def background(value)
326
+ self.option('background', rect_el(value))
327
+ end
328
+
329
+ def key_background(value)
330
+ self.option('key', rect_el(value))
331
+ end
332
+
333
+ def key_size(value)
334
+ self.option('key.size', unit_el(value))
335
+ end
336
+
337
+ def key_height(value)
338
+ self.option('key.height', unit_el(value))
339
+ end
340
+
341
+ def key_width(value)
342
+ self.option('key.width', unit_el(value))
343
+ end
344
+
345
+ def margin(value)
346
+ self.option('margin', unit_el(value))
347
+ end
348
+
349
+ def text(value)
350
+ self.option('text', text_el(value))
351
+ end
352
+
353
+ def text_align(value)
354
+ self.option('text.align', alignment_el(value))
355
+ end
356
+
357
+ def title(value)
358
+ self.option('title', text_el(value))
359
+ end
360
+
361
+ def title_align(value)
362
+ self.option('key.size', alignment_el(value))
363
+ end
364
+ end
365
+
366
+ class ThemePanelBuilder < ThemeComponentBuilder
367
+ def initialize
368
+ super("panel")
369
+ end
370
+
371
+ def background(value)
372
+ self.option('background', rect_el(value))
373
+ end
374
+
375
+ def border(value)
376
+ self.option('border', rect_el(value))
377
+ end
378
+
379
+ def grid_major(value)
380
+ self.option('grid.major', line_el(value))
381
+ end
382
+
383
+ def grid_major_x(value)
384
+ self.option('grid.major.x', line_el(value))
385
+ end
386
+
387
+ def grid_major_y(value)
388
+ self.option('grid.major.y', line_el(value))
389
+ end
390
+
391
+ def grid_minor(value)
392
+ self.option('grid.minor', line_el(value))
393
+ end
394
+
395
+ def grid_minor_x(value)
396
+ self.option('grid.minor.x', line_el(value))
397
+ end
398
+
399
+ def grid_minor_y(value)
400
+ self.option('grid.minor.y', line_el(value))
401
+ end
402
+
403
+ def aspect_ratio(value)
404
+ self.option('aspect.ratio', numeric_el(value))
405
+ end
406
+
407
+ def margin(value)
408
+ self.option('margin', unit_el(value))
409
+ end
410
+
411
+ def margin_x(value)
412
+ self.option('margin.x', unit_el(value))
413
+ end
414
+
415
+ def margin_y(value)
416
+ self.option('margin.y', unit_el(value))
417
+ end
418
+ end
419
+
420
+ self.default_theme = ThemeBuilder.new.
421
+ title(face: 'bold', size: 12).
422
+ legend do |legend|
423
+ legend.background(fill: 'white', size: 4, colour: 'white')
424
+ legend.position([0, 1])
425
+ legend.justification([0, 1])
426
+ end.
427
+ axis do |axis|
428
+ axis.ticks(colour: 'grey70', size: 0.2)
429
+ end.
430
+ panel do |panel|
431
+ panel.grid_major(colour: 'grey70', size: 0.2)
432
+ panel.grid_minor(Theme::BlankElement.new)
433
+ end.
434
+ build
435
+ end
@@ -0,0 +1,5 @@
1
+ require_relative 'ggplot2/core'
2
+ require_relative 'ggplot2/geoms'
3
+ require_relative 'ggplot2/themes'
4
+ require_relative 'ggplot2/plot_builder'
5
+ require_relative 'ggplot2/helper'
data/lib/rust.rb CHANGED
@@ -2,3 +2,22 @@ require_relative 'rust/core'
2
2
  require_relative 'rust/models/all'
3
3
  require_relative 'rust/plots/all'
4
4
  require_relative 'rust/stats/all'
5
+
6
+ module Rust
7
+ @@datasets = {}
8
+
9
+ def self.toothgrowth
10
+ @@datasets[:ToothGrowth] = Rust.exclusive { Rust['ToothGrowth'] } unless @@datasets[:ToothGrowth]
11
+ return @@datasets[:ToothGrowth]
12
+ end
13
+
14
+ def self.cars
15
+ @@datasets[:cars] = Rust.exclusive { Rust['cars'] } unless @@datasets[:cars]
16
+ return @@datasets[:cars]
17
+ end
18
+
19
+ def self.iris
20
+ @@datasets[:iris] = Rust.exclusive { Rust['iris'] } unless @@datasets[:iris]
21
+ return @@datasets[:iris]
22
+ end
23
+ end
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.11'
4
+ version: '0.12'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simone Scalabrino
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-11 00:00:00.000000000 Z
11
+ date: 2022-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rinruby
@@ -71,6 +71,12 @@ files:
71
71
  - lib/rust/core/types/matrix.rb
72
72
  - lib/rust/core/types/s4class.rb
73
73
  - lib/rust/core/types/utils.rb
74
+ - lib/rust/external/ggplot2.rb
75
+ - lib/rust/external/ggplot2/core.rb
76
+ - lib/rust/external/ggplot2/geoms.rb
77
+ - lib/rust/external/ggplot2/helper.rb
78
+ - lib/rust/external/ggplot2/plot_builder.rb
79
+ - lib/rust/external/ggplot2/themes.rb
74
80
  - lib/rust/external/robustbase.rb
75
81
  - lib/rust/models/all.rb
76
82
  - lib/rust/models/anova.rb