galaaz 0.4.2 → 0.4.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +25 -0
- data/Rakefile +8 -0
- data/bin/gknit +9 -5
- data/bin/gstudio +4 -2
- data/bin/gstudio.rb +32 -2
- data/blogs/dev/dev.html +219 -34
- data/blogs/dev/dev.md +26 -26
- data/blogs/dev/dev_files/figure-html/bubble-1.png +0 -0
- data/blogs/dev/dev_files/figure-html/diverging_bar.png +0 -0
- data/blogs/dplyr/dplyr.rb +63 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot.Rmd +38 -26
- data/blogs/galaaz_ggplot/galaaz_ggplot.aux +16 -17
- data/blogs/galaaz_ggplot/galaaz_ggplot.pdf +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot.tex +65 -31
- data/blogs/oh_my/not_so.rb +2342 -0
- data/blogs/oh_my/oh_my.Rmd +493 -0
- data/blogs/oh_my/oh_my.html +680 -0
- data/blogs/oh_my/oh_my.md +597 -0
- data/blogs/oh_my/old.Rmd +2100 -0
- data/blogs/ruby_plot/figures/facets_with_decorations.png +0 -0
- data/blogs/ruby_plot/figures/facets_with_jitter.png +0 -0
- data/blogs/ruby_plot/figures/final_box_plot.png +0 -0
- data/blogs/ruby_plot/figures/final_violin_plot.png +0 -0
- data/blogs/ruby_plot/figures/violin_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot.Rmd +147 -122
- data/blogs/ruby_plot/ruby_plot.Rmd_external_figs +662 -0
- data/blogs/ruby_plot/ruby_plot.html +49 -54
- data/blogs/ruby_plot/ruby_plot.md +147 -122
- data/blogs/ruby_plot/ruby_plot.pdf +0 -0
- data/blogs/ruby_plot/ruby_plot.tex +776 -157
- data/blogs/ruby_plot/ruby_plot_files/figure-html/dose_len.svg +57 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_delivery.svg +106 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_dose.svg +110 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color.svg +174 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color2.svg +236 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_decorations.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_jitter.svg +296 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_points.svg +236 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/final_box_plot.svg +218 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/final_violin_plot.svg +128 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/violin_with_jitter.svg +150 -0
- data/examples/islr/ch2.spec.rb +21 -18
- data/examples/islr/ch3_boston.rb +14 -5
- data/examples/islr/ch3_multiple_regression.rb +2 -3
- data/examples/islr/ch6.spec.rb +1 -1
- data/examples/islr/x_y_rnorm.jpg +0 -0
- data/lib/R_interface/r.rb +14 -10
- data/lib/R_interface/r_libs.R +9 -0
- data/lib/R_interface/r_methods.rb +77 -6
- data/lib/R_interface/{expression.rb → r_module_s.rb} +13 -14
- data/lib/R_interface/rbinary_operators.rb +58 -71
- data/lib/R_interface/rdata_frame.rb +2 -1
- data/lib/R_interface/rdevices.R +4 -0
- data/lib/R_interface/rdevices.rb +1 -1
- data/lib/R_interface/renvironment.rb +34 -1
- data/lib/R_interface/rexpression.rb +108 -2
- data/lib/R_interface/rindexed_object.rb +3 -1
- data/lib/R_interface/rlanguage.rb +18 -2
- data/lib/R_interface/rmatrix.rb +14 -0
- data/lib/R_interface/rmd_indexed_object.rb +5 -1
- data/lib/R_interface/robject.rb +61 -23
- data/lib/R_interface/rsupport.rb +111 -53
- data/lib/R_interface/rsymbol.rb +6 -5
- data/lib/R_interface/ruby_extensions.rb +130 -4
- data/lib/R_interface/runary_operators.rb +35 -3
- data/lib/R_interface/rvector.rb +1 -0
- data/lib/galaaz.rb +0 -2
- data/lib/gknit/knitr_engine.rb +58 -4
- data/lib/gknit/ruby_engine.rb +5 -6
- data/lib/util/exec_ruby.rb +55 -9
- data/specs/all.rb +13 -3
- data/specs/figures/dose_len.png +0 -0
- data/specs/r_dataframe.spec.rb +49 -26
- data/specs/r_environment.spec.rb +140 -0
- data/specs/r_eval.spec.rb +0 -15
- data/specs/r_formula.spec.rb +232 -0
- data/specs/r_function.spec.rb +7 -8
- data/specs/r_list.spec.rb +4 -0
- data/specs/r_list_apply.spec.rb +11 -11
- data/specs/r_matrix.spec.rb +3 -3
- data/specs/{r_plots.spec.rb~ → r_nse.spec.rb} +29 -6
- data/specs/r_vector_creation.spec.rb +6 -0
- data/specs/r_vector_object.spec.rb +2 -2
- data/specs/r_vector_operators.spec.rb +3 -3
- data/specs/r_vector_subsetting.spec.rb +4 -4
- data/specs/ruby_expression.spec.rb +324 -0
- data/specs/tmp.rb +12 -524
- data/sty/galaaz.sty +71 -0
- data/version.rb +1 -1
- metadata +31 -41
- data/bin/gknit2~ +0 -6
- data/bin/ogk~ +0 -4
- data/bin/prepareR.rb~ +0 -1
- data/blogs/dev/dev.Rmd~ +0 -104
- data/blogs/galaaz_ggplot/galaaz_ggplot.dvi +0 -0
- data/blogs/galaaz_ggplot/midwest_external_png~ +0 -1
- data/blogs/gknit/gknit.Rmd~ +0 -184
- data/blogs/gknit/gknit.Rnd~ +0 -17
- data/blogs/gknit/model.rb~ +0 -46
- data/blogs/ruby_plot/ruby_plot.Rmd~ +0 -215
- data/examples/islr/Figure.jpg +0 -0
- data/examples/misc/moneyball.rb~ +0 -16
- data/examples/misc/subsetting.rb~ +0 -372
- data/lib/R/eng_ruby.R~ +0 -63
- data/lib/R_interface/capture_plot.rb~ +0 -23
- data/lib/R_interface/r.rb~ +0 -121
- data/lib/R_interface/rdevices.rb~ +0 -27
- data/lib/gknit.rb~ +0 -26
- data/lib/gknit/knitr_engine.rb~ +0 -102
- data/lib/gknit/ruby_engine.rb~ +0 -72
- data/lib/util/inline_file.rb~ +0 -23
- data/r_requires/knitr.rb~ +0 -4
- data/specs/r_language.spec.rb +0 -157
@@ -0,0 +1,597 @@
|
|
1
|
+
---
|
2
|
+
title: "Extending R with classes, modules, procs, lambdas, oh my!"
|
3
|
+
author:
|
4
|
+
- "Rodrigo Botafogo"
|
5
|
+
- "Daniel Mossé - University of Pittsburgh"
|
6
|
+
tags: [Tech, Data Science, Ruby, R, GraalVM]
|
7
|
+
date: "November 19th, 2018"
|
8
|
+
output:
|
9
|
+
html_document:
|
10
|
+
self_contained: true
|
11
|
+
keep_md: true
|
12
|
+
pdf_document:
|
13
|
+
includes:
|
14
|
+
in_header: "../../sty/galaaz.sty"
|
15
|
+
keep_tex: yes
|
16
|
+
number_sections: yes
|
17
|
+
toc: true
|
18
|
+
toc_depth: 2
|
19
|
+
fontsize: 11pt
|
20
|
+
---
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
# Introduction
|
25
|
+
|
26
|
+
This paper introduces and compares Galaaz with R's S4. It is a shameless rip off of
|
27
|
+
["A '(not so)' Short Introduction to S4"](https://cran.r-project.org/doc/contrib/Genolini-S4tutorialV0-5en.pdf) by Christophe Genolini and follows the same structure and examples presented there.
|
28
|
+
|
29
|
+
Galaaz is a Ruby Gem (library) that allows very tight integration between Ruby and R.
|
30
|
+
It's integration is much tigher and transparent from what one can get beetween RinRuby
|
31
|
+
or similar solutions in Python
|
32
|
+
such as [PypeR](https://pypi.python.org/pypi/PypeR/1.1.0), [rpy2](http://rpy2.bitbucket.org/)
|
33
|
+
and other similar solutions. Galaaz targets the GraalVM and it
|
34
|
+
integrates with FastR, a high performance R interpreter for the GraalVM.
|
35
|
+
|
36
|
+
GraalVM:
|
37
|
+
|
38
|
+
|
39
|
+
# Bases of Object Programming
|
40
|
+
|
41
|
+
In this paper, we will start our discussion from Part II of "The (not so) Short Introduction
|
42
|
+
to S4", which from now on we will reference as SS4 for "short S4". Interested readers are directed
|
43
|
+
to this paper to understand the motivation and examples in that paper. In this paper we will
|
44
|
+
present the S4 code from SS4 and then the same code in Ruby/Galaaz. We will not comment on the
|
45
|
+
S4 code, as all the comments can be found in SS4, we will only focus on the Ruby/Galaaz
|
46
|
+
description.
|
47
|
+
|
48
|
+
S4 defines classes by using the setClass function:
|
49
|
+
|
50
|
+
# Classes Declaration
|
51
|
+
|
52
|
+
```
|
53
|
+
# > setClass(
|
54
|
+
# + Class="Trajectories",
|
55
|
+
# + representation=representation(
|
56
|
+
# + times = "numeric",
|
57
|
+
# + traj = "matrix"
|
58
|
+
# + )
|
59
|
+
# + )
|
60
|
+
```
|
61
|
+
|
62
|
+
# Instance Variables
|
63
|
+
|
64
|
+
In Ruby a class is defined by the keyword 'class'. Every class should start with a capital
|
65
|
+
letter. S4 'slots' are called 'instance variables' in Ruby. Differently from R's S4,
|
66
|
+
instance variables in Ruby do not have type information. It should be clear though, that S4
|
67
|
+
type information is also not a "compile" time type, since R is not compiled. The type is
|
68
|
+
checked at runtime. The same checking can be done in Ruby and we will do it later in this
|
69
|
+
document.
|
70
|
+
|
71
|
+
In the example bellow, we create
|
72
|
+
class Trajectories with two instance variables, 'times' and 'matrix'. We will not go over
|
73
|
+
the details of instance variables in Ruby, but here we created those variables with the
|
74
|
+
keyword 'attr_reader' and a colom before the variables name:
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
```
|
79
|
+
## class Trajectories
|
80
|
+
## attr_reader :times
|
81
|
+
## attr_reader :matrix
|
82
|
+
## end
|
83
|
+
```
|
84
|
+
|
85
|
+
|
86
|
+
In order to create a new instance of object Trajectories we call method new on the class and
|
87
|
+
we can store the result in a varible (not an instance variable) as bellow:
|
88
|
+
|
89
|
+
|
90
|
+
```
|
91
|
+
## @traj = Trajectories.new
|
92
|
+
```
|
93
|
+
|
94
|
+
We now have in variable '@traj' a Trajectories object. In Ruby, printing variable 'traj' will
|
95
|
+
only print the class name of the object and not it contents as in R.
|
96
|
+
|
97
|
+
|
98
|
+
```
|
99
|
+
## puts @traj
|
100
|
+
```
|
101
|
+
|
102
|
+
```
|
103
|
+
## #<GalaazUtil::Trajectories:0x628>
|
104
|
+
```
|
105
|
+
|
106
|
+
To see the contents of an object, one needs to access its components using the '.' operator:
|
107
|
+
|
108
|
+
|
109
|
+
```
|
110
|
+
## puts @traj.times
|
111
|
+
```
|
112
|
+
|
113
|
+
# Constructor
|
114
|
+
|
115
|
+
Since there is no content stored in 'times' nor 'matrix', nil is returned. In order to add
|
116
|
+
a value in the variables, we need to add a constructor to the class Trajectories. In R, a
|
117
|
+
constructor is build by default, in Ruby, this has to be created by adding a method called
|
118
|
+
'initialize'. In the example bellow, we will create the initializer that accepts two values,
|
119
|
+
a 'times' value and a 'matrix' value and they are used to initialize the value of the
|
120
|
+
instance variables:
|
121
|
+
|
122
|
+
|
123
|
+
```
|
124
|
+
## class Trajectories
|
125
|
+
##
|
126
|
+
## attr_reader :times
|
127
|
+
## attr_reader :matrix
|
128
|
+
##
|
129
|
+
## #----------------------------------------------------------
|
130
|
+
## # Initializes the Trajectories class. Takes two parameters
|
131
|
+
## # @param times
|
132
|
+
## # @param matrix
|
133
|
+
## #----------------------------------------------------------
|
134
|
+
##
|
135
|
+
## def initialize(times: nil, matrix: nil)
|
136
|
+
## @times = times
|
137
|
+
## @matrix = matrix
|
138
|
+
## end
|
139
|
+
##
|
140
|
+
## end
|
141
|
+
```
|
142
|
+
|
143
|
+
Up to this point, everything described is pure Ruby code and has absolutely no relationship
|
144
|
+
with R.
|
145
|
+
We now want to create a Trajectories with a 'times' vector. Ruby has a vector class and we could
|
146
|
+
use this class to create a vector and add it to the 'times' instance variable; however, in order
|
147
|
+
to make use of R's functions, we want to create a R vector to add to 'times'. In Galaaz,
|
148
|
+
creating R objects is done using the corresponding R functions by just preceding them with 'R.',
|
149
|
+
i.e., R functions are all defined in Galaaz in the R namespace.
|
150
|
+
|
151
|
+
Since Galaaz is Ruby and not R, some syntax adjustments are sometimes necessary. For instance,
|
152
|
+
in R, a range is represented as '(1:4)', in Ruby, the same range is represented as '(1..4)'.
|
153
|
+
When passing arguments to an R function in R one uses the '=' sign after the slot name; in R,
|
154
|
+
one uses the ':' operator after parameter's name as we can see bellow:
|
155
|
+
|
156
|
+
|
157
|
+
```
|
158
|
+
## # Create a Trajectories passing a times vector, but no matrix parameter
|
159
|
+
## @traj = Trajectories.new(times: R.c(1, 2, 3, 4))
|
160
|
+
##
|
161
|
+
## # Create a Trajectories with times and matrix
|
162
|
+
## @traj2 = Trajectories.new(times: R.c(1, 3), matrix: R.matrix((1..4), ncol: 2))
|
163
|
+
```
|
164
|
+
|
165
|
+
# Access to Instance Variables (to reach a slot)
|
166
|
+
|
167
|
+
In order to access data in an instance variable the operator '.' is used. In R, a similar
|
168
|
+
result is obtained by use of the '@' operator, but SS4 does not recommend its use. In Galaaz,
|
169
|
+
the '.' operator is the recommended way of accessing an instance variable.
|
170
|
+
|
171
|
+
Now that we have created two trajectories, let's try to print its instance variables to see
|
172
|
+
that everything is fine:
|
173
|
+
|
174
|
+
|
175
|
+
|
176
|
+
```
|
177
|
+
## puts @traj.times
|
178
|
+
```
|
179
|
+
|
180
|
+
```
|
181
|
+
## [1] 1 2 3 4
|
182
|
+
```
|
183
|
+
|
184
|
+
We now have the expected value. Note that the 'times' vector is printed exactly as it would
|
185
|
+
if we were using GNU R. Let's now take a look at variable 'traj2':
|
186
|
+
|
187
|
+
|
188
|
+
```
|
189
|
+
## puts @traj2.times
|
190
|
+
## puts
|
191
|
+
## puts @traj2.matrix
|
192
|
+
```
|
193
|
+
|
194
|
+
```
|
195
|
+
## [1] 1 3
|
196
|
+
##
|
197
|
+
## [,1] [,2]
|
198
|
+
## [1,] 1 3
|
199
|
+
## [2,] 2 4
|
200
|
+
```
|
201
|
+
|
202
|
+
Let's now build the same examples as in SS4: Three hospitals take part in a
|
203
|
+
study. The Pitié Salpêtriere (which has not yet returned its data file, shame on them!),
|
204
|
+
Cochin and Saint-Anne. We first show the code in R and the corresponding Galaaz:
|
205
|
+
|
206
|
+
```
|
207
|
+
> trajPitie <- new(Class="Trajectories")
|
208
|
+
> trajCochin <- new(
|
209
|
+
+ Class= "Trajectories",
|
210
|
+
+ times=c(1,3,4,5),
|
211
|
+
+ traj=rbind (
|
212
|
+
+ c(15,15.1, 15.2, 15.2),
|
213
|
+
+ c(16,15.9, 16,16.4),
|
214
|
+
+ c(15.2, NA, 15.3, 15.3),
|
215
|
+
+ c(15.7, 15.6, 15.8, 16)
|
216
|
+
+ )
|
217
|
+
+ )
|
218
|
+
> trajStAnne <- new(
|
219
|
+
+ Class= "Trajectories",
|
220
|
+
+ times=c(1: 10, (6: 16) *2),
|
221
|
+
+ traj=rbind(
|
222
|
+
+ matrix (seq (16,19, length=21), ncol=21, nrow=50, byrow=TRUE),
|
223
|
+
+ matrix (seq (15.8, 18, length=21), ncol=21, nrow=30, byrow=TRUE)
|
224
|
+
+ )+rnorm (21*80,0,0.2)
|
225
|
+
+ )
|
226
|
+
```
|
227
|
+
|
228
|
+
This same code in Galaaz becomes:
|
229
|
+
|
230
|
+
|
231
|
+
```
|
232
|
+
## @trajPitie = Trajectories.new
|
233
|
+
##
|
234
|
+
## @trajCochin = Trajectories.new(times: R.c(1,3,4,5),
|
235
|
+
## matrix: R.rbind(
|
236
|
+
## R.c(15,15.1, 15.2, 15.2),
|
237
|
+
## R.c(16,15.9, 16,16.4),
|
238
|
+
## R.c(15.2, R::NA, 15.3, 15.3),
|
239
|
+
## R.c(15.7, 15.6, 15.8, 16)))
|
240
|
+
##
|
241
|
+
## @trajStAnne =
|
242
|
+
## Trajectories.new(times: R.c((1..10), R.c(6..16) * 2),
|
243
|
+
## matrix: (R.rbind(
|
244
|
+
## R.matrix(R.seq(16, 19, length: 21), ncol: 21,
|
245
|
+
## nrow: 50, byrow: true),
|
246
|
+
## R.matrix(R.seq(15.8, 18, length: 21), ncol: 21,
|
247
|
+
## nrow: 30, byrow: true)) + R.rnorm(21*80, 0, 0.2)))
|
248
|
+
```
|
249
|
+
|
250
|
+
Let's check that the 'times' and 'matrix' instance variables were correctly set:
|
251
|
+
|
252
|
+
|
253
|
+
```
|
254
|
+
## puts @trajCochin.times
|
255
|
+
## puts
|
256
|
+
## puts @trajCochin.matrix
|
257
|
+
## puts
|
258
|
+
## puts @trajStAnne.times
|
259
|
+
```
|
260
|
+
|
261
|
+
```
|
262
|
+
## [1] 1 3 4 5
|
263
|
+
##
|
264
|
+
## [,1] [,2] [,3] [,4]
|
265
|
+
## [1,] 15.0 15.1 15.2 15.2
|
266
|
+
## [2,] 16.0 15.9 16.0 16.4
|
267
|
+
## [3,] 15.2 NA 15.3 15.3
|
268
|
+
## [4,] 15.7 15.6 15.8 16.0
|
269
|
+
##
|
270
|
+
## [1] 1 2 3 4 5 6 7 8 9 10 12 14 16 18 20 22 24 26 28 30 32
|
271
|
+
```
|
272
|
+
|
273
|
+
We will not at this time print trajStAnne.matrix, since this is a huge matrix and the result
|
274
|
+
would just take too much space. Later we will print just a partial view of the matrix.
|
275
|
+
|
276
|
+
# Default Values
|
277
|
+
|
278
|
+
Default values are very useful and quite often used in Ruby programs. Although SS4 does not
|
279
|
+
recommend its use, there are many cases in which default values are useful and make code simpler.
|
280
|
+
We have already seen default values in this document, with the default being 'nil'. This was
|
281
|
+
necessary in order to be able to create our constructor and passing it the proper values.
|
282
|
+
|
283
|
+
In the example bellow, a class TrajectoriesBis is created with default value 1 for times and a
|
284
|
+
matrix with no elements in matrix.
|
285
|
+
|
286
|
+
|
287
|
+
```
|
288
|
+
## class TrajectoriesBis
|
289
|
+
##
|
290
|
+
## attr_reader :times
|
291
|
+
## attr_reader :matrix
|
292
|
+
##
|
293
|
+
## #----------------------------------------------------------
|
294
|
+
## # Initializes the Trajectories class. Takes two parameters
|
295
|
+
## # @param times
|
296
|
+
## # @param matrix
|
297
|
+
## #----------------------------------------------------------
|
298
|
+
##
|
299
|
+
## def initialize(times: 1, matrix: R.matrix(0))
|
300
|
+
## @times = times
|
301
|
+
## @matrix = matrix
|
302
|
+
## end
|
303
|
+
##
|
304
|
+
## end
|
305
|
+
##
|
306
|
+
## @traj_bis = TrajectoriesBis.new
|
307
|
+
```
|
308
|
+
|
309
|
+
Let's take a look at our new class:
|
310
|
+
|
311
|
+
|
312
|
+
```
|
313
|
+
## puts @traj_bis.times
|
314
|
+
## puts
|
315
|
+
## puts @traj_bis.matrix
|
316
|
+
```
|
317
|
+
|
318
|
+
```
|
319
|
+
## 1
|
320
|
+
##
|
321
|
+
## [,1]
|
322
|
+
## [1,] 0
|
323
|
+
```
|
324
|
+
|
325
|
+
Note that '@traj_bis.times' is the numeric 1, and what we actually want is a vector
|
326
|
+
with [1] in it.
|
327
|
+
|
328
|
+
|
329
|
+
```
|
330
|
+
## class TrajectoriesBis
|
331
|
+
##
|
332
|
+
## attr_reader :times
|
333
|
+
## attr_reader :matrix
|
334
|
+
##
|
335
|
+
## #----------------------------------------------------------
|
336
|
+
## # Initializes the Trajectories class. Takes two parameters
|
337
|
+
## # @param times [R::Vector] should be an R vector.
|
338
|
+
## # @param matrix [R::Matrix] should be an R matrix.
|
339
|
+
## #----------------------------------------------------------
|
340
|
+
##
|
341
|
+
## # Use R.c to convert number 1 to a vector
|
342
|
+
## def initialize(times: R.c(1), matrix: R.matrix(0))
|
343
|
+
## @times = times
|
344
|
+
## @matrix = matrix
|
345
|
+
## end
|
346
|
+
##
|
347
|
+
## end
|
348
|
+
##
|
349
|
+
## @traj_bis = TrajectoriesBis.new
|
350
|
+
```
|
351
|
+
|
352
|
+
|
353
|
+
```
|
354
|
+
## puts @traj_bis.times
|
355
|
+
## puts
|
356
|
+
## puts @traj_bis.matrix
|
357
|
+
```
|
358
|
+
|
359
|
+
```
|
360
|
+
## [1] 1
|
361
|
+
##
|
362
|
+
## [,1]
|
363
|
+
## [1,] 0
|
364
|
+
```
|
365
|
+
|
366
|
+
|
367
|
+
# The Empty Object
|
368
|
+
|
369
|
+
When a Trajectories is created with new, and no argument is given, all its instance variables
|
370
|
+
will have the default nil value. Since Ruby has no type information, then there is only one
|
371
|
+
type (or actually no type) of nil. To check if a variable is empty, we check it against the nil
|
372
|
+
value.
|
373
|
+
|
374
|
+
# To See an Object
|
375
|
+
|
376
|
+
Ruby has very strong meta-programming features, in particular, one can use introspection to
|
377
|
+
see methods and instance variables from a given class. Method 'instance_variables' shows all
|
378
|
+
the instance variables of an object:
|
379
|
+
|
380
|
+
```
|
381
|
+
puts @traj.instance_variables
|
382
|
+
```
|
383
|
+
|
384
|
+
The description of all meta-programming features of Ruby is well beyond the scope of this
|
385
|
+
document, but it is a very frequent a powerful feature of Ruby, that makes programming in
|
386
|
+
Ruby a different experience than programming in other languages.
|
387
|
+
|
388
|
+
# Methods
|
389
|
+
|
390
|
+
Methods are a fundamental feature of object oriented programming. We will now extend our class
|
391
|
+
Trajectories to add methods to it. In SS4, a method 'plot' is added to Trajectories. At this
|
392
|
+
point, Renjin and Galaaz do not yet have plotting capabilities, so we will have to skip this
|
393
|
+
method and go directly to the implementation of the 'print' method.
|
394
|
+
|
395
|
+
Bellow is the R code for method print:
|
396
|
+
|
397
|
+
```
|
398
|
+
> setMethod ("print","Trajectories",
|
399
|
+
+ function(x,...){
|
400
|
+
+ cat("*** Class Trajectories, method Print *** \\n")
|
401
|
+
+ cat("* Times ="); print (x@times)
|
402
|
+
+ cat("* Traj = \\n"); print (x@traj)
|
403
|
+
+ cat("******* End Print (trajectories) ******* \\n")
|
404
|
+
+ }
|
405
|
+
+ )
|
406
|
+
```
|
407
|
+
|
408
|
+
Now the same code for class Trajectories in Galaaz. In general methods are defined in a class
|
409
|
+
together with all the class definition. We will first use this approach. Later, we will show
|
410
|
+
how to 'reopen' a class to add new methods to it.
|
411
|
+
|
412
|
+
In this example, we are defining a method named 'print'. We have being using method 'puts' to
|
413
|
+
output data. There is a Ruby method that is more flexible than puts and that we need to use to
|
414
|
+
implement our function: 'print'. However, trying to use Ruby print inside the definition of
|
415
|
+
Trajectories's print will not work, as Ruby will understand that as a recursive call to print.
|
416
|
+
Ruby's print is defined inside the Kernel class, so, in order to call Ruby's print inside the
|
417
|
+
definition of Trajectories's print we need to write 'Kernel.print'.
|
418
|
+
|
419
|
+
|
420
|
+
```
|
421
|
+
## class Trajectories
|
422
|
+
##
|
423
|
+
## attr_reader :times
|
424
|
+
## attr_reader :matrix
|
425
|
+
##
|
426
|
+
##
|
427
|
+
## #----------------------------------------------------------
|
428
|
+
## # Initializes the Trajectories class. Takes two parameters
|
429
|
+
## # @param times [R::Vector] should be an R vector.
|
430
|
+
## # @param matrix [R::Matrix] should be an R matrix.
|
431
|
+
## #----------------------------------------------------------
|
432
|
+
##
|
433
|
+
## def initialize(times: nil, matrix: nil)
|
434
|
+
## @times = times
|
435
|
+
## @matrix = matrix
|
436
|
+
## end
|
437
|
+
##
|
438
|
+
## #----------------------------------------------------------
|
439
|
+
## #
|
440
|
+
## #----------------------------------------------------------
|
441
|
+
##
|
442
|
+
## def print
|
443
|
+
## puts("*** Class Trajectories, method Print *** ")
|
444
|
+
## Kernel.print("times = ")
|
445
|
+
## puts @times
|
446
|
+
## puts("traj =")
|
447
|
+
## puts @matrix
|
448
|
+
## puts("******* End Print (trajectories) ******* ")
|
449
|
+
## end
|
450
|
+
##
|
451
|
+
## end
|
452
|
+
```
|
453
|
+
|
454
|
+
|
455
|
+
```
|
456
|
+
## @trajCochin.print
|
457
|
+
```
|
458
|
+
|
459
|
+
```
|
460
|
+
## *** Class Trajectories, method Print ***
|
461
|
+
## times = [1] 1 3 4 5
|
462
|
+
## traj =
|
463
|
+
## [,1] [,2] [,3] [,4]
|
464
|
+
## [1,] 15.0 15.1 15.2 15.2
|
465
|
+
## [2,] 16.0 15.9 16.0 16.4
|
466
|
+
## [3,] 15.2 NA 15.3 15.3
|
467
|
+
## [4,] 15.7 15.6 15.8 16.0
|
468
|
+
## ******* End Print (trajectories) *******
|
469
|
+
```
|
470
|
+
|
471
|
+
For Cochin, the result is correct. For Saint-Anne, print will display too much
|
472
|
+
information. So we need a second method.
|
473
|
+
|
474
|
+
Show is the default R method used to show an object when its name is written in the
|
475
|
+
console. We thus define 'show' by taking into account the size of the object: if there are too
|
476
|
+
many trajectories, 'show' posts only part of them.
|
477
|
+
|
478
|
+
Here is the R code for method 'show':
|
479
|
+
|
480
|
+
```
|
481
|
+
> setMethod("show","Trajectories",
|
482
|
+
+ function(object){
|
483
|
+
+ cat("*** Class Trajectories, method Show *** \\n")
|
484
|
+
+ cat("* Times ="); print(object@times)
|
485
|
+
+ nrowShow <- min(10,nrow(object@traj))
|
486
|
+
+ ncolShow <- min(10,ncol(object@traj))
|
487
|
+
+ cat("* Traj (limited to a matrix 10x10) = \\n")
|
488
|
+
+ print(formatC(object@traj[1:nrowShow,1:ncolShow]),quote=FALSE)
|
489
|
+
+ cat("******* End Show (trajectories) ******* \\n")
|
490
|
+
+ }
|
491
|
+
+ )
|
492
|
+
```
|
493
|
+
|
494
|
+
Now, let's write it with Galaaz. This time though, we will not rewrite the whole Trajectories
|
495
|
+
class, but just reopen it to add this specific method. The next example has many interesting
|
496
|
+
features of Galaaz, some we have already seen, others will be described now:
|
497
|
+
|
498
|
+
* As we have already seen, to call an R function one uses the R.<function> notation. There
|
499
|
+
is however another way: when the first argument to the R function is an R object such as a
|
500
|
+
matrix, a list, a vector, etc. we can use '.' notation to call the function. This makes the
|
501
|
+
function look like a method of the object. For instance, R.nrow(@matrix), can be called by
|
502
|
+
doing @matrix.nrow;
|
503
|
+
|
504
|
+
* In R, every number is converted to a vector and this can be done with method R.i. Converting
|
505
|
+
a vector with only one number back to a number can be done with method '.gz'. So if @num is
|
506
|
+
an R vector that holds a number, then @num.gz is a number that can be used normally with Ruby
|
507
|
+
methods;
|
508
|
+
|
509
|
+
* R functions and Ruby methods can be used freely in Galaaz. We show bellow two different ways
|
510
|
+
of getting the minimum of a number, either by calling R.min or by getting the minimum of an
|
511
|
+
array, with the min method;
|
512
|
+
|
513
|
+
* Galaaz allows for method 'chaining'. Method chaining, also known as named parameter idiom, is
|
514
|
+
a common syntax for invoking multiple method calls in object-oriented programming languages.
|
515
|
+
Each method returns an object, allowing the calls to be chained together in a single statement
|
516
|
+
without requiring variables to store the intermediate results. For instance @matrix.nrow.gz,
|
517
|
+
which returns the number of rows of the matrix as a number;
|
518
|
+
|
519
|
+
* Ranges in Ruby are represented by (x..y), where x is the beginning of the range and y its end.
|
520
|
+
An R matrix can be indexed by range, object@traj[1:nrowShow,1:ncolShow], the same result is
|
521
|
+
obtained in Galaaz by indexing @matrix[(1..nrow_show), (1..ncol_show)]. Observe that this
|
522
|
+
statement is then chained with the format function and with the pp method to print the matrix.
|
523
|
+
|
524
|
+
|
525
|
+
|
526
|
+
```
|
527
|
+
## class Trajectories
|
528
|
+
##
|
529
|
+
## #----------------------------------------------------------
|
530
|
+
## #
|
531
|
+
## #----------------------------------------------------------
|
532
|
+
##
|
533
|
+
## def show
|
534
|
+
## puts("*** Class Trajectories, method Show *** ")
|
535
|
+
## Kernel.print("times = ")
|
536
|
+
## puts @times
|
537
|
+
## nrow_show = [10, @matrix.nrow << 0].min
|
538
|
+
## ncol_show = R.min(10, @matrix.ncol) << 0
|
539
|
+
## puts("* Traj (limited to a matrix 10x10) = ")
|
540
|
+
## puts @matrix[(1..nrow_show), (1..ncol_show)].format(digits: 2, nsmall: 2)
|
541
|
+
## puts("******* End Show (trajectories) ******* ")
|
542
|
+
## end
|
543
|
+
##
|
544
|
+
## end
|
545
|
+
```
|
546
|
+
|
547
|
+
|
548
|
+
```
|
549
|
+
## @trajStAnne.show
|
550
|
+
```
|
551
|
+
|
552
|
+
```
|
553
|
+
## *** Class Trajectories, method Show ***
|
554
|
+
## times = [1] 1 2 3 4 5 6 7 8 9 10 12 14 16 18 20 22 24 26 28 30 32
|
555
|
+
## * Traj (limited to a matrix 10x10) =
|
556
|
+
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
|
557
|
+
## [1,] "16.20" "15.61" "16.22" "16.34" "16.16" "16.75" "16.72" "17.25"
|
558
|
+
## [2,] "15.65" "16.44" "16.32" "16.58" "16.78" "17.04" "17.00" "16.88"
|
559
|
+
## [3,] "16.12" "16.23" "16.42" "16.51" "16.67" "16.77" "17.05" "16.98"
|
560
|
+
## [4,] "15.82" "16.22" "16.48" "16.79" "16.52" "16.71" "16.90" "17.06"
|
561
|
+
## [5,] "15.92" "16.39" "16.33" "16.61" "16.56" "16.81" "16.76" "17.25"
|
562
|
+
## [6,] "15.89" "16.03" "16.43" "16.09" "16.70" "16.80" "17.06" "16.95"
|
563
|
+
## [7,] "15.83" "16.03" "16.43" "16.79" "16.27" "16.79" "16.68" "16.98"
|
564
|
+
## [8,] "15.90" "16.38" "16.27" "16.59" "16.41" "16.17" "16.91" "17.22"
|
565
|
+
## [9,] "15.90" "15.83" "16.50" "16.24" "16.46" "16.52" "16.65" "16.94"
|
566
|
+
## [10,] "15.99" "15.94" "16.70" "16.41" "16.51" "16.62" "16.98" "16.67"
|
567
|
+
## [,9] [,10]
|
568
|
+
## [1,] "17.17" "16.92"
|
569
|
+
## [2,] "17.23" "17.46"
|
570
|
+
## [3,] "17.13" "17.17"
|
571
|
+
## [4,] "17.31" "17.39"
|
572
|
+
## [5,] "17.17" "17.63"
|
573
|
+
## [6,] "17.31" "17.05"
|
574
|
+
## [7,] "17.11" "17.54"
|
575
|
+
## [8,] "17.55" "17.58"
|
576
|
+
## [9,] "16.86" "16.85"
|
577
|
+
## [10,] "17.65" "17.45"
|
578
|
+
## ******* End Show (trajectories) *******
|
579
|
+
```
|
580
|
+
|
581
|
+
Our show method has the same problem as SS4, i.e., if an empty trajectories object is created and
|
582
|
+
we try to 'show' it, it will generate an error. Let's see it:
|
583
|
+
|
584
|
+
|
585
|
+
```
|
586
|
+
## @empty_traj = Trajectories.new
|
587
|
+
```
|
588
|
+
|
589
|
+
|
590
|
+
```
|
591
|
+
## @empty_traj.show
|
592
|
+
```
|
593
|
+
|
594
|
+
```
|
595
|
+
## undefined method `nrow' for nil:NilClass
|
596
|
+
```
|
597
|
+
|