rust 0.11 → 0.12

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