galaaz 0.4.9 → 0.4.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +798 -285
- data/blogs/galaaz_ggplot/galaaz_ggplot.Rmd +3 -12
- data/blogs/galaaz_ggplot/galaaz_ggplot.aux +5 -7
- data/blogs/galaaz_ggplot/galaaz_ggplot.html +69 -29
- data/blogs/galaaz_ggplot/galaaz_ggplot.pdf +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-html/midwest_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-html/scatter_plot_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-latex/midwest_rb.pdf +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-latex/scatter_plot_rb.pdf +0 -0
- data/blogs/galaaz_ggplot/midwest.Rmd +1 -9
- data/blogs/gknit/gknit.Rmd +37 -40
- data/blogs/gknit/gknit.html +32 -30
- data/blogs/gknit/gknit.md +36 -37
- data/blogs/gknit/gknit.pdf +0 -0
- data/blogs/gknit/gknit.tex +35 -37
- data/blogs/manual/manual.Rmd +548 -125
- data/blogs/manual/manual.html +509 -286
- data/blogs/manual/manual.md +798 -285
- data/blogs/manual/manual.pdf +0 -0
- data/blogs/manual/manual.tex +2816 -0
- data/blogs/manual/manual_files/figure-latex/diverging_bar.pdf +0 -0
- data/blogs/nse_dplyr/nse_dplyr.Rmd +240 -74
- data/blogs/nse_dplyr/nse_dplyr.html +191 -87
- data/blogs/nse_dplyr/nse_dplyr.md +361 -107
- data/blogs/nse_dplyr/nse_dplyr.pdf +0 -0
- data/blogs/nse_dplyr/nse_dplyr.tex +1373 -0
- data/blogs/ruby_plot/ruby_plot.Rmd +61 -81
- data/blogs/ruby_plot/ruby_plot.html +54 -57
- data/blogs/ruby_plot/ruby_plot.md +48 -67
- data/blogs/ruby_plot/ruby_plot.pdf +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/dose_len.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_delivery.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_dose.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color2.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_points.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/final_box_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/final_violin_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/violin_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/dose_len.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facet_by_delivery.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facet_by_dose.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_by_delivery_color.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_by_delivery_color2.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_decorations.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_points.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/final_box_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/final_violin_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/violin_with_jitter.png +0 -0
- data/lib/R_interface/rdata_frame.rb +0 -12
- data/lib/R_interface/robject.rb +14 -14
- data/lib/R_interface/ruby_extensions.rb +3 -31
- data/lib/R_interface/rvector.rb +0 -12
- data/lib/gknit/knitr_engine.rb +5 -3
- data/lib/util/exec_ruby.rb +22 -61
- data/specs/tmp.rb +26 -12
- data/version.rb +1 -1
- metadata +22 -17
- data/bin/gknit_old_r +0 -236
- data/blogs/dev/dev.Rmd +0 -23
- data/blogs/dev/dev.md +0 -58
- data/blogs/dev/dev2.Rmd +0 -65
- data/blogs/dev/model.rb +0 -41
- data/blogs/dplyr/dplyr.Rmd +0 -29
- data/blogs/dplyr/dplyr.html +0 -433
- data/blogs/dplyr/dplyr.md +0 -58
- data/blogs/dplyr/dplyr.rb +0 -63
- data/blogs/galaaz_ggplot/galaaz_ggplot.log +0 -640
- data/blogs/galaaz_ggplot/galaaz_ggplot.md +0 -431
- data/blogs/galaaz_ggplot/galaaz_ggplot.tex +0 -481
- data/blogs/galaaz_ggplot/midwest.png +0 -0
- data/blogs/galaaz_ggplot/scatter_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot.tex +0 -1077
data/blogs/manual/manual.html
CHANGED
@@ -447,13 +447,8 @@ $(document).ready(function () {
|
|
447
447
|
</div>
|
448
448
|
<div id="gknitting-a-document" class="section level1">
|
449
449
|
<h1>gKnitting a Document</h1>
|
450
|
-
<p>This manual has been formatted usign gKnit. gKnit uses Knitr and R markdown to knit a document in Ruby or R and output it in any of the available formats for R markdown
|
451
|
-
|
452
|
-
Also, since it is based on Galaaz, Ruby chunks can have access to R variables and Polyglot Programming with Ruby and R is quite natural.</p>
|
453
|
-
<p>gknit was describe in more depth in:</p>
|
454
|
-
<ul>
|
455
|
-
<li>xxx.xxxx.xxx</li>
|
456
|
-
</ul>
|
450
|
+
<p>This manual has been formatted usign gKnit. gKnit uses Knitr and R markdown to knit a document in Ruby or R and output it in any of the available formats for R markdown. gKnit runs atop of GraalVM, and Galaaz. In gKnit, Ruby variables are persisted between chunks, making it an ideal solution for literate programming. Also, since it is based on Galaaz, Ruby chunks can have access to R variables and Polyglot Programming with Ruby and R is quite natural.</p>
|
451
|
+
<p><a href="https://towardsdatascience.com/how-to-do-reproducible-research-in-ruby-with-gknit-c26d2684d64e">gknit is described in more details here</a></p>
|
457
452
|
</div>
|
458
453
|
<div id="vector" class="section level1">
|
459
454
|
<h1>Vector</h1>
|
@@ -501,20 +496,20 @@ Also, since it is based on Galaaz, Ruby chunks can have access to R variables an
|
|
501
496
|
</table>
|
502
497
|
<p>Single numbers, such as 4.2, and strings, such as “four point two” are still vectors, of length 1; there are no more basic types. Vectors with length zero are possible (and useful). String vectors have mode and storage mode “character”. A single element of a character vector is often referred to as a character string.</p>
|
503
498
|
<p>To create a vector the ‘c’ (concatenate) method from the ‘R’ module should be used:</p>
|
504
|
-
<pre class="ruby"><code
|
505
|
-
puts
|
499
|
+
<pre class="ruby"><code>vec = R.c(1, 2, 3)
|
500
|
+
puts vec</code></pre>
|
506
501
|
<pre><code>## [1] 1 2 3</code></pre>
|
507
|
-
<p>Lets take a look at the type, mode and storage.mode of our vector
|
502
|
+
<p>Lets take a look at the type, mode and storage.mode of our vector vec. In order to print this out, we are creating a data frame ‘df’ and printing it out. A data frame, for those not familiar with it, is basically a table. Here we create the data frame and add the column name by passing named parameters for each column, such as ‘typeof:’, ‘mode:’ and ’storage__mode?‘. You should also note here that the double underscore is converted to a’.’. So, when printed ‘storage__mode’ will actually print as ‘storage.mode’.</p>
|
508
503
|
<p>Data frames will later be more carefully described. In R, the method used to create a data frame is ‘data.frame’, in Galaaz we use ‘data__frame’.</p>
|
509
|
-
<pre class="ruby"><code>df = R.data__frame(typeof:
|
504
|
+
<pre class="ruby"><code>df = R.data__frame(typeof: vec.typeof, mode: vec.mode, storage__mode: vec.storage__mode)
|
510
505
|
puts df</code></pre>
|
511
506
|
<pre><code>## typeof mode storage.mode
|
512
507
|
## 1 integer numeric integer</code></pre>
|
513
508
|
<p>If you want to create a vector with floating point numbers, then we need at least one of the vector’s element to be a float, such as 1.0. R users should be careful, since in R a number like ‘1’ is converted to float and to have an integer the R developer will use ‘1L’. Galaaz follows normal Ruby rules and the number 1 is an integer and 1.0 is a float.</p>
|
514
|
-
<pre class="ruby"><code
|
515
|
-
puts
|
509
|
+
<pre class="ruby"><code>vec = R.c(1.0, 2, 3)
|
510
|
+
puts vec</code></pre>
|
516
511
|
<pre><code>## [1] 1 2 3</code></pre>
|
517
|
-
<pre class="ruby"><code>df = R.data__frame(typeof:
|
512
|
+
<pre class="ruby"><code>df = R.data__frame(typeof: vec.typeof, mode: vec.mode, storage__mode: vec.storage__mode)
|
518
513
|
outputs df.kable.kable_styling</code></pre>
|
519
514
|
<table class="table" style="margin-left: auto; margin-right: auto;">
|
520
515
|
<thead>
|
@@ -547,11 +542,11 @@ double
|
|
547
542
|
<p>In this next example we try to create a vector with a variable ‘hello’ that has not yet being defined. This will raise an exception that is printed out. We get two return blocks, the first with a message explaining what went wrong and the second with the full backtrace of the error.</p>
|
548
543
|
<pre class="ruby"><code>vec = R.c(1, hello, 5)</code></pre>
|
549
544
|
<pre><code>## Message:
|
550
|
-
## undefined local variable or method `hello' for
|
545
|
+
## undefined local variable or method `hello' for #<RC:0x2e0 @out_list=nil>:RC</code></pre>
|
551
546
|
<pre><code>## Message:
|
552
|
-
##
|
553
|
-
## /home/rbotafogo/desenv/galaaz/lib/util/exec_ruby.rb:
|
554
|
-
## /home/rbotafogo/desenv/galaaz/lib/util/exec_ruby.rb:
|
547
|
+
## /home/rbotafogo/desenv/galaaz/lib/util/exec_ruby.rb:103:in `get_binding'
|
548
|
+
## /home/rbotafogo/desenv/galaaz/lib/util/exec_ruby.rb:102:in `eval'
|
549
|
+
## /home/rbotafogo/desenv/galaaz/lib/util/exec_ruby.rb:102:in `exec_ruby'
|
555
550
|
## /home/rbotafogo/desenv/galaaz/lib/gknit/knitr_engine.rb:650:in `block in initialize'
|
556
551
|
## /home/rbotafogo/desenv/galaaz/lib/R_interface/ruby_callback.rb:77:in `call'
|
557
552
|
## /home/rbotafogo/desenv/galaaz/lib/R_interface/ruby_callback.rb:77:in `callback'
|
@@ -571,55 +566,55 @@ double
|
|
571
566
|
## <REPL>:5:in `<repl wrapper>'
|
572
567
|
## <REPL>:1</code></pre>
|
573
568
|
<p>Here is a vector with logical values</p>
|
574
|
-
<pre class="ruby"><code
|
575
|
-
puts
|
569
|
+
<pre class="ruby"><code>vec = R.c(true, true, false, false, true)
|
570
|
+
puts vec</code></pre>
|
576
571
|
<pre><code>## [1] TRUE TRUE FALSE FALSE TRUE</code></pre>
|
577
572
|
<div id="combining-vectors" class="section level2">
|
578
573
|
<h2>Combining Vectors</h2>
|
579
574
|
<p>The ‘c’ functions used to create vectors can also be used to combine two vectors:</p>
|
580
|
-
<pre class="ruby"><code
|
581
|
-
|
582
|
-
|
583
|
-
puts
|
575
|
+
<pre class="ruby"><code>vec1 = R.c(10.0, 20.0, 30.0)
|
576
|
+
vec2 = R.c(4.0, 5.0, 6.0)
|
577
|
+
vec = R.c(vec1, vec2)
|
578
|
+
puts vec</code></pre>
|
584
579
|
<pre><code>## [1] 10 20 30 4 5 6</code></pre>
|
585
|
-
<p>In galaaz, methods can be chainned (somewhat like the pipe operator in R %>%, but more generic). In this next example, method ‘c’ is chainned after
|
586
|
-
<pre class="ruby"><code
|
587
|
-
puts
|
580
|
+
<p>In galaaz, methods can be chainned (somewhat like the pipe operator in R %>%, but more generic). In this next example, method ‘c’ is chainned after ‘vec1’. This also looks like ‘c’ is a method of the vector, but in reallity, this is actually closer to the pipe operator. When Galaaz identifies that ‘c’ is not a method of ‘vec’ it actually tries to call ‘R.c’ with ‘vec1’ as the first argument concatenated with all the other available arguments. The code bellow is automatically converted to the code above.</p>
|
581
|
+
<pre class="ruby"><code>vec = vec1.c(vec2)
|
582
|
+
puts vec</code></pre>
|
588
583
|
<pre><code>## [1] 10 20 30 4 5 6</code></pre>
|
589
584
|
</div>
|
590
585
|
<div id="vector-arithmetic" class="section level2">
|
591
586
|
<h2>Vector Arithmetic</h2>
|
592
587
|
<p>Arithmetic operations on vectors are performed element by element:</p>
|
593
|
-
<pre class="ruby"><code>puts
|
588
|
+
<pre class="ruby"><code>puts vec1 + vec2</code></pre>
|
594
589
|
<pre><code>## [1] 14 25 36</code></pre>
|
595
|
-
<pre class="ruby"><code>puts
|
590
|
+
<pre class="ruby"><code>puts vec1 * 5</code></pre>
|
596
591
|
<pre><code>## [1] 50 100 150</code></pre>
|
597
592
|
<p>When vectors have different length, a recycling rule is applied to the shorter vector:</p>
|
598
|
-
<pre class="ruby"><code
|
599
|
-
puts
|
593
|
+
<pre class="ruby"><code>vec3 = R.c(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0)
|
594
|
+
puts vec4 = vec1 + vec3</code></pre>
|
600
595
|
<pre><code>## [1] 11 22 33 14 25 36 17 28 39</code></pre>
|
601
596
|
</div>
|
602
597
|
<div id="vector-indexing" class="section level2">
|
603
598
|
<h2>Vector Indexing</h2>
|
604
599
|
<p>Vectors can be indexed by using the ‘[]’ operator:</p>
|
605
|
-
<pre class="ruby"><code>puts
|
600
|
+
<pre class="ruby"><code>puts vec4[3]</code></pre>
|
606
601
|
<pre><code>## [1] 33</code></pre>
|
607
|
-
<p>We can also index a vector with another vector. For example, in the code bellow, we take elements 1, 3, 5, and 7 from
|
608
|
-
<pre class="ruby"><code>puts
|
602
|
+
<p>We can also index a vector with another vector. For example, in the code bellow, we take elements 1, 3, 5, and 7 from vec3:</p>
|
603
|
+
<pre class="ruby"><code>puts vec4[R.c(1, 3, 5, 7)]</code></pre>
|
609
604
|
<pre><code>## [1] 11 33 25 17</code></pre>
|
610
605
|
<p>Repeating an index and having indices out of order is valid code:</p>
|
611
|
-
<pre class="ruby"><code>puts
|
606
|
+
<pre class="ruby"><code>puts vec4[R.c(1, 3, 3, 1)]</code></pre>
|
612
607
|
<pre><code>## [1] 11 33 33 11</code></pre>
|
613
608
|
<p>It is also possible to index a vector with a negative number or negative vector. In these cases the indexed values are not returned:</p>
|
614
|
-
<pre class="ruby"><code>puts
|
615
|
-
puts
|
609
|
+
<pre class="ruby"><code>puts vec4[-3]
|
610
|
+
puts vec4[-R.c(1, 3, 5, 7)]</code></pre>
|
616
611
|
<pre><code>## [1] 11 22 14 25 36 17 28 39
|
617
612
|
## [1] 22 14 36 28 39</code></pre>
|
618
613
|
<p>If an index is out of range, a missing value (NA) will be reported.</p>
|
619
|
-
<pre class="ruby"><code>puts
|
614
|
+
<pre class="ruby"><code>puts vec4[30]</code></pre>
|
620
615
|
<pre><code>## [1] NA</code></pre>
|
621
616
|
<p>It is also possible to index a vector by range:</p>
|
622
|
-
<pre class="ruby"><code>puts
|
617
|
+
<pre class="ruby"><code>puts vec4[(2..5)]</code></pre>
|
623
618
|
<pre><code>## [1] 22 33 14 25</code></pre>
|
624
619
|
<p>Elements in a vector can be named using the ‘names’ attribute of a vector:</p>
|
625
620
|
<pre class="ruby"><code>full_name = R.c("Rodrigo", "A", "Botafogo")
|
@@ -636,9 +631,9 @@ puts full_name</code></pre>
|
|
636
631
|
<div id="extracting-native-ruby-types-from-a-vector" class="section level2">
|
637
632
|
<h2>Extracting Native Ruby Types from a Vector</h2>
|
638
633
|
<p>Vectors created with ‘R.c’ are of class R::Vector. You might have noticed that when indexing a vector, a new vector is returned, even if this vector has one single element. In order to use R::Vector with other ruby classes it might be necessary to extract the actual Ruby native type from the vector. In order to do this extraction the ‘>>’ operator is used.</p>
|
639
|
-
<pre class="ruby"><code>puts
|
640
|
-
puts
|
641
|
-
puts
|
634
|
+
<pre class="ruby"><code>puts vec4
|
635
|
+
puts vec4 >> 0
|
636
|
+
puts vec4 >> 4</code></pre>
|
642
637
|
<pre><code>## [1] 11 22 33 14 25 36 17 28 39
|
643
638
|
## 11.0
|
644
639
|
## 25.0</code></pre>
|
@@ -1912,22 +1907,22 @@ Volvo 142E
|
|
1912
1907
|
<div id="matrix" class="section level1">
|
1913
1908
|
<h1>Matrix</h1>
|
1914
1909
|
<p>A matrix is a collection of elements organized as a two dimensional table. A matrix can be created by the ‘matrix’ function:</p>
|
1915
|
-
<pre class="ruby"><code
|
1916
|
-
|
1917
|
-
|
1910
|
+
<pre class="ruby"><code>mat = R.matrix(R.c(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0),
|
1911
|
+
nrow: 3,
|
1912
|
+
ncol: 3)
|
1918
1913
|
|
1919
|
-
puts
|
1914
|
+
puts mat</code></pre>
|
1920
1915
|
<pre><code>## [,1] [,2] [,3]
|
1921
1916
|
## [1,] 1 4 7
|
1922
1917
|
## [2,] 2 5 8
|
1923
1918
|
## [3,] 3 6 9</code></pre>
|
1924
1919
|
<p>Note that matrices data is organized by column first. It is possible to organize the matrix memory by row first passing an extra argument to the ‘matrix’ function:</p>
|
1925
|
-
<pre class="ruby"><code
|
1926
|
-
|
1927
|
-
|
1928
|
-
|
1920
|
+
<pre class="ruby"><code>mat_row = R.matrix(R.c(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0),
|
1921
|
+
nrow: 3,
|
1922
|
+
ncol: 3,
|
1923
|
+
byrow: true)
|
1929
1924
|
|
1930
|
-
puts
|
1925
|
+
puts mat_row</code></pre>
|
1931
1926
|
<pre><code>## [,1] [,2] [,3]
|
1932
1927
|
## [1,] 1 2 3
|
1933
1928
|
## [2,] 4 5 6
|
@@ -1935,31 +1930,32 @@ puts @mat_row</code></pre>
|
|
1935
1930
|
<div id="indexing-a-matrix" class="section level2">
|
1936
1931
|
<h2>Indexing a Matrix</h2>
|
1937
1932
|
<p>A matrix can be indexed by [row, column]:</p>
|
1938
|
-
<pre class="ruby"><code>puts
|
1939
|
-
puts
|
1933
|
+
<pre class="ruby"><code>puts mat_row[1, 1]
|
1934
|
+
puts mat_row[2, 3]</code></pre>
|
1940
1935
|
<pre><code>## [1] 1
|
1941
1936
|
## [1] 6</code></pre>
|
1942
1937
|
<p>It is possible to index an entire row or column with the ‘:all’ keyword</p>
|
1943
|
-
<pre class="ruby"><code>puts
|
1944
|
-
puts
|
1938
|
+
<pre class="ruby"><code>puts mat_row[1, :all]
|
1939
|
+
puts mat_row[:all, 2]</code></pre>
|
1945
1940
|
<pre><code>## [1] 1 2 3
|
1946
1941
|
## [1] 2 5 8</code></pre>
|
1947
1942
|
<p>Indexing with a vector is also possible for matrices. In the following example we want rows 1 and 3 and columns 2 and 3 building a 2 x 2 matrix.</p>
|
1948
|
-
<pre class="ruby"><code>puts
|
1943
|
+
<pre class="ruby"><code>puts mat_row[R.c(1, 3), R.c(2, 3)]</code></pre>
|
1949
1944
|
<pre><code>## [,1] [,2]
|
1950
1945
|
## [1,] 2 3
|
1951
1946
|
## [2,] 8 9</code></pre>
|
1952
|
-
<p>Matrices can be combined with functions ‘rbind
|
1953
|
-
<pre class="ruby"><code>puts
|
1954
|
-
puts @mat_row.cbind(@mat)</code></pre>
|
1947
|
+
<p>Matrices can be combined with functions ‘rbind’:</p>
|
1948
|
+
<pre class="ruby"><code>puts mat_row.rbind(mat)</code></pre>
|
1955
1949
|
<pre><code>## [,1] [,2] [,3]
|
1956
1950
|
## [1,] 1 2 3
|
1957
1951
|
## [2,] 4 5 6
|
1958
1952
|
## [3,] 7 8 9
|
1959
1953
|
## [4,] 1 4 7
|
1960
1954
|
## [5,] 2 5 8
|
1961
|
-
## [6,] 3 6 9
|
1962
|
-
|
1955
|
+
## [6,] 3 6 9</code></pre>
|
1956
|
+
<p>and ‘cbind’:</p>
|
1957
|
+
<pre class="ruby"><code>puts mat_row.cbind(mat)</code></pre>
|
1958
|
+
<pre><code>## [,1] [,2] [,3] [,4] [,5] [,6]
|
1963
1959
|
## [1,] 1 2 3 1 4 7
|
1964
1960
|
## [2,] 4 5 6 2 5 8
|
1965
1961
|
## [3,] 7 8 9 3 6 9</code></pre>
|
@@ -1971,8 +1967,8 @@ puts @mat_row.cbind(@mat)</code></pre>
|
|
1971
1967
|
<pre class="ruby"><code>nums = R.c(1.0, 2.0, 3.0)
|
1972
1968
|
strs = R.c("a", "b", "c", "d")
|
1973
1969
|
bool = R.c(true, true, false)
|
1974
|
-
|
1975
|
-
puts
|
1970
|
+
lst = R.list(nums: nums, strs: strs, bool: bool)
|
1971
|
+
puts lst</code></pre>
|
1976
1972
|
<pre><code>## $nums
|
1977
1973
|
## [1] 1 2 3
|
1978
1974
|
##
|
@@ -1981,18 +1977,18 @@ puts @lst</code></pre>
|
|
1981
1977
|
##
|
1982
1978
|
## $bool
|
1983
1979
|
## [1] TRUE TRUE FALSE</code></pre>
|
1984
|
-
<p>Note that
|
1980
|
+
<p>Note that ‘lst’ elements are named elements.</p>
|
1985
1981
|
<div id="list-indexing" class="section level2">
|
1986
1982
|
<h2>List Indexing</h2>
|
1987
1983
|
<p>List indexing, also called slicing, is done using the ‘[]’ operator and the ‘[[]]’ operator. Let’s first start with the ‘[]’ operator. The list above has three sublist indexing with ‘[]’ will return one of the sublists.</p>
|
1988
|
-
<pre class="ruby"><code>puts
|
1984
|
+
<pre class="ruby"><code>puts lst[1]</code></pre>
|
1989
1985
|
<pre><code>## $nums
|
1990
1986
|
## [1] 1 2 3</code></pre>
|
1991
1987
|
<p>Note that when using ‘[]’ a new list is returned. When using the double square bracket operator the value returned is the actual element of the list in the given position and not a slice of the original list</p>
|
1992
|
-
<pre class="ruby"><code>puts
|
1988
|
+
<pre class="ruby"><code>puts lst[[1]]</code></pre>
|
1993
1989
|
<pre><code>## [1] 1 2 3</code></pre>
|
1994
|
-
<p>When elements are named, as dones with
|
1995
|
-
<pre class="ruby"><code>puts
|
1990
|
+
<p>When elements are named, as dones with lst, indexing can be done by name:</p>
|
1991
|
+
<pre class="ruby"><code>puts lst[['bool']][[1]] >> 0</code></pre>
|
1996
1992
|
<pre><code>## true</code></pre>
|
1997
1993
|
<p>In this example, first the ‘bool’ element of the list was extracted, not as a list, but as a vector, then the first element of the vector was extracted (note that vectors also accept the ‘[[]]’ operator) and then the vector was indexed by its first element, extracting the native Ruby type.</p>
|
1998
1994
|
</div>
|
@@ -2048,17 +2044,17 @@ puts (~:mtcars)['Datsun 710', 'mpg']</code></pre>
|
|
2048
2044
|
<pre><code>## mpg cyl disp hp drat wt qsec vs am gear carb
|
2049
2045
|
## Datsun 710 22.8 4 108 93 3.85 2.32 18.61 1 1 4 1
|
2050
2046
|
## Camaro Z28 13.3 8 350 245 3.73 3.84 15.41 0 0 3 4</code></pre>
|
2051
|
-
<p>Finally, a data frame can also be indexed with a logical vector. In this next example, the ‘am’ column of :mtcars is compared with 0 (with method ‘eq’). When ‘am’ is equal to 0 the car is automatic. So, by doing ‘(~:mtcars).am.eq 0’ a logical vector is created with ‘true’ whenever ‘am’ is 0 and ‘false’ otherwise
|
2047
|
+
<p>Finally, a data frame can also be indexed with a logical vector. In this next example, the ‘am’ column of :mtcars is compared with 0 (with method ‘eq’). When ‘am’ is equal to 0 the car is automatic. So, by doing ‘(~:mtcars).am.eq 0’ a logical vector is created with ‘true’ whenever ‘am’ is 0 and ‘false’ otherwise.</p>
|
2052
2048
|
<pre class="ruby"><code># obtain a vector with 'true' for cars with automatic transmission
|
2053
2049
|
automatic = (~:mtcars).am.eq 0
|
2054
|
-
puts automatic
|
2055
|
-
|
2056
|
-
# slice the data frame by using this vector
|
2057
|
-
puts (~:mtcars)[automatic, :all]</code></pre>
|
2050
|
+
puts automatic</code></pre>
|
2058
2051
|
<pre><code>## [1] FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
|
2059
2052
|
## [12] TRUE TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE TRUE TRUE
|
2060
|
-
## [23] TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
|
2061
|
-
|
2053
|
+
## [23] TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE</code></pre>
|
2054
|
+
<p>Using this logical vector, the data frame is indexed, returning a new data frame in which all cars have automatic transmission.</p>
|
2055
|
+
<pre class="ruby"><code># slice the data frame by using this vector
|
2056
|
+
puts (~:mtcars)[automatic, :all]</code></pre>
|
2057
|
+
<pre><code>## mpg cyl disp hp drat wt qsec vs am gear carb
|
2062
2058
|
## Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
|
2063
2059
|
## Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
|
2064
2060
|
## Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
|
@@ -2123,6 +2119,30 @@ puts exp_wrong</code></pre>
|
|
2123
2119
|
<pre class="ruby"><code>exp7 = :y.assign E.sin(:x)
|
2124
2120
|
puts exp7</code></pre>
|
2125
2121
|
<pre><code>## y <- sin(x)</code></pre>
|
2122
|
+
<p>Expressions can also be written using ‘.’ notation:</p>
|
2123
|
+
<pre class="ruby"><code>exp8 = :y.assign :x.sin
|
2124
|
+
puts exp8</code></pre>
|
2125
|
+
<pre><code>## y <- sin(x)</code></pre>
|
2126
|
+
<p>When a function has multiple arguments, the first one can be used before the ‘.’:</p>
|
2127
|
+
<pre class="ruby"><code>exp9 = :x.c(:y)
|
2128
|
+
puts exp9</code></pre>
|
2129
|
+
<pre><code>## c(x, y)</code></pre>
|
2130
|
+
</div>
|
2131
|
+
<div id="evaluating-an-expression" class="section level2">
|
2132
|
+
<h2>Evaluating an Expression</h2>
|
2133
|
+
<p>Expressions can be evaluated by calling function ‘eval’ with a binding. A binding can be provided with a list:</p>
|
2134
|
+
<pre class="ruby"><code>exp = (:a + :b) * 2.0 + :c ** 2 / :z
|
2135
|
+
puts exp.eval(R.list(a: 10, b: 20, c: 30, z: 40))</code></pre>
|
2136
|
+
<pre><code>## [1] 82.5</code></pre>
|
2137
|
+
<p>… with a data frame:</p>
|
2138
|
+
<pre class="ruby"><code>df = R.data__frame(
|
2139
|
+
a: R.c(1, 2, 3),
|
2140
|
+
b: R.c(10, 20, 30),
|
2141
|
+
c: R.c(100, 200, 300),
|
2142
|
+
z: R.c(1000, 2000, 3000))
|
2143
|
+
|
2144
|
+
puts exp.eval(df)</code></pre>
|
2145
|
+
<pre><code>## [1] 32 64 96</code></pre>
|
2126
2146
|
</div>
|
2127
2147
|
</div>
|
2128
2148
|
<div id="manipulating-data" class="section level1">
|
@@ -2131,8 +2151,8 @@ puts exp7</code></pre>
|
|
2131
2151
|
<p>For these examples, we will investigate the nycflights13 data set available on the package by the same name. We use function ‘R.install_and_loads’ that checks if the library is available locally, and if not, installs it. This data frame contains all 336,776 flights that departed from New York City in 2013. The data comes from the US Bureau of Transportation Statistics.</p>
|
2132
2152
|
<pre class="ruby"><code>R.install_and_loads('nycflights13')
|
2133
2153
|
R.library('dplyr')</code></pre>
|
2134
|
-
<pre class="ruby"><code
|
2135
|
-
puts
|
2154
|
+
<pre class="ruby"><code>flights = ~:flights
|
2155
|
+
puts flights.head.as__data__frame</code></pre>
|
2136
2156
|
<pre><code>## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
|
2137
2157
|
## 1 2013 1 1 517 515 2 830 819
|
2138
2158
|
## 2 2013 1 1 533 529 4 850 830
|
@@ -2157,7 +2177,7 @@ puts @flights.head.as__data__frame</code></pre>
|
|
2157
2177
|
<div id="filtering-rows-with-filter" class="section level2">
|
2158
2178
|
<h2>Filtering rows with Filter</h2>
|
2159
2179
|
<p>In this example we filter the flights data set by giving to the filter function two expressions: the first :month.eq 1</p>
|
2160
|
-
<pre class="ruby"><code>puts
|
2180
|
+
<pre class="ruby"><code>puts flights.filter((:month.eq 1), (:day.eq 1)).head.as__data__frame</code></pre>
|
2161
2181
|
<pre><code>## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
|
2162
2182
|
## 1 2013 1 1 517 515 2 830 819
|
2163
2183
|
## 2 2013 1 1 533 529 4 850 830
|
@@ -2183,7 +2203,7 @@ puts @flights.head.as__data__frame</code></pre>
|
|
2183
2203
|
<div id="logical-operators" class="section level2">
|
2184
2204
|
<h2>Logical Operators</h2>
|
2185
2205
|
<p>All flights that departed in November of December</p>
|
2186
|
-
<pre class="ruby"><code>puts
|
2206
|
+
<pre class="ruby"><code>puts flights.filter((:month.eq 11) | (:month.eq 12)).head.as__data__frame</code></pre>
|
2187
2207
|
<pre><code>## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
|
2188
2208
|
## 1 2013 11 1 5 2359 6 352 345
|
2189
2209
|
## 2 2013 11 1 35 2250 105 123 2356
|
@@ -2206,7 +2226,7 @@ puts @flights.head.as__data__frame</code></pre>
|
|
2206
2226
|
## 5 45 2013-11-01 05:00:00
|
2207
2227
|
## 6 0 2013-11-01 06:00:00</code></pre>
|
2208
2228
|
<p>The same as above, but using the ‘in’ operator. In R, it is possible to define many operators by doing %<op>%. The %in% operator checks if a value is in a vector. In order to use those operators from Galaaz the ‘._’ method is used, where the first argument is the operator’s symbol, in this case ‘:in’ and the second argument is the vector:</p>
|
2209
|
-
<pre class="ruby"><code>puts
|
2229
|
+
<pre class="ruby"><code>puts flights.filter(:month._ :in, R.c(11, 12)).head.as__data__frame</code></pre>
|
2210
2230
|
<pre><code>## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
|
2211
2231
|
## 1 2013 11 1 5 2359 6 352 345
|
2212
2232
|
## 2 2013 11 1 35 2250 105 123 2356
|
@@ -2232,18 +2252,18 @@ puts @flights.head.as__data__frame</code></pre>
|
|
2232
2252
|
<div id="filtering-with-na-not-available" class="section level2">
|
2233
2253
|
<h2>Filtering with NA (Not Available)</h2>
|
2234
2254
|
<p>Let’s first create a ‘tibble’ with a Not Available value (R::NA). Tibbles are a modern version of a data frame and operate very similarly to one. It differs in how it outputs the values and the result of some subsetting operations that are more consistent than what is obtained from data frame.</p>
|
2235
|
-
<pre class="ruby"><code
|
2236
|
-
puts
|
2255
|
+
<pre class="ruby"><code>df = R.tibble(x: R.c(1, R::NA, 3))
|
2256
|
+
puts df.as__data__frame</code></pre>
|
2237
2257
|
<pre><code>## x
|
2238
2258
|
## 1 1
|
2239
2259
|
## 2 NA
|
2240
2260
|
## 3 3</code></pre>
|
2241
2261
|
<p>Now filtering by :x > 1 shows all lines that satisfy this condition, where the row with R:NA does not.</p>
|
2242
|
-
<pre class="ruby"><code>puts
|
2262
|
+
<pre class="ruby"><code>puts df.filter(:x > 1).as__data__frame</code></pre>
|
2243
2263
|
<pre><code>## x
|
2244
2264
|
## 1 3</code></pre>
|
2245
2265
|
<p>To match an NA use method ’is__na’</p>
|
2246
|
-
<pre class="ruby"><code>puts
|
2266
|
+
<pre class="ruby"><code>puts df.filter((:x.is__na) | (:x > 1)).as__data__frame</code></pre>
|
2247
2267
|
<pre><code>## x
|
2248
2268
|
## 1 NA
|
2249
2269
|
## 2 3</code></pre>
|
@@ -2251,7 +2271,7 @@ puts @df.as__data__frame</code></pre>
|
|
2251
2271
|
<div id="arrange-rows-with-arrange" class="section level2">
|
2252
2272
|
<h2>Arrange Rows with arrange</h2>
|
2253
2273
|
<p>Arrange reorders the rows of a data frame by the given arguments.</p>
|
2254
|
-
<pre class="ruby"><code>puts
|
2274
|
+
<pre class="ruby"><code>puts flights.arrange(:year, :month, :day).head.as__data__frame</code></pre>
|
2255
2275
|
<pre><code>## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
|
2256
2276
|
## 1 2013 1 1 517 515 2 830 819
|
2257
2277
|
## 2 2013 1 1 533 529 4 850 830
|
@@ -2274,7 +2294,7 @@ puts @df.as__data__frame</code></pre>
|
|
2274
2294
|
## 5 0 2013-01-01 06:00:00
|
2275
2295
|
## 6 58 2013-01-01 05:00:00</code></pre>
|
2276
2296
|
<p>To arrange in descending order, use function ‘desc’</p>
|
2277
|
-
<pre class="ruby"><code>puts
|
2297
|
+
<pre class="ruby"><code>puts flights.arrange(:dep_delay.desc).head.as__data__frame</code></pre>
|
2278
2298
|
<pre><code>## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
|
2279
2299
|
## 1 2013 1 9 641 900 1301 1242 1530
|
2280
2300
|
## 2 2013 6 15 1432 1935 1137 1607 2120
|
@@ -2300,7 +2320,7 @@ puts @df.as__data__frame</code></pre>
|
|
2300
2320
|
<div id="selecting-columns" class="section level2">
|
2301
2321
|
<h2>Selecting columns</h2>
|
2302
2322
|
<p>To select specific columns from a dataset we use function ‘select’:</p>
|
2303
|
-
<pre class="ruby"><code>puts
|
2323
|
+
<pre class="ruby"><code>puts flights.select(:year, :month, :day).head.as__data__frame</code></pre>
|
2304
2324
|
<pre><code>## year month day
|
2305
2325
|
## 1 2013 1 1
|
2306
2326
|
## 2 2013 1 1
|
@@ -2309,7 +2329,7 @@ puts @df.as__data__frame</code></pre>
|
|
2309
2329
|
## 5 2013 1 1
|
2310
2330
|
## 6 2013 1 1</code></pre>
|
2311
2331
|
<p>It is also possible to select column in a given range</p>
|
2312
|
-
<pre class="ruby"><code>puts
|
2332
|
+
<pre class="ruby"><code>puts flights.select(:year.up_to :day).head.as__data__frame</code></pre>
|
2313
2333
|
<pre><code>## year month day
|
2314
2334
|
## 1 2013 1 1
|
2315
2335
|
## 2 2013 1 1
|
@@ -2318,7 +2338,7 @@ puts @df.as__data__frame</code></pre>
|
|
2318
2338
|
## 5 2013 1 1
|
2319
2339
|
## 6 2013 1 1</code></pre>
|
2320
2340
|
<p>Select all columns that start with a given name sequence</p>
|
2321
|
-
<pre class="ruby"><code>puts
|
2341
|
+
<pre class="ruby"><code>puts flights.select(E.starts_with('arr')).head.as__data__frame</code></pre>
|
2322
2342
|
<pre><code>## arr_time arr_delay
|
2323
2343
|
## 1 830 11
|
2324
2344
|
## 2 850 20
|
@@ -2334,7 +2354,7 @@ puts @df.as__data__frame</code></pre>
|
|
2334
2354
|
<li><p>num_range(“x”, (1..3)): matches x1, x2 and x3</p></li>
|
2335
2355
|
</ul>
|
2336
2356
|
<p>A helper function that comes in handy when we just want to rearrange column order is ‘Everything’:</p>
|
2337
|
-
<pre class="ruby"><code>puts
|
2357
|
+
<pre class="ruby"><code>puts flights.select(:year, :month, :day, E.everything).head.as__data__frame</code></pre>
|
2338
2358
|
<pre><code>## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
|
2339
2359
|
## 1 2013 1 1 517 515 2 830 819
|
2340
2360
|
## 2 2013 1 1 533 529 4 850 830
|
@@ -2359,13 +2379,13 @@ puts @df.as__data__frame</code></pre>
|
|
2359
2379
|
</div>
|
2360
2380
|
<div id="add-variables-to-a-dataframe-with-mutate" class="section level2">
|
2361
2381
|
<h2>Add variables to a dataframe with ‘mutate’</h2>
|
2362
|
-
<pre class="ruby"><code
|
2363
|
-
|
2364
|
-
|
2365
|
-
|
2366
|
-
|
2382
|
+
<pre class="ruby"><code>flights_sm = flights.
|
2383
|
+
select((:year.up_to :day),
|
2384
|
+
E.ends_with('delay'),
|
2385
|
+
:distance,
|
2386
|
+
:air_time)
|
2367
2387
|
|
2368
|
-
puts
|
2388
|
+
puts flights_sm.head.as__data__frame</code></pre>
|
2369
2389
|
<pre><code>## year month day dep_delay arr_delay distance air_time
|
2370
2390
|
## 1 2013 1 1 2 11 1400 227
|
2371
2391
|
## 2 2013 1 1 4 20 1416 227
|
@@ -2373,10 +2393,10 @@ puts @flights_sm.head.as__data__frame</code></pre>
|
|
2373
2393
|
## 4 2013 1 1 -1 -18 1576 183
|
2374
2394
|
## 5 2013 1 1 -6 -25 762 116
|
2375
2395
|
## 6 2013 1 1 -4 12 719 150</code></pre>
|
2376
|
-
<pre class="ruby"><code
|
2377
|
-
|
2378
|
-
|
2379
|
-
puts
|
2396
|
+
<pre class="ruby"><code>flights_sm = flights_sm.
|
2397
|
+
mutate(gain: :dep_delay - :arr_delay,
|
2398
|
+
speed: :distance / :air_time * 60)
|
2399
|
+
puts flights_sm.head.as__data__frame</code></pre>
|
2380
2400
|
<pre><code>## year month day dep_delay arr_delay distance air_time gain speed
|
2381
2401
|
## 1 2013 1 1 2 11 1400 227 -9 370.0441
|
2382
2402
|
## 2 2013 1 1 4 20 1416 227 -16 374.2731
|
@@ -2388,11 +2408,11 @@ puts @flights_sm.head.as__data__frame</code></pre>
|
|
2388
2408
|
<div id="summarising-data" class="section level2">
|
2389
2409
|
<h2>Summarising data</h2>
|
2390
2410
|
<p>Function ‘summarise’ calculates summaries for the data frame. When no ‘group_by’ is used a single value is obtained from the data frame:</p>
|
2391
|
-
<pre class="ruby"><code>puts
|
2411
|
+
<pre class="ruby"><code>puts flights.summarise(delay: E.mean(:dep_delay, na__rm: true)).as__data__frame</code></pre>
|
2392
2412
|
<pre><code>## delay
|
2393
2413
|
## 1 12.63907</code></pre>
|
2394
2414
|
<p>When a data frame is groupe with ‘group_by’ summaries apply to the given group:</p>
|
2395
|
-
<pre class="ruby"><code>by_day =
|
2415
|
+
<pre class="ruby"><code>by_day = flights.group_by(:year, :month, :day)
|
2396
2416
|
puts by_day.summarise(delay: :dep_delay.mean(na__rm: true)).head.as__data__frame</code></pre>
|
2397
2417
|
<pre><code>## year month day delay
|
2398
2418
|
## 1 2013 1 1 11.548926
|
@@ -2402,7 +2422,7 @@ puts by_day.summarise(delay: :dep_delay.mean(na__rm: true)).head.as__data__frame
|
|
2402
2422
|
## 5 2013 1 5 5.732218
|
2403
2423
|
## 6 2013 1 6 7.148014</code></pre>
|
2404
2424
|
<p>Next we put many operations together by pipping them one after the other:</p>
|
2405
|
-
<pre class="ruby"><code>delays =
|
2425
|
+
<pre class="ruby"><code>delays = flights.
|
2406
2426
|
group_by(:dest).
|
2407
2427
|
summarise(
|
2408
2428
|
count: E.n,
|
@@ -2410,105 +2430,14 @@ puts by_day.summarise(delay: :dep_delay.mean(na__rm: true)).head.as__data__frame
|
|
2410
2430
|
delay: :arr_delay.mean(na__rm: true)).
|
2411
2431
|
filter(:count > 20, :dest != "NHL")
|
2412
2432
|
|
2413
|
-
puts delays.as__data__frame</code></pre>
|
2414
|
-
<pre><code>##
|
2415
|
-
## 1
|
2416
|
-
## 2
|
2417
|
-
## 3
|
2418
|
-
## 4
|
2419
|
-
## 5
|
2420
|
-
## 6
|
2421
|
-
## 7 BDL 443 116.00000 7.04854369
|
2422
|
-
## 8 BGR 375 378.00000 8.02793296
|
2423
|
-
## 9 BHM 297 865.99663 16.87732342
|
2424
|
-
## 10 BNA 6333 758.21348 11.81245891
|
2425
|
-
## 11 BOS 15508 190.63696 2.91439222
|
2426
|
-
## 12 BQN 896 1578.98326 8.24549550
|
2427
|
-
## 13 BTV 2589 265.09154 8.95099602
|
2428
|
-
## 14 BUF 4681 296.80837 8.94595186
|
2429
|
-
## 15 BUR 371 2465.00000 8.17567568
|
2430
|
-
## 16 BWI 1781 179.41830 10.72673385
|
2431
|
-
## 17 BZN 36 1882.00000 7.60000000
|
2432
|
-
## 18 CAE 116 603.55172 41.76415094
|
2433
|
-
## 19 CAK 864 397.00000 19.69833729
|
2434
|
-
## 20 CHO 52 305.00000 9.50000000
|
2435
|
-
## 21 CHS 2884 632.91678 10.59296847
|
2436
|
-
## 22 CLE 4573 414.17428 9.18161129
|
2437
|
-
## 23 CLT 14064 538.02730 7.36031885
|
2438
|
-
## 24 CMH 3524 476.55505 10.60132291
|
2439
|
-
## 25 CRW 138 444.00000 14.67164179
|
2440
|
-
## 26 CVG 3941 575.15986 15.36456376
|
2441
|
-
## 27 DAY 1525 537.10230 12.68048606
|
2442
|
-
## 28 DCA 9705 211.00618 9.06695204
|
2443
|
-
## 29 DEN 7266 1614.67836 8.60650021
|
2444
|
-
## 30 DFW 8738 1383.04303 0.32212685
|
2445
|
-
## 31 DSM 569 1020.88752 19.00573614
|
2446
|
-
## 32 DTW 9384 498.12852 5.42996346
|
2447
|
-
## 33 EGE 213 1735.70892 6.30434783
|
2448
|
-
## 34 FLL 12055 1070.06877 8.08212154
|
2449
|
-
## 35 GRR 765 605.78170 18.18956044
|
2450
|
-
## 36 GSO 1606 449.84184 14.11260054
|
2451
|
-
## 37 GSP 849 595.95995 15.93544304
|
2452
|
-
## 38 HNL 707 4972.67468 -1.36519258
|
2453
|
-
## 39 HOU 2115 1420.15508 7.17618819
|
2454
|
-
## 40 IAD 5700 224.84684 13.86420212
|
2455
|
-
## 41 IAH 7198 1407.20672 4.24079040
|
2456
|
-
## 42 ILM 110 500.00000 4.63551402
|
2457
|
-
## 43 IND 2077 652.26288 9.94043412
|
2458
|
-
## 44 JAC 25 1875.60000 28.09523810
|
2459
|
-
## 45 JAX 2720 824.67610 11.84483416
|
2460
|
-
## 46 LAS 5997 2240.96148 0.25772849
|
2461
|
-
## 47 LAX 16174 2468.62236 0.54711094
|
2462
|
-
## 48 LGB 668 2465.00000 -0.06202723
|
2463
|
-
## 49 MCI 2008 1097.69522 14.51405836
|
2464
|
-
## 50 MCO 14082 943.11057 5.45464309
|
2465
|
-
## 51 MDW 4113 718.04595 12.36422360
|
2466
|
-
## 52 MEM 1789 954.20123 10.64531435
|
2467
|
-
## 53 MHT 1009 207.02973 14.78755365
|
2468
|
-
## 54 MIA 11728 1091.55244 0.29905978
|
2469
|
-
## 55 MKE 2802 733.38151 14.16722038
|
2470
|
-
## 56 MSN 572 803.95455 20.19604317
|
2471
|
-
## 57 MSP 7185 1017.40167 7.27016886
|
2472
|
-
## 58 MSY 3799 1177.70571 6.49017497
|
2473
|
-
## 59 MVY 221 173.00000 -0.28571429
|
2474
|
-
## 60 MYR 59 550.66102 4.60344828
|
2475
|
-
## 61 OAK 312 2576.00000 3.07766990
|
2476
|
-
## 62 OKC 346 1325.00000 30.61904762
|
2477
|
-
## 63 OMA 849 1135.56655 14.69889841
|
2478
|
-
## 64 ORD 17283 729.00081 5.87661475
|
2479
|
-
## 65 ORF 1536 288.52344 10.94909344
|
2480
|
-
## 66 PBI 6554 1028.83811 8.56297210
|
2481
|
-
## 67 PDX 1354 2445.56573 5.14157973
|
2482
|
-
## 68 PHL 1632 94.32353 10.12719014
|
2483
|
-
## 69 PHX 4656 2141.30326 2.09704733
|
2484
|
-
## 70 PIT 2875 334.06122 7.68099053
|
2485
|
-
## 71 PSE 365 1617.00000 7.87150838
|
2486
|
-
## 72 PVD 376 160.00000 16.23463687
|
2487
|
-
## 73 PWM 2352 276.12840 11.66040210
|
2488
|
-
## 74 RDU 8163 426.75769 10.05238095
|
2489
|
-
## 75 RIC 2454 281.40465 20.11125320
|
2490
|
-
## 76 ROC 2416 259.25083 11.56064461
|
2491
|
-
## 77 RSW 3537 1072.85327 3.23814963
|
2492
|
-
## 78 SAN 2737 2437.29923 3.13916574
|
2493
|
-
## 79 SAT 686 1578.34111 6.94537178
|
2494
|
-
## 80 SAV 804 709.18408 15.12950601
|
2495
|
-
## 81 SDF 1157 645.98358 12.66938406
|
2496
|
-
## 82 SEA 3923 2412.66531 -1.09909910
|
2497
|
-
## 83 SFO 13331 2577.92356 2.67289152
|
2498
|
-
## 84 SJC 329 2569.00000 3.44817073
|
2499
|
-
## 85 SJU 5819 1599.83365 2.52052659
|
2500
|
-
## 86 SLC 2467 1986.98662 0.17625459
|
2501
|
-
## 87 SMF 284 2521.00000 12.10992908
|
2502
|
-
## 88 SNA 825 2434.00000 -7.86822660
|
2503
|
-
## 89 SRQ 1211 1044.65153 3.08243131
|
2504
|
-
## 90 STL 4339 878.72321 11.07846451
|
2505
|
-
## 91 STT 522 1626.98276 -3.83590734
|
2506
|
-
## 92 SYR 1761 205.92164 8.90392501
|
2507
|
-
## 93 TPA 7466 1003.93557 7.40852503
|
2508
|
-
## 94 TUL 315 1215.00000 33.65986395
|
2509
|
-
## 95 TVC 101 652.38614 12.96842105
|
2510
|
-
## 96 TYS 631 638.80983 24.06920415
|
2511
|
-
## 97 XNA 1036 1142.50579 7.46572581</code></pre>
|
2433
|
+
puts delays.as__data__frame.head</code></pre>
|
2434
|
+
<pre><code>## dest count dist delay
|
2435
|
+
## 1 ABQ 254 1826.0000 4.381890
|
2436
|
+
## 2 ACK 265 199.0000 4.852273
|
2437
|
+
## 3 ALB 439 143.0000 14.397129
|
2438
|
+
## 4 ATL 17215 757.1082 11.300113
|
2439
|
+
## 5 AUS 2439 1514.2530 6.019909
|
2440
|
+
## 6 AVL 275 583.5818 8.003831</code></pre>
|
2512
2441
|
</div>
|
2513
2442
|
</div>
|
2514
2443
|
<div id="using-data-table" class="section level1">
|
@@ -2517,9 +2446,9 @@ puts delays.as__data__frame</code></pre>
|
|
2517
2446
|
R.install_and_loads('curl')
|
2518
2447
|
|
2519
2448
|
input = "https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv"
|
2520
|
-
|
2521
|
-
puts
|
2522
|
-
puts
|
2449
|
+
flights = R.fread(input)
|
2450
|
+
puts flights
|
2451
|
+
puts flights.dim</code></pre>
|
2523
2452
|
<pre><code>## year month day dep_delay arr_delay carrier origin dest air_time
|
2524
2453
|
## 1: 2014 1 1 14 13 AA JFK LAX 359
|
2525
2454
|
## 2: 2014 1 1 -3 13 AA JFK LAX 363
|
@@ -2564,17 +2493,17 @@ puts data_table.ID</code></pre>
|
|
2564
2493
|
## 6: c 6 12 18
|
2565
2494
|
## [1] "b" "b" "b" "a" "a" "c"</code></pre>
|
2566
2495
|
<pre class="ruby"><code># subset rows in i
|
2567
|
-
ans =
|
2496
|
+
ans = flights[(:origin.eq "JFK") & (:month.eq 6)]
|
2568
2497
|
puts ans.head
|
2569
2498
|
|
2570
2499
|
# Get the first two rows from flights.
|
2571
2500
|
|
2572
|
-
ans =
|
2501
|
+
ans = flights[(1..2)]
|
2573
2502
|
puts ans
|
2574
2503
|
|
2575
2504
|
# Sort flights first by column origin in ascending order, and then by dest in descending order:
|
2576
2505
|
|
2577
|
-
# ans =
|
2506
|
+
# ans = flights[E.order(:origin, -(:dest))]
|
2578
2507
|
# puts ans.head</code></pre>
|
2579
2508
|
<pre><code>## year month day dep_delay arr_delay carrier origin dest air_time
|
2580
2509
|
## 1: 2014 6 1 -9 -5 AA JFK LAX 324
|
@@ -2599,15 +2528,15 @@ puts ans
|
|
2599
2528
|
<pre class="ruby"><code># Select column(s) in j
|
2600
2529
|
# select arr_delay column, but return it as a vector.
|
2601
2530
|
|
2602
|
-
ans =
|
2531
|
+
ans = flights[:all, :arr_delay]
|
2603
2532
|
puts ans.head
|
2604
2533
|
|
2605
2534
|
# Select arr_delay column, but return as a data.table instead.
|
2606
2535
|
|
2607
|
-
ans =
|
2536
|
+
ans = flights[:all, :arr_delay.list]
|
2608
2537
|
puts ans.head
|
2609
2538
|
|
2610
|
-
ans =
|
2539
|
+
ans = flights[:all, E.list(:arr_delay, :dep_delay)]</code></pre>
|
2611
2540
|
<pre><code>## [1] 13 13 9 -26 1 0
|
2612
2541
|
## arr_delay
|
2613
2542
|
## 1: 13
|
@@ -2621,113 +2550,407 @@ ans = @flights[:all, E.list(:arr_delay, :dep_delay)]</code></pre>
|
|
2621
2550
|
<h1>Graphics in Galaaz</h1>
|
2622
2551
|
<p>Creating graphics in Galaaz is quite easy, as it can use all the power of ggplot2. There are many resources in the web that teaches ggplot, so here we give a quick example of ggplot integration with Ruby. We continue to use the :mtcars dataset and we will plot a diverging bar plot, showing cars that have ‘above’ or ‘below’ gas consuption. Let’s first prepare the data frame with the necessary data:</p>
|
2623
2552
|
<pre class="ruby"><code># copy the R variable :mtcars to the Ruby mtcars variable
|
2624
|
-
|
2553
|
+
mtcars = ~:mtcars
|
2625
2554
|
|
2626
2555
|
# create a new column 'car_name' to store the car names so that it can be
|
2627
2556
|
# used for plotting. The 'rownames' of the data frame cannot be used as
|
2628
2557
|
# data for plotting
|
2629
|
-
|
2558
|
+
mtcars.car_name = R.rownames(:mtcars)
|
2630
2559
|
|
2631
2560
|
# compute normalized mpg and add it to a new column called mpg_z
|
2632
2561
|
# Note that the mean value for mpg can be obtained by calling the 'mean'
|
2633
2562
|
# function on the vector 'mtcars.mpg'. The same with the standard
|
2634
2563
|
# deviation 'sd'. The vector is then rounded to two digits with 'round 2'
|
2635
|
-
|
2564
|
+
mtcars.mpg_z = ((mtcars.mpg - mtcars.mpg.mean)/mtcars.mpg.sd).round 2
|
2636
2565
|
|
2637
2566
|
# create a new column 'mpg_type'. Function 'ifelse' is a vectorized function
|
2638
2567
|
# that looks at every element of the mpg_z vector and if the value is below
|
2639
2568
|
# 0, returns 'below', otherwise returns 'above'
|
2640
|
-
|
2569
|
+
mtcars.mpg_type = (mtcars.mpg_z < 0).ifelse("below", "above")
|
2641
2570
|
|
2642
2571
|
# order the mtcar data set by the mpg_z vector from smaler to larger values
|
2643
|
-
|
2572
|
+
mtcars = mtcars[mtcars.mpg_z.order, :all]
|
2644
2573
|
|
2645
2574
|
# convert the car_name column to a factor to retain sorted order in plot
|
2646
|
-
|
2575
|
+
mtcars.car_name = mtcars.car_name.factor levels: mtcars.car_name
|
2647
2576
|
|
2648
2577
|
# let's look at the final data frame
|
2649
|
-
puts
|
2650
|
-
<pre><code>## mpg cyl
|
2651
|
-
## Cadillac Fleetwood 10.4 8
|
2652
|
-
## Lincoln Continental 10.4 8
|
2653
|
-
## Camaro Z28 13.3 8
|
2654
|
-
## Duster 360 14.3 8
|
2655
|
-
## Chrysler Imperial 14.7 8
|
2656
|
-
## Maserati Bora 15.0 8
|
2657
|
-
## Merc 450SLC 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3 3
|
2658
|
-
## AMC Javelin 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3 2
|
2659
|
-
## Dodge Challenger 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3 2
|
2660
|
-
## Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4
|
2661
|
-
## Merc 450SE 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3
|
2662
|
-
## Merc 450SL 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3
|
2663
|
-
## Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
|
2664
|
-
## Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
|
2665
|
-
## Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
|
2666
|
-
## Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
|
2667
|
-
## Pontiac Firebird 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2
|
2668
|
-
## Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
|
2669
|
-
## Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
|
2670
|
-
## Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
|
2671
|
-
## Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
|
2672
|
-
## Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
|
2673
|
-
## Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
|
2674
|
-
## Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
|
2675
|
-
## Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
|
2676
|
-
## Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
|
2677
|
-
## Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
|
2678
|
-
## Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
|
2679
|
-
## Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
|
2680
|
-
## Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
|
2681
|
-
## Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
|
2682
|
-
## Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
|
2578
|
+
puts mtcars.head</code></pre>
|
2579
|
+
<pre><code>## mpg cyl disp hp drat wt qsec vs am gear carb
|
2580
|
+
## Cadillac Fleetwood 10.4 8 472 205 2.93 5.250 17.98 0 0 3 4
|
2581
|
+
## Lincoln Continental 10.4 8 460 215 3.00 5.424 17.82 0 0 3 4
|
2582
|
+
## Camaro Z28 13.3 8 350 245 3.73 3.840 15.41 0 0 3 4
|
2583
|
+
## Duster 360 14.3 8 360 245 3.21 3.570 15.84 0 0 3 4
|
2584
|
+
## Chrysler Imperial 14.7 8 440 230 3.23 5.345 17.42 0 0 3 4
|
2585
|
+
## Maserati Bora 15.0 8 301 335 3.54 3.570 14.60 0 1 5 8
|
2683
2586
|
## car_name mpg_z mpg_type
|
2684
2587
|
## Cadillac Fleetwood Cadillac Fleetwood -1.61 below
|
2685
2588
|
## Lincoln Continental Lincoln Continental -1.61 below
|
2686
2589
|
## Camaro Z28 Camaro Z28 -1.13 below
|
2687
2590
|
## Duster 360 Duster 360 -0.96 below
|
2688
2591
|
## Chrysler Imperial Chrysler Imperial -0.89 below
|
2689
|
-
## Maserati Bora Maserati Bora -0.84 below
|
2690
|
-
## Merc 450SLC Merc 450SLC -0.81 below
|
2691
|
-
## AMC Javelin AMC Javelin -0.81 below
|
2692
|
-
## Dodge Challenger Dodge Challenger -0.76 below
|
2693
|
-
## Ford Pantera L Ford Pantera L -0.71 below
|
2694
|
-
## Merc 450SE Merc 450SE -0.61 below
|
2695
|
-
## Merc 450SL Merc 450SL -0.46 below
|
2696
|
-
## Merc 280C Merc 280C -0.38 below
|
2697
|
-
## Valiant Valiant -0.33 below
|
2698
|
-
## Hornet Sportabout Hornet Sportabout -0.23 below
|
2699
|
-
## Merc 280 Merc 280 -0.15 below
|
2700
|
-
## Pontiac Firebird Pontiac Firebird -0.15 below
|
2701
|
-
## Ferrari Dino Ferrari Dino -0.06 below
|
2702
|
-
## Mazda RX4 Mazda RX4 0.15 above
|
2703
|
-
## Mazda RX4 Wag Mazda RX4 Wag 0.15 above
|
2704
|
-
## Hornet 4 Drive Hornet 4 Drive 0.22 above
|
2705
|
-
## Volvo 142E Volvo 142E 0.22 above
|
2706
|
-
## Toyota Corona Toyota Corona 0.23 above
|
2707
|
-
## Datsun 710 Datsun 710 0.45 above
|
2708
|
-
## Merc 230 Merc 230 0.45 above
|
2709
|
-
## Merc 240D Merc 240D 0.72 above
|
2710
|
-
## Porsche 914-2 Porsche 914-2 0.98 above
|
2711
|
-
## Fiat X1-9 Fiat X1-9 1.20 above
|
2712
|
-
## Honda Civic Honda Civic 1.71 above
|
2713
|
-
## Lotus Europa Lotus Europa 1.71 above
|
2714
|
-
## Fiat 128 Fiat 128 2.04 above
|
2715
|
-
## Toyota Corolla Toyota Corolla 2.29 above</code></pre>
|
2592
|
+
## Maserati Bora Maserati Bora -0.84 below</code></pre>
|
2716
2593
|
<p>Now, lets plot the diverging bar plot. When using gKnit, there is no need to call ‘R.awt’ to create a plotting device, since gKnit does take care of it. Galaaz provides integration with ggplot. The interested reader should check online for more information on ggplot, since it is outside the scope of this manual describing how ggplot works. We give here but a brief description on how this plot is generated.</p>
|
2717
2594
|
<p>ggplot implements the ‘grammar of graphics’. In this approach, plots are build by adding layers to the plot. On the first layer we describe what we want on the ‘x’ and ‘y’ axis of the plot. In this case, we have ‘car_name’ on the ‘x’ axis and ‘mpg_z’ on the ‘y’ axis. Then the type of graph is specified by adding ‘geom_bar’ (for a bar graph). We specify that our bars should be filled using ‘mpg_type’, which is either ‘above’ or ‘bellow’ giving then two colours for filling. On the next layer we specify the labels for the graph, then we add the title and subtitle. Finally, in a bar chart usually bars go on the vertical direction, but in this graph we want the bars to be horizontally layed so we add ‘coord_flip’.</p>
|
2718
2595
|
<pre class="ruby"><code>require 'ggplot'
|
2719
2596
|
|
2720
|
-
puts
|
2721
|
-
|
2722
|
-
|
2723
|
-
|
2724
|
-
|
2725
|
-
|
2726
|
-
|
2727
|
-
|
2597
|
+
puts mtcars.ggplot(E.aes(x: :car_name, y: :mpg_z, label: :mpg_z)) +
|
2598
|
+
R.geom_bar(E.aes(fill: :mpg_type), stat: 'identity', width: 0.5) +
|
2599
|
+
R.scale_fill_manual(name: 'Mileage',
|
2600
|
+
labels: R.c('Above Average', 'Below Average'),
|
2601
|
+
values: R.c('above': '#00ba38', 'below': '#f8766d')) +
|
2602
|
+
R.labs(subtitle: "Normalised mileage from 'mtcars'",
|
2603
|
+
title: "Diverging Bars") +
|
2604
|
+
R.coord_flip</code></pre>
|
2728
2605
|
<p><img src="data:image/png;base64,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" /><!-- --></p>
|
2606
|
+
</div>
|
2607
|
+
<div id="coding-with-tidyverse" class="section level1">
|
2608
|
+
<h1>Coding with Tidyverse</h1>
|
2609
|
+
<p>In R, and when coding with ‘tidyverse’, arguments to a function are usually not <em>referencially transparent</em>. That is, you can’t replace a value with a seemingly equivalent object that you’ve defined elsewhere. To see the problem, let’s first define a data frame:</p>
|
2610
|
+
<pre class="ruby"><code>df = R.data__frame(x: (1..3), y: (3..1))
|
2611
|
+
puts df</code></pre>
|
2612
|
+
<pre><code>## x y
|
2613
|
+
## 1 1 3
|
2614
|
+
## 2 2 2
|
2615
|
+
## 3 3 1</code></pre>
|
2616
|
+
<p>and now, let’s look at this code:</p>
|
2617
|
+
<pre class="r"><code>my_var <- x
|
2618
|
+
filter(df, my_var == 1)</code></pre>
|
2619
|
+
<p>It generates the following error: "object ‘x’ not found.</p>
|
2620
|
+
<p>However, in Galaaz, arguments are referencially transparent as can be seen by the code bellow. Note initally that ‘my_var = :x’ will not give the error “object ‘x’ not found” since ‘:x’ is treated as an expression and assigned to my_var. Then when doing (my_var.eq 1), my_var is a variable that resolves to ‘:x’ and it becomes equivalent to (:x.eq 1) which is what we want.</p>
|
2621
|
+
<pre class="ruby"><code>my_var = :x
|
2622
|
+
puts df.filter(my_var.eq 1)</code></pre>
|
2623
|
+
<pre><code>## x y
|
2624
|
+
## 1 1 3</code></pre>
|
2625
|
+
<p>As stated by Hardley</p>
|
2626
|
+
<blockquote>
|
2627
|
+
<p>dplyr code is ambiguous. Depending on what variables are defined where, filter(df, x == y) could be equivalent to any of:</p>
|
2628
|
+
</blockquote>
|
2629
|
+
<pre><code>df[df$x == df$y, ]
|
2630
|
+
df[df$x == y, ]
|
2631
|
+
df[x == df$y, ]
|
2632
|
+
df[x == y, ]</code></pre>
|
2633
|
+
<p>In galaaz this ambiguity does not exist, filter(df, x.eq y) is not a valid expression as expressions are build with symbols. In doing filter(df, :x.eq y) we are looking for elements of the ‘x’ column that are equal to a previously defined y variable. Finally in filter(df, :x.eq :y) we are looking for elements in which the ‘x’ column value is equal to the ‘y’ column value. This can be seen in the following two chunks of code:</p>
|
2634
|
+
<pre class="ruby"><code>y = 1
|
2635
|
+
x = 2
|
2636
|
+
|
2637
|
+
# looking for values where the 'x' column is equal to the 'y' column
|
2638
|
+
puts df.filter(:x.eq :y)</code></pre>
|
2639
|
+
<pre><code>## x y
|
2640
|
+
## 1 2 2</code></pre>
|
2641
|
+
<pre class="ruby"><code># looking for values where the 'x' column is equal to the 'y' variable
|
2642
|
+
# in this case, the number 1
|
2643
|
+
puts df.filter(:x.eq y)</code></pre>
|
2644
|
+
<pre><code>## x y
|
2645
|
+
## 1 1 3</code></pre>
|
2646
|
+
<div id="writing-a-function-that-applies-to-different-data-sets" class="section level2">
|
2647
|
+
<h2>Writing a function that applies to different data sets</h2>
|
2648
|
+
<p>Let’s suppose that we want to write a function that receives as the first argument a data frame and as second argument an expression that adds a column to the data frame that is equal to the sum of elements in column ‘a’ plus ‘x’.</p>
|
2649
|
+
<p>Here is the intended behaviour using the ‘mutate’ function of ‘dplyr’:</p>
|
2650
|
+
<pre><code>mutate(df1, y = a + x)
|
2651
|
+
mutate(df2, y = a + x)
|
2652
|
+
mutate(df3, y = a + x)
|
2653
|
+
mutate(df4, y = a + x)</code></pre>
|
2654
|
+
<p>The naive approach to writing an R function to solve this problem is:</p>
|
2655
|
+
<pre><code>mutate_y <- function(df) {
|
2656
|
+
mutate(df, y = a + x)
|
2657
|
+
}</code></pre>
|
2658
|
+
<p>Unfortunately, in R, this function can fail silently if one of the variables isn’t present in the data frame, but is present in the global environment. We will not go through here how to solve this problem in R.</p>
|
2659
|
+
<p>In Galaaz the method mutate_y bellow will work fine and will never fail silently.</p>
|
2660
|
+
<pre class="ruby"><code>def mutate_y(df)
|
2661
|
+
df.mutate(:y.assign :a + :x)
|
2662
|
+
end</code></pre>
|
2663
|
+
<p>Here we create a data frame that has only one column named ‘x’:</p>
|
2664
|
+
<pre class="ruby"><code>df1 = R.data__frame(x: (1..3))
|
2665
|
+
puts df1</code></pre>
|
2666
|
+
<pre><code>## x
|
2667
|
+
## 1 1
|
2668
|
+
## 2 2
|
2669
|
+
## 3 3</code></pre>
|
2670
|
+
<p>Note that method mutate_y will fail independetly from the fact that variable ‘a’ is defined and in the scope of the method. Variable ‘a’ has no relationship with the symbol ‘:a’ used in the definition of ‘mutate_y’ above:</p>
|
2671
|
+
<pre class="ruby"><code>a = 10
|
2672
|
+
mutate_y(df1)</code></pre>
|
2673
|
+
<pre><code>## Message:
|
2674
|
+
## Error in mutate_impl(.data, dots) :
|
2675
|
+
## Evaluation error: object 'a' not found.
|
2676
|
+
## In addition: Warning message:
|
2677
|
+
## In mutate_impl(.data, dots) :
|
2678
|
+
## mismatched protect/unprotect (unprotect with empty protect stack) (RError)
|
2679
|
+
## Translated to internal error</code></pre>
|
2680
|
+
</div>
|
2681
|
+
<div id="different-expressions" class="section level2">
|
2682
|
+
<h2>Different expressions</h2>
|
2683
|
+
<p>Let’s move to the next problem as presented by Hardley where trying to write a function in R that will receive two argumens, the first a variable and the second an expression is not trivial. Bellow we create a data frame and we want to write a function that groups data by a variable and summarises it by an expression:</p>
|
2684
|
+
<pre class="r"><code>set.seed(123)
|
2685
|
+
|
2686
|
+
df <- data.frame(
|
2687
|
+
g1 = c(1, 1, 2, 2, 2),
|
2688
|
+
g2 = c(1, 2, 1, 2, 1),
|
2689
|
+
a = sample(5),
|
2690
|
+
b = sample(5)
|
2691
|
+
)
|
2692
|
+
|
2693
|
+
as.data.frame(df) </code></pre>
|
2694
|
+
<pre><code>## g1 g2 a b
|
2695
|
+
## 1 1 1 2 1
|
2696
|
+
## 2 1 2 4 3
|
2697
|
+
## 3 2 1 5 4
|
2698
|
+
## 4 2 2 3 2
|
2699
|
+
## 5 2 1 1 5</code></pre>
|
2700
|
+
<pre class="r"><code>d2 <- df %>%
|
2701
|
+
group_by(g1) %>%
|
2702
|
+
summarise(a = mean(a))
|
2703
|
+
|
2704
|
+
as.data.frame(d2) </code></pre>
|
2705
|
+
<pre><code>## g1 a
|
2706
|
+
## 1 1 3
|
2707
|
+
## 2 2 3</code></pre>
|
2708
|
+
<pre class="r"><code>d2 <- df %>%
|
2709
|
+
group_by(g2) %>%
|
2710
|
+
summarise(a = mean(a))
|
2711
|
+
|
2712
|
+
as.data.frame(d2) </code></pre>
|
2713
|
+
<pre><code>## g2 a
|
2714
|
+
## 1 1 2.666667
|
2715
|
+
## 2 2 3.500000</code></pre>
|
2716
|
+
<p>As shown by Hardley, one might expect this function to do the trick:</p>
|
2717
|
+
<pre class="r"><code>my_summarise <- function(df, group_var) {
|
2718
|
+
df %>%
|
2719
|
+
group_by(group_var) %>%
|
2720
|
+
summarise(a = mean(a))
|
2721
|
+
}
|
2722
|
+
|
2723
|
+
# my_summarise(df, g1)
|
2724
|
+
#> Error: Column `group_var` is unknown</code></pre>
|
2725
|
+
<p>In order to solve this problem, coding with dplyr requires the introduction of many new concepts and functions such as ‘quo’, ‘quos’, ‘enquo’, ‘enquos’, ‘!!’ (bang bang), ‘!!!’ (triple bang). Again, we’ll leave to Hardley the explanation on how to use all those functions.</p>
|
2726
|
+
<p>Now, let’s try to implement the same function in galaaz. The next code block first prints the ‘df’ data frame defined previously in R (to access an R variable from Galaaz, we use the tilda operator ‘~’ applied to the R variable name as symbol, i.e., ‘:df’.</p>
|
2727
|
+
<pre class="ruby"><code>puts ~:df</code></pre>
|
2728
|
+
<pre><code>## g1 g2 a b
|
2729
|
+
## 1 1 1 2 1
|
2730
|
+
## 2 1 2 4 3
|
2731
|
+
## 3 2 1 5 4
|
2732
|
+
## 4 2 2 3 2
|
2733
|
+
## 5 2 1 1 5</code></pre>
|
2734
|
+
<p>We then create the ‘my_summarize’ method and call it passing the R data frame and the group by variable ‘:g1’:</p>
|
2735
|
+
<pre class="ruby"><code>def my_summarize(df, group_var)
|
2736
|
+
df.group_by(group_var).
|
2737
|
+
summarize(a: :a.mean)
|
2738
|
+
end
|
2739
|
+
|
2740
|
+
puts my_summarize(:df, :g1).as__data__frame</code></pre>
|
2741
|
+
<pre><code>## g1 a
|
2742
|
+
## 1 1 3
|
2743
|
+
## 2 2 3</code></pre>
|
2744
|
+
<p>It works!!! Well, let’s make sure this was not just some coincidence</p>
|
2745
|
+
<pre class="ruby"><code>puts my_summarize(:df, :g2).as__data__frame</code></pre>
|
2746
|
+
<pre><code>## g2 a
|
2747
|
+
## 1 1 2.666667
|
2748
|
+
## 2 2 3.500000</code></pre>
|
2749
|
+
<p>Great, everything is fine! No magic, no new functions, no complexities, just normal, standard Ruby code. If you’ve ever done NSE in R, this certainly feels much safer and easy to implement.</p>
|
2750
|
+
</div>
|
2751
|
+
<div id="different-input-variables" class="section level2">
|
2752
|
+
<h2>Different input variables</h2>
|
2753
|
+
<p>In the previous section we’ve managed to get rid of all NSE formulation for a simple example, but does this remain true for more complex examples, or will the Galaaz way prove inpractical for more complex code?</p>
|
2754
|
+
<p>In the next example Hardley proposes us to write a function that given an expression such as ‘a’ or ‘a * b’, calculates three summaries. What we want a function that does the same as these R statements:</p>
|
2755
|
+
<pre><code>summarise(df, mean = mean(a), sum = sum(a), n = n())
|
2756
|
+
#> # A tibble: 1 x 3
|
2757
|
+
#> mean sum n
|
2758
|
+
#> <dbl> <int> <int>
|
2759
|
+
#> 1 3 15 5
|
2760
|
+
|
2761
|
+
summarise(df, mean = mean(a * b), sum = sum(a * b), n = n())
|
2762
|
+
#> # A tibble: 1 x 3
|
2763
|
+
#> mean sum n
|
2764
|
+
#> <dbl> <int> <int>
|
2765
|
+
#> 1 9 45 5</code></pre>
|
2766
|
+
<p>Let’s try it in galaaz:</p>
|
2767
|
+
<pre class="ruby"><code>def my_summarise2(df, expr)
|
2768
|
+
df.summarize(
|
2769
|
+
mean: E.mean(expr),
|
2770
|
+
sum: E.sum(expr),
|
2771
|
+
n: E.n
|
2772
|
+
)
|
2773
|
+
end
|
2774
|
+
|
2775
|
+
puts my_summarise2((~:df), :a)
|
2776
|
+
puts "\n"
|
2777
|
+
puts my_summarise2((~:df), :a * :b)</code></pre>
|
2778
|
+
<pre><code>## mean sum n
|
2779
|
+
## 1 3 15 5
|
2780
|
+
##
|
2781
|
+
## mean sum n
|
2782
|
+
## 1 9 45 5</code></pre>
|
2783
|
+
<p>Once again, there is no need to use any special theory or functions. The only point to be careful about is the use of ‘E’ to build expressions from functions ‘mean’, ‘sum’ and ‘n’.</p>
|
2784
|
+
</div>
|
2785
|
+
<div id="different-input-and-output-variable" class="section level2">
|
2786
|
+
<h2>Different input and output variable</h2>
|
2787
|
+
<p>Now the next challenge presented by Hardley is to vary the name of the output variables based on the received expression. So, if the input expression is ‘a’, we want our data frame columns to be named ‘mean_a’ and ‘sum_a’. Now, if the input expression is ‘b’, columns should be named ‘mean_b’ and ‘sum_b’.</p>
|
2788
|
+
<pre><code>mutate(df, mean_a = mean(a), sum_a = sum(a))
|
2789
|
+
#> # A tibble: 5 x 6
|
2790
|
+
#> g1 g2 a b mean_a sum_a
|
2791
|
+
#> <dbl> <dbl> <int> <int> <dbl> <int>
|
2792
|
+
#> 1 1 1 1 3 3 15
|
2793
|
+
#> 2 1 2 4 2 3 15
|
2794
|
+
#> 3 2 1 2 1 3 15
|
2795
|
+
#> 4 2 2 5 4 3 15
|
2796
|
+
#> # … with 1 more row
|
2797
|
+
|
2798
|
+
mutate(df, mean_b = mean(b), sum_b = sum(b))
|
2799
|
+
#> # A tibble: 5 x 6
|
2800
|
+
#> g1 g2 a b mean_b sum_b
|
2801
|
+
#> <dbl> <dbl> <int> <int> <dbl> <int>
|
2802
|
+
#> 1 1 1 1 3 3 15
|
2803
|
+
#> 2 1 2 4 2 3 15
|
2804
|
+
#> 3 2 1 2 1 3 15
|
2805
|
+
#> 4 2 2 5 4 3 15
|
2806
|
+
#> # … with 1 more row</code></pre>
|
2807
|
+
<p>In order to solve this problem in R, Hardley needs to introduce some more new functions and notations: ‘quo_name’ and the ‘:=’ operator from package ‘rlang’</p>
|
2808
|
+
<p>Here is our Ruby code:</p>
|
2809
|
+
<pre class="ruby"><code>def my_mutate(df, expr)
|
2810
|
+
mean_name = "mean_#{expr.to_s}"
|
2811
|
+
sum_name = "sum_#{expr.to_s}"
|
2812
|
+
|
2813
|
+
df.mutate(mean_name => E.mean(expr),
|
2814
|
+
sum_name => E.sum(expr))
|
2815
|
+
end
|
2816
|
+
|
2817
|
+
puts my_mutate((~:df), :a)
|
2818
|
+
puts "\n"
|
2819
|
+
puts my_mutate((~:df), :b)</code></pre>
|
2820
|
+
<pre><code>## g1 g2 a b mean_a sum_a
|
2821
|
+
## 1 1 1 2 1 3 15
|
2822
|
+
## 2 1 2 4 3 3 15
|
2823
|
+
## 3 2 1 5 4 3 15
|
2824
|
+
## 4 2 2 3 2 3 15
|
2825
|
+
## 5 2 1 1 5 3 15
|
2826
|
+
##
|
2827
|
+
## g1 g2 a b mean_b sum_b
|
2828
|
+
## 1 1 1 2 1 3 15
|
2829
|
+
## 2 1 2 4 3 3 15
|
2830
|
+
## 3 2 1 5 4 3 15
|
2831
|
+
## 4 2 2 3 2 3 15
|
2832
|
+
## 5 2 1 1 5 3 15</code></pre>
|
2833
|
+
<p>It really seems that “Non Standard Evaluation” is actually quite standard in Galaaz! But, you might have noticed a small change in the way the arguments to the mutate method were called. In a previous example we used df.summarise(mean: E.mean(:a), …) where the column name was followed by a ‘:’ colom. In this example, we have df.mutate(mean_name => E.mean(expr), …) and variable mean_name is not followed by ‘:’ but by ‘=>’. This is standard Ruby notation.</p>
|
2834
|
+
<p>[explain….]</p>
|
2835
|
+
</div>
|
2836
|
+
<div id="capturing-multiple-variables" class="section level2">
|
2837
|
+
<h2>Capturing multiple variables</h2>
|
2838
|
+
<p>Moving on with new complexities, Hardley proposes us to solve the problem in which the summarise function will receive any number of grouping variables.</p>
|
2839
|
+
<p>This again is quite standard Ruby. In order to receive an undefined number of paramenters the paramenter is preceded by ’*’:</p>
|
2840
|
+
<pre class="ruby"><code>def my_summarise3(df, *group_vars)
|
2841
|
+
df.group_by(*group_vars).
|
2842
|
+
summarise(a: E.mean(:a))
|
2843
|
+
end
|
2844
|
+
|
2845
|
+
puts my_summarise3((~:df), :g1, :g2).as__data__frame</code></pre>
|
2846
|
+
<pre><code>## g1 g2 a
|
2847
|
+
## 1 1 1 2
|
2848
|
+
## 2 1 2 4
|
2849
|
+
## 3 2 1 3
|
2850
|
+
## 4 2 2 3</code></pre>
|
2851
|
+
</div>
|
2852
|
+
<div id="why-does-r-require-nse-and-galaaz-does-not" class="section level2">
|
2853
|
+
<h2>Why does R require NSE and Galaaz does not?</h2>
|
2854
|
+
<p>NSE introduces a number of new concepts, such as ‘quoting’, ‘quasiquotation’, ‘unquoting’ and ‘unquote-splicing’, while in Galaaz none of those concepts are needed. What gives?</p>
|
2855
|
+
<p>R is an extremely flexible language and it has lazy evaluation of parameters. When in R a function is called as ‘summarise(df, a = b)’, the summarise function receives the litteral ‘a = b’ parameter and can work with this as if it were a string. In R, it is not clear what a and b are, they can be expressions or they can be variables, it is up to the function to decide what ‘a = b’ means.</p>
|
2856
|
+
<p>In Ruby, there is no lazy evaluation of parameters and ‘a’ is always a variable and so is ‘b’. Variables assume their value as soon as they are used, so ‘x = a’ is immediately evaluate and variable ‘x’ will receive the value of variable ‘a’ as soon as the Ruby statement is executed. Ruby also provides the notion of a symbol; ‘:a’ is a symbol and does not evaluate to anything. Galaaz uses Ruby symbols to build expressions that are not bound to anything: ‘:a.eq :b’ is clearly an expression and has no relationship whatsoever with the statment ‘a = b’. By using symbols, variables and expressions all the possible ambiguities that are found in R are eliminated in Galaaz.</p>
|
2857
|
+
<p>The main problem that remains, is that in R, functions are not clearly documented as what type of input they are expecting, they might be expecting regular variables or they might be expecting expressions and the R function will know how to deal with an input of the form ‘a = b’, now for the Ruby developer it might not be immediately clear if it should call the function passing the value ‘true’ if variable ‘a’ is equal to variable ‘b’ or if it should call the function passing the expression ‘:a.eq :b’.</p>
|
2858
|
+
</div>
|
2859
|
+
<div id="advanced-dplyr-features" class="section level2">
|
2860
|
+
<h2>Advanced dplyr features</h2>
|
2861
|
+
<p>In the blog: Programming with dplyr by using dplyr (<a href="https://www.r-bloggers.com/programming-with-dplyr-by-using-dplyr/" class="uri">https://www.r-bloggers.com/programming-with-dplyr-by-using-dplyr/</a>) Iñaki Úcar shows surprise that some R users are trying to code in dplyr avoiding the use of NSE. For instance he says:</p>
|
2862
|
+
<blockquote>
|
2863
|
+
<p>Take the example of seplyr. It stands for standard evaluation dplyr, and enables us to program over dplyr without having “to bring in (or study) any deep-theory or heavy-weight tools such as rlang/tidyeval”.</p>
|
2864
|
+
</blockquote>
|
2865
|
+
<p>For me, there isn’t really any surprise that users are trying to avoid dplyr deep-theory. R users frequently are not programmers and learning to code is already hard business, on top of that, having to learn how to ‘quote’ or ‘enquo’ or ‘quos’ or ‘enquos’ is not necessarily a ‘piece of cake’. So much so, that ‘tidyeval’ has some more advanced functions that instead of using quoted expressions, uses strings as arguments.</p>
|
2866
|
+
<p>In the following examples, we show the use of functions ‘group_by_at’, ‘summarise_at’ and ‘rename_at’ that receive strings as argument. The data frame used in ‘starwars’ that describes features of characters in the Starwars movies:</p>
|
2867
|
+
<pre class="ruby"><code>puts (~:starwars).head.as__data__frame</code></pre>
|
2868
|
+
<pre><code>## name height mass hair_color skin_color eye_color birth_year
|
2869
|
+
## 1 Luke Skywalker 172 77 blond fair blue 19.0
|
2870
|
+
## 2 C-3PO 167 75 <NA> gold yellow 112.0
|
2871
|
+
## 3 R2-D2 96 32 <NA> white, blue red 33.0
|
2872
|
+
## 4 Darth Vader 202 136 none white yellow 41.9
|
2873
|
+
## 5 Leia Organa 150 49 brown light brown 19.0
|
2874
|
+
## 6 Owen Lars 178 120 brown, grey light blue 52.0
|
2875
|
+
## gender homeworld species
|
2876
|
+
## 1 male Tatooine Human
|
2877
|
+
## 2 <NA> Tatooine Droid
|
2878
|
+
## 3 <NA> Naboo Droid
|
2879
|
+
## 4 male Tatooine Human
|
2880
|
+
## 5 female Alderaan Human
|
2881
|
+
## 6 male Tatooine Human
|
2882
|
+
## films
|
2883
|
+
## 1 Revenge of the Sith, Return of the Jedi, The Empire Strikes Back, A New Hope, The Force Awakens
|
2884
|
+
## 2 Attack of the Clones, The Phantom Menace, Revenge of the Sith, Return of the Jedi, The Empire Strikes Back, A New Hope
|
2885
|
+
## 3 Attack of the Clones, The Phantom Menace, Revenge of the Sith, Return of the Jedi, The Empire Strikes Back, A New Hope, The Force Awakens
|
2886
|
+
## 4 Revenge of the Sith, Return of the Jedi, The Empire Strikes Back, A New Hope
|
2887
|
+
## 5 Revenge of the Sith, Return of the Jedi, The Empire Strikes Back, A New Hope, The Force Awakens
|
2888
|
+
## 6 Attack of the Clones, Revenge of the Sith, A New Hope
|
2889
|
+
## vehicles starships
|
2890
|
+
## 1 Snowspeeder, Imperial Speeder Bike X-wing, Imperial shuttle
|
2891
|
+
## 2
|
2892
|
+
## 3
|
2893
|
+
## 4 TIE Advanced x1
|
2894
|
+
## 5 Imperial Speeder Bike
|
2895
|
+
## 6</code></pre>
|
2896
|
+
<p>The grouped_mean function bellow will receive a grouping variable and calculate summaries for the value_variables given:</p>
|
2897
|
+
<pre class="r"><code>grouped_mean <- function(data, grouping_variables, value_variables) {
|
2898
|
+
data %>%
|
2899
|
+
group_by_at(grouping_variables) %>%
|
2900
|
+
mutate(count = n()) %>%
|
2901
|
+
summarise_at(c(value_variables, "count"), mean, na.rm = TRUE) %>%
|
2902
|
+
rename_at(value_variables, funs(paste0("mean_", .)))
|
2903
|
+
}
|
2904
|
+
|
2905
|
+
gm = starwars %>%
|
2906
|
+
grouped_mean("eye_color", c("mass", "birth_year"))
|
2907
|
+
|
2908
|
+
as.data.frame(gm) </code></pre>
|
2909
|
+
<pre><code>## eye_color mean_mass mean_birth_year count
|
2910
|
+
## 1 black 76.28571 33.00000 10
|
2911
|
+
## 2 blue 86.51667 67.06923 19
|
2912
|
+
## 3 blue-gray 77.00000 57.00000 1
|
2913
|
+
## 4 brown 66.09231 108.96429 21
|
2914
|
+
## 5 dark NaN NaN 1
|
2915
|
+
## 6 gold NaN NaN 1
|
2916
|
+
## 7 green, yellow 159.00000 NaN 1
|
2917
|
+
## 8 hazel 66.00000 34.50000 3
|
2918
|
+
## 9 orange 282.33333 231.00000 8
|
2919
|
+
## 10 pink NaN NaN 1
|
2920
|
+
## 11 red 81.40000 33.66667 5
|
2921
|
+
## 12 red, blue NaN NaN 1
|
2922
|
+
## 13 unknown 31.50000 NaN 3
|
2923
|
+
## 14 white 48.00000 NaN 1
|
2924
|
+
## 15 yellow 81.11111 76.38000 11</code></pre>
|
2925
|
+
<p>The same code with Galaaz, becomes:</p>
|
2926
|
+
<pre class="ruby"><code>def grouped_mean(data, grouping_variables, value_variables)
|
2927
|
+
data.
|
2928
|
+
group_by_at(grouping_variables).
|
2929
|
+
mutate(count: E.n).
|
2930
|
+
summarise_at(E.c(value_variables, "count"), ~:mean, na__rm: true).
|
2931
|
+
rename_at(value_variables, E.funs(E.paste0("mean_", value_variables)))
|
2932
|
+
end
|
2933
|
+
|
2934
|
+
puts grouped_mean((~:starwars), "eye_color", E.c("mass", "birth_year")).as__data__frame</code></pre>
|
2935
|
+
<pre><code>## eye_color mean_mass mean_birth_year count
|
2936
|
+
## 1 black 76.28571 33.00000 10
|
2937
|
+
## 2 blue 86.51667 67.06923 19
|
2938
|
+
## 3 blue-gray 77.00000 57.00000 1
|
2939
|
+
## 4 brown 66.09231 108.96429 21
|
2940
|
+
## 5 dark NaN NaN 1
|
2941
|
+
## 6 gold NaN NaN 1
|
2942
|
+
## 7 green, yellow 159.00000 NaN 1
|
2943
|
+
## 8 hazel 66.00000 34.50000 3
|
2944
|
+
## 9 orange 282.33333 231.00000 8
|
2945
|
+
## 10 pink NaN NaN 1
|
2946
|
+
## 11 red 81.40000 33.66667 5
|
2947
|
+
## 12 red, blue NaN NaN 1
|
2948
|
+
## 13 unknown 31.50000 NaN 3
|
2949
|
+
## 14 white 48.00000 NaN 1
|
2950
|
+
## 15 yellow 81.11111 76.38000 11</code></pre>
|
2729
2951
|
<p>[TO BE CONTINUED…]</p>
|
2730
2952
|
</div>
|
2953
|
+
</div>
|
2731
2954
|
<div id="contributing" class="section level1">
|
2732
2955
|
<h1>Contributing</h1>
|
2733
2956
|
<ul>
|