galaaz 0.5.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/LICENSE +0 -0
  4. data/README.md +1416 -667
  5. data/Rakefile +68 -41
  6. data/bin/galaaz-bootstrap +137 -0
  7. data/bin/galaaz-jruby +11 -0
  8. data/bin/galaaz-ruby +16 -0
  9. data/bin/galaaz_jruby_env.inc.sh +6 -0
  10. data/bin/galaaz_ruby_env.inc.sh +36 -0
  11. data/bin/gbookdown +63 -0
  12. data/bin/gknit +83 -13
  13. data/bin/gknit-draft.rb +0 -0
  14. data/bin/gstudio +5 -3
  15. data/bin/gstudio_irb.rb +0 -0
  16. data/bin/gstudio_pry.rb +0 -0
  17. data/bin/install-tinytex +6 -0
  18. data/bin/run_all_rspec +44 -0
  19. data/bin/run_example +17 -0
  20. data/bin/run_old_rspec +20 -0
  21. data/bin/run_rspec +24 -0
  22. data/bin/run_rspec_subset +38 -0
  23. data/bin/run_slow_rspec +20 -0
  24. data/blogs/R-on-Rails-Planning-Document.md +940 -0
  25. data/blogs/README.md +100 -0
  26. data/blogs/galaaz_ggplot/galaaz_ggplot.Rmd +38 -66
  27. data/blogs/galaaz_ggplot/galaaz_ggplot.log +754 -0
  28. data/blogs/galaaz_ggplot/galaaz_ggplot.md +115 -155
  29. data/blogs/galaaz_ggplot/galaaz_ggplot.tex +607 -0
  30. data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-gfm/midwest_rb.png +0 -0
  31. data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-gfm/scatter_plot_rb.png +0 -0
  32. data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-html/midwest_rb.png +0 -0
  33. data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-html/scatter_plot_rb.png +0 -0
  34. data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-markdown_github/midwest_rb.png +0 -0
  35. data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-markdown_github/scatter_plot_rb.png +0 -0
  36. data/blogs/galaaz_ggplot/midwest.Rmd +3 -3
  37. data/blogs/galaaz_ggplot/midwest_external_png +0 -0
  38. data/blogs/gknit/gknit.Rmd +47 -52
  39. data/blogs/gknit/gknit.md +1430 -0
  40. data/blogs/gknit/gknit_files/figure-gfm/bubble-1.png +0 -0
  41. data/blogs/gknit/gknit_files/figure-gfm/diverging_bar.png +0 -0
  42. data/blogs/gknit/gknit_files/figure-html/bubble-1.png +0 -0
  43. data/blogs/gknit/gknit_files/figure-html/diverging_bar.png +0 -0
  44. data/blogs/gknit/lst.rds +0 -0
  45. data/blogs/gknit/model.rb +1 -1
  46. data/blogs/gknit/stats.bib +0 -0
  47. data/blogs/manual/include_model_local_repro.Rmd +14 -0
  48. data/blogs/manual/include_model_local_repro.md +75 -0
  49. data/blogs/manual/lst.rds +0 -0
  50. data/blogs/manual/manual.Rmd +855 -239
  51. data/blogs/manual/manual.log +1786 -0
  52. data/blogs/manual/manual.md +1416 -667
  53. data/blogs/manual/manual.tex +1883 -1161
  54. data/blogs/manual/manual_files/figure-html/bubble-1.png +0 -0
  55. data/blogs/manual/manual_files/figure-html/diverging_bar.png +0 -0
  56. data/blogs/manual/manual_files/figure-latex/bubble-1.png +0 -0
  57. data/blogs/manual/model.rb +1 -1
  58. data/blogs/nse_dplyr/nse_dplyr.Rmd +84 -111
  59. data/blogs/nse_dplyr/nse_dplyr.log +928 -0
  60. data/blogs/nse_dplyr/nse_dplyr.md +198 -229
  61. data/blogs/oh_my/not_so.rb +0 -0
  62. data/blogs/oh_my/oh_my.Rmd +1234 -25
  63. data/blogs/oh_my/oh_my.log +804 -0
  64. data/blogs/oh_my/oh_my.md +1663 -86
  65. data/blogs/oh_my/oh_my.tex +821 -0
  66. data/blogs/oh_my/old.Rmd +15 -14
  67. data/blogs/ruby_plot/ruby_plot.Rmd +58 -82
  68. data/blogs/ruby_plot/ruby_plot.log +885 -0
  69. data/blogs/ruby_plot/ruby_plot.md +71 -102
  70. data/blogs/ruby_plot/ruby_plot.tex +940 -0
  71. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/dose_len.png +0 -0
  72. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facet_by_delivery.png +0 -0
  73. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facet_by_dose.png +0 -0
  74. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facets_by_delivery_color.png +0 -0
  75. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facets_by_delivery_color2.png +0 -0
  76. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facets_with_decorations.png +0 -0
  77. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facets_with_jitter.png +0 -0
  78. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facets_with_points.png +0 -0
  79. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/final_box_plot.png +0 -0
  80. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/final_violin_plot.png +0 -0
  81. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/violin_with_jitter.png +0 -0
  82. data/blogs/ruby_plot/ruby_plot_files/figure-html/dose_len.png +0 -0
  83. data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_delivery.png +0 -0
  84. data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_dose.png +0 -0
  85. data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color.png +0 -0
  86. data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color2.png +0 -0
  87. data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_decorations.png +0 -0
  88. data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_jitter.png +0 -0
  89. data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_points.png +0 -0
  90. data/blogs/ruby_plot/ruby_plot_files/figure-html/final_box_plot.png +0 -0
  91. data/blogs/ruby_plot/ruby_plot_files/figure-html/final_violin_plot.png +0 -0
  92. data/blogs/ruby_plot/ruby_plot_files/figure-html/violin_with_jitter.png +0 -0
  93. data/blogs/ruby_plot/ruby_plot_files/figure-latex/dose_len.png +0 -0
  94. data/blogs/ruby_plot/ruby_plot_files/figure-latex/facet_by_delivery.png +0 -0
  95. data/blogs/ruby_plot/ruby_plot_files/figure-latex/facet_by_dose.png +0 -0
  96. data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_by_delivery_color.png +0 -0
  97. data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_by_delivery_color2.png +0 -0
  98. data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_decorations.png +0 -0
  99. data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_jitter.png +0 -0
  100. data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_points.png +0 -0
  101. data/blogs/ruby_plot/ruby_plot_files/figure-latex/final_box_plot.png +0 -0
  102. data/blogs/ruby_plot/ruby_plot_files/figure-latex/final_violin_plot.png +0 -0
  103. data/blogs/ruby_plot/ruby_plot_files/figure-latex/violin_with_jitter.png +0 -0
  104. data/blogs/test/test.Rmd +14 -0
  105. data/blogs/test/test.md +10 -0
  106. data/examples/50Plots_MasterList/Images/midwest-scatterplot.PNG +0 -0
  107. data/examples/50Plots_MasterList/ScatterPlot.rb +2 -1
  108. data/examples/50Plots_MasterList/scatter_plot.rb +1 -0
  109. data/examples/Bibliography/master.bib +0 -0
  110. data/examples/Bibliography/stats.bib +0 -0
  111. data/examples/R/calc.R +0 -0
  112. data/examples/R/java_interop.R +0 -0
  113. data/examples/bioconductor_deseq2_airway/Documentation/DESeq2-airway-walkthrough.md +56 -0
  114. data/examples/bioconductor_deseq2_airway/bench_galaaz_three_same_process.rb +54 -0
  115. data/examples/bioconductor_deseq2_airway/bench_r_three_same_process.R +34 -0
  116. data/examples/bioconductor_deseq2_airway/deseq2_airway_galaaz.rb +34 -0
  117. data/examples/bioconductor_deseq2_airway/deseq2_airway_galaaz_optimized.rb +35 -0
  118. data/examples/bioconductor_deseq2_airway/deseq2_airway_minimal.R +30 -0
  119. data/examples/bioconductor_deseq2_airway/deseq2_airway_pipeline_for_bench.R +36 -0
  120. data/examples/islr/all.rb +14 -0
  121. data/examples/islr/ch2.spec.rb +38 -7
  122. data/examples/islr/ch3.spec.rb +12 -2
  123. data/examples/islr/ch3_boston.rb +28 -0
  124. data/examples/islr/ch3_multiple_regression.rb +1 -0
  125. data/examples/islr/ch6.spec.rb +25 -1
  126. data/examples/islr/x_y_rnorm.jpg +0 -0
  127. data/examples/latex_templates/Test-acm_article/acm_proc_article-sp.cls +0 -0
  128. data/examples/latex_templates/Test-acm_article/sigproc.bib +0 -0
  129. data/examples/latex_templates/Test-acs_article/acs-Test-acs_article.bib +0 -0
  130. data/examples/latex_templates/Test-acs_article/acs-my_output.bib +0 -0
  131. data/examples/latex_templates/Test-aea_article/BibFile.bib +0 -0
  132. data/examples/latex_templates/Test-aea_article/Test-aea_article.Rmd +0 -0
  133. data/examples/latex_templates/Test-aea_article/references.bib +0 -0
  134. data/examples/latex_templates/Test-amq_article/Test-amq_article.Rmd +0 -0
  135. data/examples/latex_templates/Test-amq_article/Test-amq_article.pdfsync +0 -0
  136. data/examples/latex_templates/Test-ieee_article/IEEEtran.bst +0 -0
  137. data/examples/latex_templates/Test-ieee_article/mybibfile.bib +0 -0
  138. data/examples/latex_templates/Test-rjournal_article/RJournal.sty +0 -0
  139. data/examples/latex_templates/Test-rjournal_article/RJreferences.bib +0 -0
  140. data/examples/latex_templates/Test-rjournal_article/Test-rjournal_article.Rmd +0 -0
  141. data/examples/misc/baseball.csv +0 -0
  142. data/examples/misc/ggplot.rb +5 -3
  143. data/examples/misc/moneyball.rb +1 -0
  144. data/examples/misc/subsetting.rb +1 -0
  145. data/examples/multithread_shards_to_r/shards_to_r.rb +68 -0
  146. data/examples/rmarkdown/svm-rmarkdown-anon-ms-example/svm-rmarkdown-anon-ms-example.Rmd +0 -0
  147. data/examples/rmarkdown/svm-rmarkdown-article-example/svm-rmarkdown-article-example.Rmd +0 -0
  148. data/examples/rmarkdown/svm-rmarkdown-beamer-example/svm-rmarkdown-beamer-example.Rmd +0 -0
  149. data/examples/rmarkdown/svm-rmarkdown-cv/svm-rmarkdown-cv.Rmd +0 -0
  150. data/examples/rmarkdown/svm-rmarkdown-syllabus-example/attend-grade-relationships.csv +0 -0
  151. data/examples/rmarkdown/svm-rmarkdown-syllabus-example/svm-rmarkdown-syllabus-example.Rmd +0 -0
  152. data/examples/rmarkdown/svm-xaringan-example/svm-xaringan-example.Rmd +0 -0
  153. data/examples/sthda_ggplot/README.md +0 -0
  154. data/examples/sthda_ggplot/RUN.md +41 -0
  155. data/examples/sthda_ggplot/all.rb +1 -0
  156. data/examples/sthda_ggplot/one_variable_continuous/density_gg.rb +1 -0
  157. data/examples/sthda_ggplot/one_variable_continuous/geom_area.rb +1 -0
  158. data/examples/sthda_ggplot/one_variable_continuous/geom_density.rb +3 -0
  159. data/examples/sthda_ggplot/one_variable_continuous/geom_dotplot.rb +1 -0
  160. data/examples/sthda_ggplot/one_variable_continuous/geom_freqpoly.rb +1 -0
  161. data/examples/sthda_ggplot/one_variable_continuous/geom_histogram.rb +1 -0
  162. data/examples/sthda_ggplot/one_variable_continuous/histogram_density.rb +1 -0
  163. data/examples/sthda_ggplot/one_variable_continuous/stat.rb +1 -0
  164. data/examples/sthda_ggplot/one_variable_discrete/bar.rb +1 -0
  165. data/examples/sthda_ggplot/qplots/box_violin_dot.rb +1 -0
  166. data/examples/sthda_ggplot/qplots/scatter_plots.rb +1 -0
  167. data/examples/sthda_ggplot/scatter_gg.rb +1 -0
  168. data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_bin2d.rb +1 -0
  169. data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_density2d.rb +1 -0
  170. data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_hex.rb +1 -0
  171. data/examples/sthda_ggplot/two_variables_cont_cont/geom_point.rb +1 -0
  172. data/examples/sthda_ggplot/two_variables_cont_cont/geom_smooth.rb +1 -0
  173. data/examples/sthda_ggplot/two_variables_cont_cont/misc.rb +1 -0
  174. data/examples/sthda_ggplot/two_variables_cont_function/geom_area.rb +5 -3
  175. data/examples/sthda_ggplot/two_variables_disc_cont/geom_bar.rb +1 -0
  176. data/examples/sthda_ggplot/two_variables_disc_cont/geom_boxplot.rb +1 -0
  177. data/examples/sthda_ggplot/two_variables_disc_cont/geom_dotplot.rb +1 -0
  178. data/examples/sthda_ggplot/two_variables_disc_cont/geom_jitter.rb +1 -0
  179. data/examples/sthda_ggplot/two_variables_disc_cont/geom_line.rb +1 -0
  180. data/examples/sthda_ggplot/two_variables_disc_cont/geom_violin.rb +1 -0
  181. data/examples/sthda_ggplot/two_variables_disc_disc/geom_jitter.rb +1 -0
  182. data/examples/sthda_ggplot/two_variables_error/geom_crossbar.rb +1 -0
  183. data/ext/new_bridge/Makefile +46 -0
  184. data/ext/new_bridge/galaaz_gatekeeper_phase0.cpp +12 -0
  185. data/ext/new_bridge/galaaz_gatekeeper_phase1.cpp +1639 -0
  186. data/lib/R_interface/galaaz_device.R +20 -0
  187. data/lib/R_interface/include_engine.R +109 -0
  188. data/lib/R_interface/new_bridge_adapter.rb +824 -0
  189. data/lib/R_interface/r.rb +177 -25
  190. data/lib/R_interface/r_arrow.rb +113 -0
  191. data/lib/R_interface/r_libs.R +3 -3
  192. data/lib/R_interface/r_methods.rb +13 -126
  193. data/lib/R_interface/r_module_s.rb +0 -0
  194. data/lib/R_interface/rbinary_operators.rb +20 -2
  195. data/lib/R_interface/rclosure.rb +5 -1
  196. data/lib/R_interface/rdata_frame.rb +34 -70
  197. data/lib/R_interface/rdevice.rb +125 -0
  198. data/lib/R_interface/rdevices.R +0 -0
  199. data/lib/R_interface/renvironment.rb +10 -4
  200. data/lib/R_interface/rexpression.rb +5 -1
  201. data/lib/R_interface/rindexed_object.rb +41 -13
  202. data/lib/R_interface/rlanguage.rb +20 -62
  203. data/lib/R_interface/rlist.rb +115 -25
  204. data/lib/R_interface/rlogical_operators.rb +0 -0
  205. data/lib/R_interface/rmatrix.rb +2 -11
  206. data/lib/R_interface/rmd_indexed_object.rb +5 -1
  207. data/lib/R_interface/robject.rb +348 -290
  208. data/lib/R_interface/rpkg.rb +0 -0
  209. data/lib/R_interface/rsupport.rb +609 -328
  210. data/lib/R_interface/rsupport_scope.rb +2 -1
  211. data/lib/R_interface/rsymbol.rb +50 -0
  212. data/lib/R_interface/ruby_callback.rb +2 -3
  213. data/lib/R_interface/ruby_extensions.rb +225 -175
  214. data/lib/R_interface/runary_operators.rb +0 -0
  215. data/lib/R_interface/rvector.rb +162 -31
  216. data/lib/galaaz.rb +0 -0
  217. data/lib/galaaz_jruby.rb +22 -0
  218. data/lib/galaaz_ruby.rb +34 -0
  219. data/lib/gknit/diagnostics.rb +50 -0
  220. data/lib/gknit/draft.rb +23 -17
  221. data/lib/gknit/include_engine.rb +15 -7
  222. data/lib/gknit/knitr_engine.rb +223 -74
  223. data/lib/gknit/rb_engine.rb +3 -3
  224. data/lib/gknit/ruby_engine.rb +0 -0
  225. data/lib/gknit.rb +1 -0
  226. data/lib/new_bridge/bootstrap/windows_bootstrap.rb +285 -0
  227. data/lib/new_bridge/envelope.rb +51 -0
  228. data/lib/new_bridge/eval_result.rb +26 -0
  229. data/lib/new_bridge/framing.rb +39 -0
  230. data/lib/new_bridge/instance_pool_client.rb +38 -0
  231. data/lib/new_bridge/r_instance_manager.rb +404 -0
  232. data/lib/new_bridge/session_client.rb +530 -0
  233. data/lib/new_bridge/tcp_framed.rb +44 -0
  234. data/lib/new_bridge.rb +9 -0
  235. data/lib/util/exec_ruby.rb +95 -20
  236. data/lib/util/inline_file.rb +35 -30
  237. data/new_bridge_specs/benchmark_phase5_5_unboxing_spec.rb +96 -0
  238. data/new_bridge_specs/eval_r_async_spec.rb +113 -0
  239. data/new_bridge_specs/integration_phase5_1_concurrent_spec.rb +50 -0
  240. data/new_bridge_specs/integration_phase5_1_eval_spec.rb +16 -0
  241. data/new_bridge_specs/integration_phase5_1_r_api_spec.rb +25 -0
  242. data/new_bridge_specs/integration_phase5_1_smoke_spec.rb +31 -0
  243. data/new_bridge_specs/integration_phase5_2_dataframe_unboxing_spec.rb +19 -0
  244. data/new_bridge_specs/integration_phase5_2_handle_eval_unboxing_spec.rb +25 -0
  245. data/new_bridge_specs/integration_phase5_3_callback_args_spec.rb +28 -0
  246. data/new_bridge_specs/integration_phase5_3_callback_error_spec.rb +22 -0
  247. data/new_bridge_specs/integration_phase5_3_callback_timeout_spec.rb +28 -0
  248. data/new_bridge_specs/integration_phase5_3_callbacks_smoke_spec.rb +22 -0
  249. data/new_bridge_specs/integration_phase5_3_edge_cases_spec.rb +52 -0
  250. data/new_bridge_specs/integration_phase5_3_nested_spec.rb +30 -0
  251. data/new_bridge_specs/integration_phase5_4_concurrent_sessions_spec.rb +53 -0
  252. data/new_bridge_specs/integration_phase5_4_nested_session_callbacks_spec.rb +49 -0
  253. data/new_bridge_specs/integration_phase5_4_session_routing_spec.rb +38 -0
  254. data/new_bridge_specs/integration_phase5_5_stress_concurrency_spec.rb +52 -0
  255. data/new_bridge_specs/integration_phase5_5_unbox_walk_spec.rb +46 -0
  256. data/new_bridge_specs/phase0_protocol_spec.rb +96 -0
  257. data/new_bridge_specs/phase1_req_ret_spec.rb +66 -0
  258. data/new_bridge_specs/phase2_multi_instance_spec.rb +67 -0
  259. data/new_bridge_specs/phase3_callbacks_spec.rb +71 -0
  260. data/new_bridge_specs/phase4_2_hardening_spec.rb +252 -0
  261. data/new_bridge_specs/phase4_3_r_instance_manager_spec.rb +85 -0
  262. data/new_bridge_specs/phase4_nested_callbacks_spec.rb +123 -0
  263. data/r_requires/ggplot.rb +0 -0
  264. data/r_requires/knitr.rb +0 -0
  265. data/specs/all.rb +15 -11
  266. data/specs/arrow_from_ruby_batches_spec.rb +50 -0
  267. data/specs/arrow_semantics_spec.rb +64 -0
  268. data/specs/bridge_concurrent_spec.rb +46 -0
  269. data/specs/bridge_nested_spec.rb +25 -0
  270. data/specs/dataframe_semantics_spec.rb +122 -0
  271. data/specs/dataframe_single_index_logical_filter_spec.rb +21 -0
  272. data/specs/dispatch_probe_cache_spec.rb +38 -0
  273. data/specs/dispatch_probe_error_class_fallback_spec.rb +20 -0
  274. data/specs/dispatch_probe_fallback_spec.rb +18 -0
  275. data/specs/environment_semantics_spec.rb +89 -0
  276. data/specs/field_access_spec.rb +31 -0
  277. data/specs/figures/bg.jpeg +0 -0
  278. data/specs/figures/bg.png +0 -0
  279. data/specs/figures/bg.svg +168 -57
  280. data/specs/figures/dose_len.png +0 -0
  281. data/specs/figures/no_args.jpeg +0 -0
  282. data/specs/figures/no_args.png +0 -0
  283. data/specs/figures/no_args.svg +168 -57
  284. data/specs/figures/width_height.jpeg +0 -0
  285. data/specs/figures/width_height.png +0 -0
  286. data/specs/figures/width_height_units1.jpeg +0 -0
  287. data/specs/figures/width_height_units1.png +0 -0
  288. data/specs/figures/width_height_units2.jpeg +0 -0
  289. data/specs/figures/width_height_units2.png +0 -0
  290. data/specs/formula_semantics_spec.rb +81 -0
  291. data/specs/galaaz_util_exec_ruby_spec.rb +85 -0
  292. data/specs/galaaz_util_inline_file_spec.rb +54 -0
  293. data/specs/gknit_cli_option_permutation_spec.rb +24 -0
  294. data/specs/gknit_include_engine_spec.rb +72 -0
  295. data/specs/gknit_install_timeout_report_spec.rb +69 -0
  296. data/specs/gknit_internal_error_report_spec.rb +57 -0
  297. data/specs/gknit_vector_map_output_spec.rb +59 -0
  298. data/specs/globalenv_guardrail_spec.rb +52 -0
  299. data/specs/language_expression_semantics_spec.rb +145 -0
  300. data/specs/list_semantics_spec.rb +111 -0
  301. data/specs/new_bridge_bulk_dataframe_transfer_spec.rb +44 -0
  302. data/specs/new_bridge_bulk_vector_transfer_spec.rb +73 -0
  303. data/specs/new_bridge_callback_timeout_spec.rb +69 -0
  304. data/specs/new_bridge_eval_r_fallback_spec.rb +55 -0
  305. data/specs/nil_null_spec.rb +42 -0
  306. data/specs/object_build_phase2_spec.rb +53 -0
  307. data/specs/phase1_callback_bridge_spec.rb +84 -0
  308. data/specs/phase2_gknit_generic_rendering_guardrail_spec.rb +46 -0
  309. data/specs/phase2_gknit_no_raw_code_leakage_spec.rb +43 -0
  310. data/specs/phase3_gknit_generic_graphics_capture_spec.rb +71 -0
  311. data/specs/plot_device_semantics_spec.rb +28 -0
  312. data/specs/plot_snapshot_semantics_spec.rb +58 -0
  313. data/specs/protocol_result_spec.rb +236 -0
  314. data/specs/r_batch_fail_fast_spec.rb +47 -0
  315. data/specs/r_bridge_bootstrap_spec.rb +11 -0
  316. data/specs/r_devices.spec.rb +1 -1
  317. data/specs/r_eval.spec.rb +16 -18
  318. data/specs/r_function.spec.rb +1 -1
  319. data/specs/r_instance_manager_spec.rb +285 -0
  320. data/specs/r_list_apply.spec.rb +15 -15
  321. data/specs/r_matrix.spec.rb +0 -0
  322. data/specs/r_nse.spec.rb +5 -5
  323. data/specs/r_object_send_dispatch_spec.rb +13 -0
  324. data/specs/r_vector_comparator_spec.rb +8 -0
  325. data/specs/r_vector_creation.spec.rb +0 -0
  326. data/specs/r_vector_functions.spec.rb +0 -0
  327. data/specs/r_vector_object.spec.rb +0 -0
  328. data/specs/r_vector_operators.spec.rb +0 -0
  329. data/specs/r_vector_structured_scalar_reads_spec.rb +35 -0
  330. data/specs/r_vector_subsetting.spec.rb +0 -0
  331. data/specs/range_helper_spec.rb +21 -0
  332. data/specs/rsupport_scope_spec.rb +28 -0
  333. data/specs/rsupport_var_name_thread_safety_spec.rb +24 -0
  334. data/specs/scalar_character_spec.rb +44 -0
  335. data/specs/scoped_symbol_dsl_refinement_spec.rb +40 -0
  336. data/specs/session_env_bridge_spec.rb +25 -0
  337. data/specs/simplecov_bootstrap_spec.rb +10 -0
  338. data/specs/spec_helper.rb +10 -0
  339. data/specs/tmp.rb +0 -0
  340. data/specs/unboxing_recursion_regression_spec.rb +30 -0
  341. data/specs/unboxing_spec.rb +49 -0
  342. data/specs/verify_callbacks.rb +42 -0
  343. data/sty/galaaz.sty +0 -0
  344. data/version.rb +1 -1
  345. metadata +219 -63
  346. data/blogs/galaaz_ggplot/galaaz_ggplot.html +0 -520
  347. data/blogs/galaaz_ggplot/galaaz_ggplot.pdf +0 -0
  348. data/blogs/galaaz_ggplot/midwest.html +0 -188
  349. data/blogs/gknit/gknit.html +0 -2266
  350. data/blogs/gknit/gknit.pdf +0 -0
  351. data/blogs/manual/manual.html +0 -4638
  352. data/blogs/manual/manual.pdf +0 -0
  353. data/blogs/manual/manual_files/figure-latex/diverging_bar.pdf +0 -0
  354. data/blogs/nse_dplyr/nse_dplyr.html +0 -878
  355. data/blogs/nse_dplyr/nse_dplyr.pdf +0 -0
  356. data/blogs/oh_my/oh_my.html +0 -568
  357. data/blogs/ruby_plot/ruby_plot.html +0 -544
  358. data/blogs/ruby_plot/ruby_plot.pdf +0 -0
  359. data/examples/latex_templates/Test-acs_article/Test-acs_article.pdf +0 -0
  360. data/examples/latex_templates/Test-aea_article/Test-aea_article.pdf +0 -0
  361. data/examples/latex_templates/Test-amq_article/Test-amq_article.pdf +0 -0
  362. data/examples/latex_templates/Test-amq_article/pics/Figure2.pdf +0 -0
  363. data/examples/latex_templates/Test-asa_article/Test-asa_article.pdf +0 -0
  364. data/examples/latex_templates/Test-ieee_article/Test-ieee_article.pdf +0 -0
  365. data/examples/latex_templates/Test-rjournal_article/RJwrapper.pdf +0 -0
  366. data/examples/latex_templates/Test-springer_article/Test-springer_article.pdf +0 -0
  367. data/examples/rmarkdown/svm-rmarkdown-anon-ms-example/svm-rmarkdown-anon-ms-example.pdf +0 -0
  368. data/examples/rmarkdown/svm-rmarkdown-article-example/svm-rmarkdown-article-example.pdf +0 -0
  369. data/examples/rmarkdown/svm-rmarkdown-beamer-example/svm-rmarkdown-beamer-example.pdf +0 -0
  370. data/examples/rmarkdown/svm-rmarkdown-cv/svm-rmarkdown-cv.pdf +0 -0
  371. data/examples/rmarkdown/svm-rmarkdown-syllabus-example/svm-rmarkdown-syllabus-example.pdf +0 -0
  372. data/specs/r_dataframe.spec.rb +0 -379
  373. data/specs/r_environment.spec.rb +0 -140
  374. data/specs/r_formula.spec.rb +0 -232
  375. data/specs/r_language.spec.rb +0 -112
  376. data/specs/r_list.spec.rb +0 -293
  377. data/specs/r_plots.spec.rb +0 -72
  378. data/specs/ruby_expression.spec.rb +0 -316
data/README.md CHANGED
@@ -1,16 +1,17 @@
1
1
  ---
2
2
  title: "Galaaz Manual"
3
- subtitle: "How to tightly couple Ruby and R in GraalVM"
3
+ subtitle: "R-on-Rails: GNU R meets Ruby for the web"
4
4
  author: "Rodrigo Botafogo"
5
- tags: [Galaaz, Ruby, R, TruffleRuby, FastR, GraalVM, ggplot2]
6
- date: "2019"
7
- bibliography: "/home/rbotafogo/Bibliography/stats.bib"
5
+ tags: [Galaaz, "R-on-Rails", Ruby, Rails, JRuby, R, "GNU R", ggplot2, knitr, dplyr, Bioconductor, Arrow]
6
+ date: "2026"
7
+ bibliography: "../../examples/Bibliography/stats.bib"
8
8
  output:
9
9
  html_document:
10
10
  self_contained: true
11
11
  keep_md: true
12
- md_document:
13
- variant: markdown_github
12
+ toc: true
13
+ toc_depth: 3
14
+ number_sections: true
14
15
  pdf_document:
15
16
  includes:
16
17
  in_header: "../../sty/galaaz.sty"
@@ -18,6 +19,8 @@ output:
18
19
  number_sections: yes
19
20
  toc: true
20
21
  toc_depth: 3
22
+ md_document:
23
+ variant: markdown_github
21
24
  fontsize: 11pt
22
25
  ---
23
26
 
@@ -25,83 +28,64 @@ fontsize: 11pt
25
28
 
26
29
  # Introduction
27
30
 
28
- Galaaz is a system for tightly coupling Ruby and R. Ruby is a powerful language, with a large
29
- community, a very large set of libraries and great for web development. However, it lacks
30
- libraries for data science, statistics, scientific plotting and machine learning. On the
31
- other hand, R is considered one of the most powerful languages for solving all of the above
32
- problems. Maybe the strongest competitor to R is Python with libraries such as NumPy,
33
- Panda, SciPy, SciKit-Learn and a couple more.
34
-
35
- With Galaaz we do not intend to re-implement any of the scientific libraries in R, we allow
36
- for very tight coupling between the two languages to the point that the Ruby developer does
37
- not need to know that there is an R engine running.
38
-
39
- According to Wikipedia "Ruby is a dynamic, interpreted, reflective, object-oriented,
40
- general-purpose programming language. It was designed and developed in the mid-1990s by Yukihiro
41
- "Matz" Matsumoto in Japan." It reached high popularity with the development of Ruby on Rails
42
- (RoR) by David Heinemeier Hansson. RoR is a web application framework first released
43
- around 2005. It makes extensive use of Ruby's metaprogramming features. With RoR,
44
- Ruby became very popular. According to [Ruby's Tiobe index](https://www.tiobe.com/tiobe-index/ruby/)
45
- it peeked in popularity around 2008, then declined until 2015 when it started picking up again.
46
- At the time of this writing (November 2018), the Tiobe index puts Ruby in 16th position as
47
- most popular language.
48
-
49
- Python, a language similar to Ruby, ranks 4th in the index. Java, C and C++ take the
50
- first three positions. Ruby is often criticized for its focus on web applications.
51
- But Ruby can do [much more](https://github.com/markets/awesome-ruby) than just web applications.
52
- Yet, for scientific computing, Ruby lags way behind Python and R. Python has
53
- Django framework for web, NumPy for numerical arrays, Pandas for data analysis.
54
- R is a free software environment for statistical computing and graphics with thousands
55
- of libraries for data analysis.
56
-
57
- Until recently, there was no real perspective for Ruby to bridge this gap.
58
- Implementing a complete scientific computing infrastructure would take too long.
59
- Enters [Oracle's GraalVM](https://www.graalvm.org/):
60
-
61
- > GraalVM is a universal virtual machine for running applications written in
62
- > JavaScript, Python 3, Ruby, R, JVM-based languages like Java, Scala, Kotlin,
63
- > and LLVM-based languages such as C and C++.
64
- >
65
- > GraalVM removes the isolation between programming languages and enables
66
- > interoperability in a shared runtime. It can run either standalone or in the
67
- > context of OpenJDK, Node.js, Oracle Database, or MySQL.
68
- >
69
- > GraalVM allows you to write polyglot applications with a seamless way to pass
70
- > values from one language to another. With GraalVM there is no copying or
71
- > marshaling necessary as it is with other polyglot systems. This lets you
72
- > achieve high performance when language boundaries are crossed. Most of the time
73
- > there is no additional cost for crossing a language boundary at all.
74
- >
75
- > Often developers have to make uncomfortable compromises that require them
76
- > to rewrite their software in other languages. For example:
77
- >
78
- > * That library is not available in my language. I need to rewrite it.
79
- > * That language would be the perfect fit for my problem, but we cannot
80
- > run it in our environment.
81
- > * That problem is already solved in my language, but the language is
82
- > too slow.
83
- >
84
- > With GraalVM we aim to allow developers to freely choose the right language for
85
- > the task at hand without making compromises.
86
-
87
- As stated above, GraalVM is a _universal_ virtual machine that allows Ruby and R (and other
88
- languages) to run on the same environment. GraalVM allows polyglot applications to
89
- _seamlessly_ interact with one another and pass values from one language to the other.
90
- Although a great idea, GraalVM still requires application writers to know several languages.
91
- To eliminate that requirement, we built Galaaz, a gem for Ruby, to tightly couple
92
- Ruby and R and allow those languages to interact in a way that the user will be unaware
93
- of such interaction. In other words, a Ruby programmer will be able to use all
94
- the capabilities of R without knowing the R syntax.
95
-
96
- Library wrapping is a usual way of bringing features from one language into another.
97
- To improve performance, Python often wraps more efficient C libraries. For the
98
- Python developer, the existence of such C libraries is hidden. The problem with
99
- library wrapping is that for any new library, there is the need to handcraft a new
100
- wrapper.
101
-
102
- Galaaz, instead of wrapping a single C or R library, wraps the whole R language
103
- in Ruby. Doing so, all thousands of R libraries are available immediately
104
- to Ruby developers without any new wrapping effort.
31
+ **Galaaz is R-on-Rails:** keep **GNU R** for statistics, graphics, and the CRAN /
32
+ Bioconductor ecosystem, and use **Ruby** (with **Rails** when you need a web app) for
33
+ everything R was never meant to own—HTTP, auth, databases, background jobs, HTML, APIs.
34
+
35
+ The primary audience is the **R scientist** who already has models, dplyr/ggplot pipelines,
36
+ and domain knowledge, and wants a **one-person path to production on the web**. Learning
37
+ enough Ruby and Rails to ship an app is usually easier than hiring (or becoming) a dedicated
38
+ web-and-stats dual specialist. Ruby developers gain full access to R libraries as a
39
+ secondary benefit; the design priority is **R workflow first, web second**.
40
+
41
+ Galaaz does **not** re-implement scientific libraries in Ruby. Ruby and R run in **separate
42
+ processes**; the **Galaaz bridge** sends requests to standard **GNU R** and returns typed
43
+ results. You keep calling familiar ideas from Ruby (`R.c(...)`, `R.library('ggplot2')`,
44
+ `~R[:mtcars]`, dplyr-style chains) while Rails (or plain Ruby) owns the application shell.
45
+
46
+ **Python** remains a strong competitor for end-to-end data science stacks (NumPy, pandas,
47
+ SciPy, scikit-learn, Django/Flask). Galaaz’s bet is different: if your science already lives
48
+ in **R**, do not rewrite it in Python to get a website—**put R on Rails**.
49
+
50
+ Ruby is a dynamic, object-oriented language that became widely known through **Ruby on Rails**
51
+ (RoR). It remains a practical choice for web applications and general-purpose scripting.
52
+ Until Galaaz, Ruby lacked a tight, ecosystem-complete bridge to GNU R. Library wrapping
53
+ (one gem per package) does not scale; Galaaz wraps **the R language**, so thousands of R
54
+ packages are available without a new handcrafted wrapper for each one.
55
+
56
+ **Galaaz 2.0** supports **JRuby** and **CRuby** equally for the same NewBridge protocol.
57
+ Pick the Ruby that fits your app: JRuby when you want real multithreading for web and I/O;
58
+ CRuby when you prefer MRI. R remains the same **GNU R** you use interactively—including
59
+ compiled extensions and Bioconductor. Earlier GraalVM / TruffleRuby / FastR experiments
60
+ are no longer the focus.
61
+
62
+ The bridge handles **communication and typing** between the two worlds; large tables can
63
+ also flow through **Apache Arrow** on the R side when you use the optional helpers described
64
+ later in this manual.
65
+
66
+ ## R-on-Rails: the one-person app for R scientists
67
+
68
+ If you already think in R, the usual web options are painful: Shiny for some apps, or a full
69
+ rewrite in another stack. **R-on-Rails** means:
70
+
71
+ 1. **Keep your science in R** — packages, formulas, plots, Bioconductor, the same engine as RStudio.
72
+ 2. **Learn enough Ruby/Rails** routes, controllers, views, jobs, auth—not a second statistics career.
73
+ 3. **Call R from the app** Galaaz loads R behind the scenes; long jobs can complete asynchronously
74
+ while Rails stays responsive (see later sections on the bridge and `R::Async`).
75
+ 4. **Ship alone when you need to** one developer can own both the analysis and the product UI,
76
+ without waiting for a separate “stats engineer” and “Rails engineer.”
77
+
78
+ Typical shape:
79
+
80
+ - Interactive exploration and reports: **gstudio**, **gknit** (R Markdown with Ruby chunks).
81
+ - Product: a **Rails** (or Sinatra) app that calls `R.*` for the heavy statistical steps.
82
+ - Scale for many users: more **R worker processes/containers** behind the app (R is single-threaded
83
+ per process); the Ruby web tier scales separately (JRuby threads or a multi-process CRuby
84
+ setup). Galaaz’s instance manager is the starting point for that pattern—not a rewrite of
85
+ every algorithm à la enterprise ScaleR.
86
+
87
+ Rubyists are welcome: the same bridge exposes CRAN to application code. The **intended** on-ramp,
88
+ though, is **R scientist small Rails app**, not “hire a stats team to teach Rails developers R.”
105
89
 
106
90
  ## What does Galaaz mean
107
91
 
@@ -118,27 +102,285 @@ Galaaz is the Portuguese name for "Galahad". From Wikipedia:
118
102
  His name should not be mistaken with Galehaut, a different knight from
119
103
  Arthurian legend.
120
104
 
105
+ # Command-line tools (`bin/`)
106
+
107
+ The Galaaz repository ships many helpers under **`bin/`**. When working from a **clone**, call
108
+ them as **`bin/<name>`** from the project root (or `./bin/<name>`). If you install the **gem**,
109
+ only a subset is guaranteed on your `PATH` (see the gemspec: **`galaaz`**, **`gstudio`**, **`gknit`**, **`grun`**, **`gknit-draft`**); for development and CI, prefer the **`bin/`** copies so JVM flags and paths stay correct.
110
+
111
+ Below, **current (Galaaz 2.0 + JRuby or CRuby + GNU R)** means the tool uses **`bin/galaaz-ruby`**
112
+ / **`GALAAZ_RUBY`** (default: `ruby` on `PATH`) and applies JVM flags only when the interpreter is
113
+ JRuby (`bin/galaaz_jruby_env.inc.sh` / `lib/galaaz_jruby.rb`). **`bin/galaaz-jruby`** forces JRuby.
114
+ **Legacy** means the script still targets **GraalVM** polyglot Ruby / FastR-era invocation and is
115
+ **not** expected to work on a typical JRuby or CRuby NewBridge setup.
116
+
117
+ **Table layout:** names in the first column are **`bin/`** filenames (run as `bin/<name>` from the repo root). Long options and examples sit **outside** the tables so PDF columns stay readable.
118
+
119
+
120
+
121
+ <table class="table table-striped table-condensed" style="margin-left: auto; margin-right: auto;">
122
+ <thead>
123
+ <tr>
124
+ <th style="text-align:left;"> Script </th>
125
+ <th style="text-align:left;"> Role </th>
126
+ <th style="text-align:left;"> 2.0? </th>
127
+ </tr>
128
+ </thead>
129
+ <tbody>
130
+ <tr>
131
+ <td style="text-align:left;"> galaaz-bootstrap </td>
132
+ <td style="text-align:left;"> WSL2 helper: Docker checks; optional TinyTeX or poppler for gKnit PDF. </td>
133
+ <td style="text-align:left;"> Yes* </td>
134
+ </tr>
135
+ <tr>
136
+ <td style="text-align:left;"> galaaz-ruby </td>
137
+ <td style="text-align:left;"> Selected Ruby (default: ruby on PATH) with repo lib/ on LOAD_PATH; JVM flags only on JRuby. </td>
138
+ <td style="text-align:left;"> Yes </td>
139
+ </tr>
140
+ <tr>
141
+ <td style="text-align:left;"> galaaz-jruby </td>
142
+ <td style="text-align:left;"> Thin wrapper that forces JRuby (same flags as galaaz-ruby under JRuby). </td>
143
+ <td style="text-align:left;"> Yes </td>
144
+ </tr>
145
+ <tr>
146
+ <td style="text-align:left;"> galaaz_ruby_env.inc.sh </td>
147
+ <td style="text-align:left;"> Sourced by bash wrappers; sets GALAAZ_RUBY_BIN and optional JVM args. </td>
148
+ <td style="text-align:left;"> Yes† </td>
149
+ </tr>
150
+ <tr>
151
+ <td style="text-align:left;"> galaaz_jruby_env.inc.sh </td>
152
+ <td style="text-align:left;"> Sourced when the interpreter is JRuby; sets GALAAZ_REQUIRED_JRUBY_J_ARGS. </td>
153
+ <td style="text-align:left;"> Yes† </td>
154
+ </tr>
155
+ <tr>
156
+ <td style="text-align:left;"> install-tinytex </td>
157
+ <td style="text-align:left;"> Install TinyTeX for PDF output. </td>
158
+ <td style="text-align:left;"> Yes </td>
159
+ </tr>
160
+ </tbody>
161
+ </table>
162
+
163
+ \* Where WSL/Docker apply. **`galaaz-bootstrap` flags:** `--check`, `--apply`, `--runtime` (`docker` \| `local` \| `auto`), `--[no-]prompt-doc-tools`.
164
+
165
+ † Not run directly.
166
+
167
+ **`galaaz-ruby` / `galaaz-jruby` examples** (from repo root):
168
+
169
+ ```text
170
+ bin/galaaz-ruby my_script.rb
171
+ GALAAZ_RUBY=jruby bin/galaaz-ruby my_script.rb
172
+ bin/galaaz-jruby my_script.rb
173
+ bin/galaaz-ruby -S rspec
174
+ ```
175
+
176
+ ## Interactive use, examples, and Rake
177
+
178
+ <table class="table table-striped table-condensed" style="margin-left: auto; margin-right: auto;">
179
+ <thead>
180
+ <tr>
181
+ <th style="text-align:left;"> Script </th>
182
+ <th style="text-align:left;"> Role </th>
183
+ <th style="text-align:left;"> 2.0? </th>
184
+ </tr>
185
+ </thead>
186
+ <tbody>
187
+ <tr>
188
+ <td style="text-align:left;"> gstudio </td>
189
+ <td style="text-align:left;"> IRB or Pry with Galaaz preloaded (ruby on PATH; JVM flags on JRuby). </td>
190
+ <td style="text-align:left;"> Yes </td>
191
+ </tr>
192
+ <tr>
193
+ <td style="text-align:left;"> run_example </td>
194
+ <td style="text-align:left;"> Run one Ruby file using the same Ruby/JVM setup as tests. </td>
195
+ <td style="text-align:left;"> Yes </td>
196
+ </tr>
197
+ <tr>
198
+ <td style="text-align:left;"> galaaz </td>
199
+ <td style="text-align:left;"> Forward arguments to rake (needs rake; same GALAAZ_RUBY as other launchers). </td>
200
+ <td style="text-align:left;"> Yes </td>
201
+ </tr>
202
+ </tbody>
203
+ </table>
204
+
205
+ ## gKnit and document drafts
206
+
207
+ <table class="table table-striped table-condensed" style="margin-left: auto; margin-right: auto;">
208
+ <thead>
209
+ <tr>
210
+ <th style="text-align:left;"> Script </th>
211
+ <th style="text-align:left;"> Role </th>
212
+ <th style="text-align:left;"> 2.0? </th>
213
+ </tr>
214
+ </thead>
215
+ <tbody>
216
+ <tr>
217
+ <td style="text-align:left;"> gknit </td>
218
+ <td style="text-align:left;"> Knit .Rmd via ruby on PATH (JRuby or CRuby) and R Markdown render. </td>
219
+ <td style="text-align:left;"> Yes </td>
220
+ </tr>
221
+ <tr>
222
+ <td style="text-align:left;"> gknit-draft </td>
223
+ <td style="text-align:left;"> Drafts from rticles-style templates; wrapper still uses legacy polyglot ruby. </td>
224
+ <td style="text-align:left;"> Legacy </td>
225
+ </tr>
226
+ <tr>
227
+ <td style="text-align:left;"> gknit-draft.rb </td>
228
+ <td style="text-align:left;"> Ruby entry: GKnit.draft (use with galaaz-ruby + LOAD_PATH). </td>
229
+ <td style="text-align:left;"> Yes </td>
230
+ </tr>
231
+ <tr>
232
+ <td style="text-align:left;"> gknit_Rscript </td>
233
+ <td style="text-align:left;"> Polyglot Rscript launcher; hard-coded LOAD_PATH sample. </td>
234
+ <td style="text-align:left;"> No </td>
235
+ </tr>
236
+ </tbody>
237
+ </table>
238
+
239
+ **`gknit` CLI** (see `gknit -h`): `--output_format`, `--output_file`, `--output_dir`, `--bridge_timeout_sec`, `--callback_timeout_ms`. If `--output_format` is omitted, the **first** YAML `output:` target wins.
240
+
241
+ Prefer **`galaaz-jruby`** for **`gknit-draft`** workflows until that wrapper matches the **`gknit`** stack.
242
+
243
+ ## Tests
244
+
245
+ <table class="table table-striped table-condensed" style="margin-left: auto; margin-right: auto;">
246
+ <thead>
247
+ <tr>
248
+ <th style="text-align:left;"> Script </th>
249
+ <th style="text-align:left;"> Role </th>
250
+ <th style="text-align:left;"> 2.0? </th>
251
+ </tr>
252
+ </thead>
253
+ <tbody>
254
+ <tr>
255
+ <td style="text-align:left;"> run_rspec </td>
256
+ <td style="text-align:left;"> Top-level specs/*_spec.rb with spec_helper (see docs/testing.md). </td>
257
+ <td style="text-align:left;"> Yes </td>
258
+ </tr>
259
+ <tr>
260
+ <td style="text-align:left;"> run_all_rspec </td>
261
+ <td style="text-align:left;"> Compile ext/new_bridge; run specs/ and new_bridge_specs/ together. </td>
262
+ <td style="text-align:left;"> Yes </td>
263
+ </tr>
264
+ <tr>
265
+ <td style="text-align:left;"> run_slow_rspec </td>
266
+ <td style="text-align:left;"> Suites under slow-specs/ (read script header for spec_helper). </td>
267
+ <td style="text-align:left;"> Yes </td>
268
+ </tr>
269
+ <tr>
270
+ <td style="text-align:left;"> run_old_rspec </td>
271
+ <td style="text-align:left;"> Legacy suites under old_specs/. </td>
272
+ <td style="text-align:left;"> Yes </td>
273
+ </tr>
274
+ <tr>
275
+ <td style="text-align:left;"> run_rspec_subset </td>
276
+ <td style="text-align:left;"> Numbered subset 1–18 (Documentation/Spec_Subsets.md). </td>
277
+ <td style="text-align:left;"> Yes </td>
278
+ </tr>
279
+ </tbody>
280
+ </table>
281
+
282
+ ## Other
283
+
284
+ <table class="table table-striped table-condensed" style="margin-left: auto; margin-right: auto;">
285
+ <thead>
286
+ <tr>
287
+ <th style="text-align:left;"> Script </th>
288
+ <th style="text-align:left;"> Role </th>
289
+ <th style="text-align:left;"> 2.0? </th>
290
+ </tr>
291
+ </thead>
292
+ <tbody>
293
+ <tr>
294
+ <td style="text-align:left;"> grun </td>
295
+ <td style="text-align:left;"> Graal-era launcher: polyglot ruby with --jvm. Use galaaz-jruby -S instead. </td>
296
+ <td style="text-align:left;"> No </td>
297
+ </tr>
298
+ <tr>
299
+ <td style="text-align:left;"> gstudio_irb.rb / gstudio_pry.rb </td>
300
+ <td style="text-align:left;"> Loaded by gstudio; not meant to be run standalone. </td>
301
+ <td style="text-align:left;"> Yes </td>
302
+ </tr>
303
+ </tbody>
304
+ </table>
305
+
306
+ For day-to-day **2.0** use, rely on **`bin/galaaz-ruby`** (or **`bin/galaaz-jruby`** when you want to force JRuby), **`bin/gstudio`**, **`bin/gknit`**, **`bin/run_example`**, **`bin/run_rspec`** / **`bin/run_all_rspec`**, and **`bin/galaaz-bootstrap`** on WSL when using Dockerized R. Treat **`grun`**, **`gknit_Rscript`**, and the polyglot **`ruby`** invocation in **`gknit-draft`** as **legacy** until they are ported to the same launcher path as **`gknit`**.
307
+
121
308
  # System Compatibility
122
309
 
123
- * Oracle Linux 7
124
- * Ubuntu 18.04 LTS
125
- * Ubuntu 16.04 LTS
126
- * Fedora 28
127
- * macOS 10.14 (Mojave)
128
- * macOS 10.13 (High Sierra)
310
+ Typical development and CI targets:
129
311
 
130
- # Dependencies
312
+ * **Linux** — recent Ubuntu LTS or comparable distributions (x86_64).
313
+ * **macOS** — recent releases with JRuby or CRuby and GNU R available.
314
+ * **Windows** — use **WSL2** (same Linux stack as above); native Windows is not the primary target.
315
+
316
+ The native **gatekeeper** component under `ext/new_bridge` is built with `make` and a C++ toolchain; see the project `README` if compilation fails on your platform.
131
317
 
132
- * TruffleRuby
133
- * FastR
318
+ # Dependencies
134
319
 
320
+ * **Ruby** — **JRuby** or **CRuby** (both supported for NewBridge). Tested with **JRuby 10.1.1.0**
321
+ (+ **JDK 21**) and **CRuby 3.3.12**. Use `bin/galaaz-ruby` (honors `GALAAZ_RUBY`) or plain
322
+ `gem install galaaz` under the Ruby you choose.
323
+ * **GNU R** — `R` and `Rscript` on your `PATH` (tested with **4.3.3**), plus a C++ toolchain (`g++`, `make`) and the **Rcpp** package to compile the gatekeeper.
324
+ * **galaaz gem** — runtime dependency `msgpack` is pulled in by `gem install`.
325
+ * Optional: **Docker** — if you run R in a container (common on WSL2); see bootstrap below.
326
+ * Optional R packages for examples in this manual — e.g. `ggplot2`, `dplyr`, `knitr`, `kableExtra`, `arrow`, Bioconductor tools such as **DESeq2** (installed the usual R way).
135
327
 
136
328
  # Installation
137
329
 
138
- * Install GrallVM (http://www.graalvm.org/)
139
- * Install Ruby (gu install Ruby)
140
- * Install FastR (gu install R)
141
- * Install rake if you want to run the specs and examples (gem install rake)
330
+ The supported install is **`gem install` + compile the gatekeeper**. You do not need a git clone.
331
+
332
+ 1. Install **JRuby** (and a compatible **JDK**) **or CRuby 3.3+**, plus **GNU R** (with `Rscript` and a C++ compiler).
333
+ 2. In R, install **Rcpp**: `install.packages("Rcpp")`.
334
+ 3. Install the gem: `jruby -S gem install galaaz` (or `gem install galaaz` under CRuby).
335
+ 4. Compile the native gatekeeper from the installed gem:
336
+
337
+ ```
338
+ gem_dir="$(ruby -e "puts Gem::Specification.find_by_name('galaaz').full_gem_path")"
339
+ # under JRuby: gem_dir="$(jruby -e "puts Gem::Specification.find_by_name('galaaz').full_gem_path")"
340
+ make -C "${gem_dir}/ext/new_bridge" all
341
+ ```
342
+
343
+ 5. Ensure **`R`** starts GNU R and can install packages (network access to CRAN when you first call `R.install_and_loads`). For **Apache Arrow** on Java 9+, pass `-J--add-opens=java.base/java.nio=ALL-UNNAMED` to JRuby (from a checkout, `bin/galaaz-jruby` does this; on CRuby this flag is not needed).
344
+
345
+ For **gKnit**, **knitr**, **rmarkdown**, and LaTeX (PDF output), install the corresponding R packages, **Pandoc**, and a TeX distribution if you need PDF; the repository includes helpers such as **`bin/install-tinytex`** where appropriate.
346
+
347
+ A **table of all `bin/` scripts** (bootstrap, Ruby launcher, gstudio, gknit, test runners, and which ones are legacy) is in the section **Command-line tools (`bin/`)** earlier in this manual.
348
+
349
+ ### From a repository checkout (contributors)
350
+
351
+ 1. Install **bundler** if needed, then run **`bundle install`** with your chosen Ruby
352
+ (`jruby -S bundle install` or CRuby `bundle install`) in the repository root.
353
+ 2. Build the bridge native code: **`make -C ext/new_bridge all`** (or **`rake compile_gatekeeper`**).
354
+ 3. Run scripts with **`bin/galaaz-ruby`** (uses `ruby` on `PATH`; set **`GALAAZ_RUBY=jruby`** or
355
+ **`GALAAZ_RUBY=ruby`** to force an engine). Spec runners: **`bin/run_rspec`** /
356
+ **`bin/run_all_rspec`** (same `GALAAZ_RUBY` rule). **`bin/galaaz-jruby`** remains a thin
357
+ wrapper that forces JRuby.
358
+
359
+ A **gstudio** try image (JRuby + R + Galaaz already installed) is **`docker run --rm -it ghcr.io/rbotafogo/galaaz-try:gstudio`** (or **`./docker/try-gstudio/run.sh`** from a checkout). Maintainers can prove a RubyGems install on a throwaway Ubuntu machine (no repo inside the container) with **`./docker/cold-install/run.sh published-specs`** (JRuby) or **`./docker/cold-install-cruby/run.sh published-specs`** (CRuby).
360
+
361
+ ## Windows + WSL2 (optional: Docker / R in a container)
362
+
363
+ If you run Galaaz on Windows through WSL2 and want containerized R instances,
364
+ Docker Desktop is the supported setup.
365
+
366
+ 1. Install Docker Desktop on Windows:
367
+ - https://www.docker.com/products/docker-desktop/
368
+ 2. Open Docker Desktop and enable WSL integration:
369
+ - Settings > Resources > WSL Integration
370
+ - Enable integration for your target distro
371
+ - Apply & Restart Docker Desktop
372
+ 3. In WSL, run Galaaz bootstrap:
373
+
374
+ > ruby bin/galaaz-bootstrap --apply
375
+ > ruby bin/galaaz-bootstrap --check
376
+
377
+ Expected result:
378
+ - docker CLI available
379
+ - docker compose available
380
+ - docker daemon reachable (`docker info` works)
381
+
382
+ If bootstrap reports daemon is unreachable, check Docker Desktop is running and
383
+ WSL integration is enabled for the distro where Galaaz is installed.
142
384
 
143
385
  # Usage
144
386
 
@@ -147,7 +389,7 @@ Galaaz is the Portuguese name for "Galahad". From Wikipedia:
147
389
  > gstudio
148
390
 
149
391
 
150
- ```ruby
392
+ ``` ruby
151
393
  vec = R.c(1, 2, 3, 4)
152
394
  puts vec
153
395
  ```
@@ -172,7 +414,7 @@ Galaaz is the Portuguese name for "Galahad". From Wikipedia:
172
414
 
173
415
  > galaaz -T
174
416
 
175
- Shows a list with all available executalbe tasks. To execute a task, substitute the
417
+ Shows a list with all available executable tasks. To execute a task, substitute the
176
418
  'rake' word in the list with 'galaaz'. For instance, the following line shows up
177
419
  after 'galaaz -T'
178
420
 
@@ -182,17 +424,214 @@ Galaaz is the Portuguese name for "Galahad". From Wikipedia:
182
424
 
183
425
  > galaaz master_list:scatter_plot
184
426
 
427
+ # JRuby, CRuby, multithreading, and the R bridge
185
428
 
186
- # Accessing R from Ruby
429
+ Galaaz 2.0 supports **JRuby** and **CRuby** equally for NewBridge. On **JRuby**, your
430
+ application can use **real parallel threads** for I/O-bound work (HTTP clients, database
431
+ connections, message consumers, and so on). On **CRuby**, prefer multi-process scaling for
432
+ CPU-bound concurrency. R itself is still executed in a **single GNU R process** behind the
433
+ Galaaz bridge (use multiple R workers when you need more R throughput).
434
+
435
+ When several Ruby threads call into R at the same time, the bridge **serializes** those calls:
436
+ each request is matched to a reply using an internal per-call **queue**, so you do not need to
437
+ add your own mutex around every `R.foo` from application threads. (You should still use normal
438
+ Ruby synchronization when **Ruby** data structures are shared between threads—for example, when
439
+ appending rows from each thread into a shared array before sending them to R.)
440
+
441
+ A practical pattern is:
442
+
443
+ 1. Use threads (or a connection pool) to read from **multiple databases or shards** in parallel.
444
+ 2. Merge the rows in Ruby under a `Mutex` if you collect into one structure.
445
+ 3. Hand the merged table to R **once** (for example with `R::Arrow.from_ruby_batches` and dplyr,
446
+ or by building a data frame) so heavy statistics run in R with fewer bridge round-trips.
447
+
448
+ A runnable sketch lives in
449
+ `examples/multithread_shards_to_r/shards_to_r.rb` (simulated shard queries; swap in your DB
450
+ driver). For concurrency tests on the bridge itself, see `specs/bridge_concurrent_spec.rb` and
451
+ `specs/arrow_from_ruby_batches_spec.rb`.
452
+
453
+ ## Long-running R calls and a completion block
454
+
455
+ For R work that can take a long time, the bridge can avoid a Ruby-side **wait timeout** by
456
+ scheduling the call and resuming in a **block** when the `RET` arrives.
457
+
458
+ - **`R.eval_r_async(code, timeout: nil) { |result| ... }`** — string eval; on success, `result.value`
459
+ is the same formatted string as **`R.eval_r`** (use `timeout: nil` for no Ruby-side limit).
460
+ - **`R::Async.<rname>(...) { |result| ... }`** — same dispatch as **`R.<rname>(...)`**, but async;
461
+ on success, `result.value` is an **`R::Object`** (or unboxed Ruby value / Symbol), like synchronous
462
+ **`R.<rname>`**. Optional keyword **`timeout:`** applies a Ruby-side wait limit (completion receives
463
+ **`NewBridge::SessionClient::TimeoutError`** if R is too slow).
464
+
465
+ **Important:** **`R.foo(...) { |x| }`** is already used for dplyr-style scopes (`R::Support.new_scope`),
466
+ so async R calls must use **`R::Async`** or **`R.eval_r_async`**, not a bare **`R.foo` with a block.**
467
+
468
+ `NewBridge::EvalResult` exposes **`#ok?`**, **`#value`**, and **`#error`**. The completion block runs on a
469
+ **background thread** (not the bridge reader thread).
470
+
471
+ The example below is **plain Ruby** (no Rails). The R snippet sleeps (standing in for heavy work) and then
472
+ returns an integer so the success branch shows a **non-nil** value. (`Sys.sleep` alone returns **NULL** in R;
473
+ on success **`result.value`** is then **`nil`** in Ruby—that is expected, not a bridge error.)
474
+
475
+
476
+ ``` ruby
477
+ require 'thread'
478
+
479
+ completion = Queue.new
480
+
481
+ R.eval_r_async('({ Sys.sleep(0.3); 42L })', timeout: nil) do |result|
482
+ if result.ok?
483
+ puts "[completion] R finished; eval_r-style value: #{result.value.inspect}"
484
+ else
485
+ puts "[completion] R/bridge error: #{result.error.class}: #{result.error.message}"
486
+ end
487
+ completion.push(:done)
488
+ end
489
+
490
+ 3.times do |i|
491
+ puts "[main] other Ruby work step #{i + 1}"
492
+ sleep 0.05
493
+ end
494
+
495
+ completion.pop
496
+ puts "[main] R completion has run; exiting."
497
+ ```
498
+
499
+ ```
500
+ ## [main] other Ruby work step 1
501
+ ## [main] other Ruby work step 2
502
+ ## [main] other Ruby work step 3
503
+ ## [completion] R finished; eval_r-style value: "[1] 42"
504
+ ## [main] R completion has run; exiting.
505
+ ```
506
+
507
+ In a **web application**, the HTTP response usually ends before R finishes, so you would not
508
+ `Queue#pop` in the controller; you would persist an identifier, let the completion block write
509
+ the outcome to storage, and notify the client (poll, WebSocket, Turbo Stream, etc.). The plain
510
+ Ruby pattern above is only to show **when** the result exists (inside the block, or after data
511
+ written there is observed elsewhere). Runnable specs live in **`new_bridge_specs/eval_r_async_spec.rb`**.
512
+
513
+ ## Galaaz + Rails (R-on-Rails) integration baseline
514
+
515
+ This is the practical **R-on-Rails** starter: an R scientist’s analysis behind a small Rails
516
+ app. The baseline we used in WSL aimed at:
517
+
518
+ 1. Rails boots under **JRuby or CRuby** (same bridge; see Installation).
519
+ 2. Galaaz is loaded from a local checkout (before publishing to RubyGems).
520
+ 3. A request path can execute **`R.eval(...)`** (or `R.*`) and return a result.
521
+
522
+ You keep statistical work in R; Rails owns HTTP, sessions, and HTML. See **R-on-Rails: the
523
+ one-person app for R scientists** in the Introduction for the product framing.
524
+
525
+ ### 1) Create the app with Ruby-friendly options
526
+
527
+ Rails defaults can pull gems that are awkward on some setups (for example sqlite native
528
+ extension paths on JRuby, or deployment extras you do not need). A minimal app avoids early friction:
529
+
530
+ ```bash
531
+ cd /home/rbotafogo/desenv_linux
532
+ jruby -S rails new hedi --skip-git --minimal --skip-kamal --skip-solid --skip-active-record
533
+ ```
534
+
535
+ Then install gems:
536
+
537
+ ```bash
538
+ cd /home/rbotafogo/desenv_linux/hedi
539
+ jruby -S bundle install
540
+ ```
541
+
542
+ ### 2) Use Galaaz as a local path gem
543
+
544
+ For local development we keep a stable path:
187
545
 
188
- One of the nice aspects of Galaaz on GraalVM, is that variables and functions defined in R, can
189
- be easily accessed from Ruby. For instance, to access the 'mtcars' data frame from R
190
- in Ruby, we use the ':mtcar' symbol preceded by the '~' operator, thus '~:r_vec' retrieves the
191
- value of the 'mtcars' variable.
546
+ - `~/gems/galaaz` -> symlink to your Galaaz checkout
547
+ - optional built gem archive in `~/gems/pkg/`
192
548
 
549
+ In Rails `Gemfile`:
193
550
 
194
551
  ```ruby
195
- puts ~:mtcars
552
+ gem "galaaz", path: "/home/rbotafogo/gems/galaaz", require: false
553
+ ```
554
+
555
+ And load after Rails boot in `config/application.rb`:
556
+
557
+ ```ruby
558
+ config.after_initialize { require "galaaz" }
559
+ ```
560
+
561
+ Why `require: false` + `after_initialize`? In this integration, loading Galaaz too early via
562
+ `Bundler.require` triggered Rails/JRuby initialization failures.
563
+
564
+ ### 3) Simple request-path smoke test
565
+
566
+ A direct smoke test from Rails runner:
567
+
568
+ ```bash
569
+ cd /home/rbotafogo/desenv_linux/hedi
570
+ jruby -S bundle exec rails runner "puts R.eval('sum(c(1,2,3,4,5))').inspect"
571
+ ```
572
+
573
+ Expected output:
574
+
575
+ ```text
576
+ 15.0
577
+ ```
578
+
579
+ ### 4) HTTP endpoint pattern
580
+
581
+ For this baseline, a small Rack endpoint was the most stable first step to prove request-time R
582
+ evaluation. (A full ActionController stack can be enabled later as the app evolves.)
583
+
584
+ Minimal pattern:
585
+
586
+ 1. Define a Rack app class under `lib/` that runs `R.eval(...)` and returns HTML/JSON.
587
+ 2. Point a route to that Rack app (`root to: MyRackApp`).
588
+ 3. Verify with browser/curl.
589
+
590
+ ### 5) Running from WSL and opening from Windows
591
+
592
+ Recommended bind:
593
+
594
+ ```bash
595
+ jruby -S bundle exec rails server -b 0.0.0.0 -p 3000
596
+ ```
597
+
598
+ Then open from Windows:
599
+
600
+ - `http://localhost:3000` (usually works with WSL localhost forwarding), or
601
+ - `http://<wsl-ip>:3000` if needed.
602
+
603
+ In development, if Host Authorization blocks requests with unexpected Host headers, use:
604
+
605
+ ```ruby
606
+ # config/environments/development.rb
607
+ config.hosts.clear
608
+ ```
609
+
610
+ ### 6) Troubleshooting checklist
611
+
612
+ - `Could not find ... in locally installed gems`:
613
+ run `jruby -S bundle install` in the Rails app directory.
614
+ - Stale PID after crash:
615
+ remove `tmp/pids/server.pid`.
616
+ - Local Galaaz path changed:
617
+ verify `Gemfile` path target exists and rerun bundler.
618
+ - R runtime issues:
619
+ confirm GNU R is installed and on `PATH` in the same shell where Rails runs.
620
+
621
+ As new Rails features are added (controllers, jobs, websockets, background rendering, plot
622
+ generation), extend this section with concrete, runnable snippets and the associated operational
623
+ checks.
624
+
625
+ # Accessing R from Ruby
626
+
627
+ One of the nice aspects of Galaaz is that variables and functions defined in R can
628
+ be easily accessed from Ruby. For instance, to access the `mtcars` data frame from R
629
+ in Ruby, we use the symbol `:mtcars` preceded by the `~` operator: `~R[:mtcars]` retrieves the
630
+ value of the `mtcars` object in R.
631
+
632
+
633
+ ``` ruby
634
+ puts ~R[:mtcars]
196
635
  ```
197
636
 
198
637
  ```
@@ -231,11 +670,36 @@ puts ~:mtcars
231
670
  ## Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
232
671
  ```
233
672
 
234
- To access an R function from Ruby, the R function needs to be preceeded by 'R.' scoping.
235
- Bellow we see and example of creating a R::Vector by calling the 'c' R function
673
+ ## Scoped symbols and lexical scoping
674
+
675
+ Galaaz 2.0 uses **scoped symbols** by default. The canonical style is `R[:name]`:
676
+
677
+ - `~R[:mtcars]` fetches an R object by name.
678
+ - `R[:a] + R[:b]` builds an expression.
679
+ - `R[:year].up_to(R[:day])` builds range expressions.
236
680
 
681
+ If you prefer the terse `:x` syntax, you can opt in with lexical scoping using a Ruby refinement:
237
682
 
238
683
  ```ruby
684
+ module MyScript
685
+ using Galaaz::SymbolDSL
686
+
687
+ def self.run
688
+ expr = :a + :b
689
+ puts expr
690
+ puts ~:mtcars
691
+ end
692
+ end
693
+ ```
694
+
695
+ `using Galaaz::SymbolDSL` is **lexically scoped**: only code in that module/file scope gets `:x` DSL behavior.
696
+ Outside that scope, plain Ruby `Symbol` behavior is unchanged.
697
+
698
+ To access an R function from Ruby, the R function needs to be preceded by `R.` scoping.
699
+ Below we see an example of creating a R::Vector by calling the 'c' R function
700
+
701
+
702
+ ``` ruby
239
703
  puts vec = R.c(1.0, 2.0, 3.0, 4.0)
240
704
  ```
241
705
 
@@ -245,7 +709,7 @@ puts vec = R.c(1.0, 2.0, 3.0, 4.0)
245
709
  Note that 'vec' is an object of type R::Vector:
246
710
 
247
711
 
248
- ```ruby
712
+ ``` ruby
249
713
  puts vec.class
250
714
  ```
251
715
 
@@ -257,12 +721,12 @@ R::Object. In R, there is also a function 'class'. In order to access that funct
257
721
  can call method 'rclass' in the R::Object:
258
722
 
259
723
 
260
- ```ruby
724
+ ``` ruby
261
725
  puts vec.rclass
262
726
  ```
263
727
 
264
728
  ```
265
- ## [1] "numeric"
729
+ ## numeric
266
730
  ```
267
731
  When working with R::Object(s), it is possible to use the '.' operator to pipe operations.
268
732
  When using '.', the object to which the '.' is applied becomes the first argument of the
@@ -271,7 +735,7 @@ two vectors or more vectors (in R, there are no scalar values, scalars are conve
271
735
  vectors of size 1. Within Galaaz, scalar parameter is converted to a size one vector):
272
736
 
273
737
 
274
- ```ruby
738
+ ``` ruby
275
739
  puts R.c(vec, 10, 20, 30)
276
740
  ```
277
741
 
@@ -281,18 +745,18 @@ puts R.c(vec, 10, 20, 30)
281
745
  The call above to the 'c' function can also be done using '.' notation:
282
746
 
283
747
 
284
- ```ruby
748
+ ``` ruby
285
749
  puts vec.c(10, 20, 30)
286
750
  ```
287
751
 
288
752
  ```
289
753
  ## [1] 1 2 3 4 10 20 30
290
754
  ```
291
- We will talk about vector indexing in a latter section. But notice here that indexing
755
+ We will talk about vector indexing in a later section. But notice here that indexing
292
756
  an R::Vector will return another R::Vector:
293
757
 
294
758
 
295
- ```ruby
759
+ ``` ruby
296
760
  puts vec[1]
297
761
  ```
298
762
 
@@ -304,7 +768,7 @@ in an R::Object, but the native Ruby object. For this, we can index the R object
304
768
  the '>>' operator:
305
769
 
306
770
 
307
- ```ruby
771
+ ``` ruby
308
772
  puts vec >> 0
309
773
  puts vec >> 2
310
774
  ```
@@ -319,7 +783,7 @@ in Galaaz with named parameters. For instance, here is an example of creating a
319
783
  with named elements:
320
784
 
321
785
 
322
- ```ruby
786
+ ``` ruby
323
787
  puts R.list(first_name: "Rodrigo", last_name: "Botafogo")
324
788
  ```
325
789
 
@@ -337,25 +801,22 @@ Method or Lambda in place of the expected R function. In this next example, we w
337
801
  add 2 to every element of our previously created vector:
338
802
 
339
803
 
340
- ```ruby
804
+ ``` ruby
341
805
  puts vec.map { |x| x + 2 }
342
806
  ```
343
807
 
344
808
  ```
345
- ## [1] 3
346
- ## [1] 4
347
- ## [1] 5
348
- ## [1] 6
809
+ ## [1] 3 4 5 6
349
810
  ```
350
811
 
351
812
  # gKnitting a Document
352
813
 
353
- This manual has been formatted usign gKnit. gKnit uses Knitr and R markdown to knit
354
- a document in Ruby or R and output it in any of the available formats for R markdown.
355
- gKnit runs atop of GraalVM, and Galaaz. In gKnit, Ruby variables are persisted between
814
+ This manual has been formatted using gKnit. gKnit uses knitr and R Markdown to knit
815
+ a document in Ruby or R and output it in any of the available formats for R Markdown.
816
+ gKnit runs with **JRuby or CRuby**, **GNU R**, and Galaaz. In gKnit, Ruby variables are persisted between
356
817
  chunks, making it an ideal solution for literate programming. Also, since it is based
357
- on Galaaz, Ruby chunks can have access to R variables and Polyglot Programming with
358
- Ruby and R is quite natural.
818
+ on Galaaz, Ruby chunks can have access to R variables and combining Ruby with R in one
819
+ document is natural.
359
820
 
360
821
  The idea of "literate programming" was first introduced by Donald Knuth in the
361
822
  1980's [@Knuth:literate_programming].
@@ -374,7 +835,7 @@ single document or set of documents that when distributed to peers could be reru
374
835
  the same output and reports.
375
836
 
376
837
  The R community has put a great deal of effort in reproducible research. In 2002, Sweave was
377
- introduced and it allowed mixing R code with Latex generating high quality PDF documents. A
838
+ introduced and it allowed mixing R code with LaTeX, generating high-quality PDF documents. A
378
839
  Sweave document could include code, the results of executing the code, graphics and text
379
840
  such that it contained the whole narrative to reproduce the research. In
380
841
  2012, Knitr, developed by Yihui Xie from RStudio was released to replace Sweave and to
@@ -383,7 +844,7 @@ were necessary for Sweave.
383
844
 
384
845
  With Knitr, __R markdown__ was also developed, an extension to the
385
846
  Markdown format. With __R markdown__ and Knitr it is possible to generate reports in a multitude
386
- of formats such as HTML, markdown, Latex, PDF, dvi, etc. __R markdown__ also allows the use of
847
+ of formats such as HTML, Markdown, LaTeX, PDF, DVI, etc. __R markdown__ also allows the use of
387
848
  multiple programming languages such as R, Ruby, Python, etc. in the same document.
388
849
 
389
850
  In __R markdown__, text is interspersed with
@@ -404,14 +865,14 @@ create a list, then we would need to save it to file, but to save it, we need so
404
865
  data into a binary format:
405
866
 
406
867
 
407
- ```ruby
868
+ ``` ruby
408
869
  lst = R.list(a: 1, b: 2, c: 3)
409
870
  lst.saveRDS("lst.rds")
410
871
  ```
411
872
  then, on the next chunk, where variable 'lst' is used, we need to read back it's value
412
873
 
413
874
 
414
- ```ruby
875
+ ``` ruby
415
876
  lst = R.readRDS("lst.rds")
416
877
  puts lst
417
878
  ```
@@ -431,16 +892,16 @@ Now, any single code has dozens of variables that we might want to use and reuse
431
892
  Clearly, such an approach becomes quickly unmanageable. Probably, because of
432
893
  this problem, it is very rare to see any __R markdown__ document in the Ruby community.
433
894
 
434
- When variables can be used accross chunks, then no overhead is needed:
895
+ When variables can be used across chunks, then no overhead is needed:
435
896
 
436
897
 
437
- ```ruby
898
+ ``` ruby
438
899
  lst = R.list(a: 1, b: 2, c: 3)
439
900
  # any other code can be added here
440
901
  ```
441
902
 
442
903
 
443
- ```ruby
904
+ ``` ruby
444
905
  puts lst
445
906
  ```
446
907
 
@@ -456,8 +917,8 @@ puts lst
456
917
  ```
457
918
 
458
919
  In the Python community, the same effort to have code and text in an integrated environment
459
- started around the first decade of 2000. In 2006 iPython 0.7.2 was released. In 2014,
460
- Fernando Pérez, spun off project Jupyter from iPython creating a web-based interactive
920
+ started around the first decade of the 2000s. In 2006 IPython 0.7.2 was released. In 2014,
921
+ Fernando Pérez spun off the Jupyter project from IPython, creating a web-based interactive
461
922
  computation environment. Jupyter can now be used with many languages, including Ruby with the
462
923
  iruby gem (https://github.com/SciRuby/iruby). In order to have multiple languages in a Jupyter
463
924
  notebook the SoS kernel was developed (https://vatlab.github.io/sos-docs/).
@@ -471,8 +932,8 @@ have in a single document, text and code.
471
932
 
472
933
  In gKnit, Ruby variables are persisted between
473
934
  chunks, making it an ideal solution for literate programming in this language. Also,
474
- since it is based on Galaaz, Ruby chunks can have access to R variables and Polyglot Programming
475
- with Ruby and R is quite natural.
935
+ since it is based on Galaaz, Ruby chunks can access R variables (`~R[:name]`, `R.*`) through the
936
+ **Galaaz bridge** while knitr drives **GNU R**—no GraalVM polyglot runtime is required.
476
937
 
477
938
  This is not a blog post on __R markdown__, and the interested user is directed to the following links
478
939
  for detailed information on its capabilities and use.
@@ -486,7 +947,7 @@ gKnitting Ruby and R documents quickly.
486
947
  ## The Yaml header
487
948
 
488
949
  An __R markdown__ document should start with a Yaml header and be stored in a file with
489
- '.Rmd' extension. This document has the following header for gKitting an HTML document.
950
+ '.Rmd' extension. This document has the following header for gKnitting an HTML document.
490
951
 
491
952
  ```
492
953
  ---
@@ -494,7 +955,7 @@ title: "How to do reproducible research in Ruby with gKnit"
494
955
  author:
495
956
  - "Rodrigo Botafogo"
496
957
  - "Daniel Mossé - University of Pittsburgh"
497
- tags: [Tech, Data Science, Ruby, R, GraalVM]
958
+ tags: [Tech, Data Science, Ruby, R, JRuby, Galaaz]
498
959
  date: "20/02/2019"
499
960
  output:
500
961
  html_document:
@@ -509,6 +970,35 @@ output:
509
970
 
510
971
  For more information on the options in the Yaml header, [check here](https://bookdown.org/yihui/rmarkdown/html-document.html).
511
972
 
973
+ ## Choosing the output format when calling gknit
974
+
975
+ Yes: you can select the render target on the **command line**. **`bin/gknit`** (or **`gknit`** on your `PATH`) forwards options to **`rmarkdown::render`** via **`R::Rmarkdown.render`**.
976
+
977
+ * **`--output_format FORMAT`** — name of the format, as in the YAML `output:` block. Examples:
978
+ * **`html_document`** — HTML (often the default you list first under `output:`).
979
+ * **`pdf_document`** — PDF (you need a working LaTeX setup, e.g. TinyTeX; see **`bin/install-tinytex`**).
980
+ * **`md_document`**, **`github_document`**, or any other format defined in your YAML.
981
+ * **`all`** — render **every** format declared under `output:` in the document (same idea as in R Markdown).
982
+
983
+ If you **omit** **`--output_format`**, gknit passes **`NULL`** for the format argument. In that case **rmarkdown** uses the **first** format listed under **`output:`** in the YAML (and if none is specified there, behavior follows the usual rmarkdown defaults, typically HTML).
984
+
985
+ Other useful flags:
986
+
987
+ * **`--output_file NAME`** — output file name (optional path; see also **`--output_dir`**).
988
+ * **`--output_dir DIR`** — directory for the rendered file (created if missing).
989
+ * **`--bridge_timeout_sec`** / **`--callback_timeout_ms`** — longer R or install steps (see elsewhere in this manual).
990
+
991
+ Examples (run from the directory where paths make sense, or use absolute paths):
992
+
993
+ ```text
994
+ bin/gknit blogs/manual/manual.Rmd
995
+ bin/gknit --output_format html_document blogs/manual/manual.Rmd
996
+ bin/gknit --output_format pdf_document blogs/manual/manual.Rmd
997
+ bin/gknit --output_format all blogs/manual/manual.Rmd
998
+ ```
999
+
1000
+ Use **`gknit -h`** for the full option list.
1001
+
512
1002
  ## __R Markdown__ formatting
513
1003
 
514
1004
  Document formatting can be done with simple markups such as:
@@ -553,7 +1043,7 @@ Running and executing Ruby and R code is actually what really interests us is th
553
1043
  Inserting a code chunk is done by adding code in a block delimited by three back ticks
554
1044
  followed by an open
555
1045
  curly brace ('{') followed with the engine name (r, ruby, rb, include, ...), an
556
- any optional chunk_label and options, as shown bellow:
1046
+ any optional chunk_label and options, as shown below:
557
1047
 
558
1048
  ````
559
1049
  ```{engine_name [chunk_label], [chunk_options]}
@@ -573,7 +1063,7 @@ print(vec)
573
1063
  If this block is added to an __R markdown__ document and gKnitted the result will be:
574
1064
 
575
1065
 
576
- ```r
1066
+ ``` r
577
1067
  vec <- c(1, 2, 3)
578
1068
  print(vec)
579
1069
  ```
@@ -621,7 +1111,7 @@ presentation we will not show chunk definitions any longer.
621
1111
 
622
1112
 
623
1113
 
624
- ```r
1114
+ ``` r
625
1115
  r_vec <- c(1, 2, 3, 4, 5)
626
1116
 
627
1117
  reduce_sum <- function(...) {
@@ -632,7 +1122,7 @@ reduce_sum <- function(...) {
632
1122
  We can, possibly in another chunk, access the vector and call the function as follows:
633
1123
 
634
1124
 
635
- ```r
1125
+ ``` r
636
1126
  print(r_vec)
637
1127
  ```
638
1128
 
@@ -640,7 +1130,7 @@ print(r_vec)
640
1130
  ## [1] 1 2 3 4 5
641
1131
  ```
642
1132
 
643
- ```r
1133
+ ``` r
644
1134
  print(reduce_sum(r_vec))
645
1135
  ```
646
1136
 
@@ -659,7 +1149,7 @@ grammar of graphics" [@Wilkinson:grammar_of_graphics]. The idea of the grammar o
659
1149
  is to build a graphics by adding layers to the plot. More information can be found in
660
1150
  https://towardsdatascience.com/a-comprehensive-guide-to-the-grammar-of-graphics-for-effective-visualization-of-multi-dimensional-1f92b4ed4149.
661
1151
 
662
- In the plot bellow the 'mpg' dataset from base R is used. "The data concerns city-cycle fuel
1152
+ In the plot below the 'mpg' dataset from base R is used. "The data concerns city-cycle fuel
663
1153
  consumption in miles per gallon, to be predicted in terms of 3 multivalued discrete and 5
664
1154
  continuous attributes." (Quinlan, 1993)
665
1155
 
@@ -673,21 +1163,9 @@ car point equal its high way consumption (size = hwy). Finally, a last layer is
673
1163
  a linear regression line (method = "lm") for every manufacturer.
674
1164
 
675
1165
 
676
- ```r
1166
+ ``` r
677
1167
  # load package and data
678
1168
  library(ggplot2)
679
- ```
680
-
681
- ```
682
- ## Message:
683
- ## Registered S3 methods overwritten by 'ggplot2':
684
- ## method from
685
- ## [.quosures rlang
686
- ## c.quosures rlang
687
- ## print.quosures rlang
688
- ```
689
-
690
- ```r
691
1169
  data(mpg, package="ggplot2")
692
1170
 
693
1171
  mpg_select <- mpg[mpg$manufacturer %in% c("audi", "ford", "honda", "hyundai"), ]
@@ -702,6 +1180,10 @@ g + geom_jitter(aes(col=manufacturer, size=hwy)) +
702
1180
  geom_smooth(aes(col=manufacturer), method="lm", se=F)
703
1181
  ```
704
1182
 
1183
+ ```
1184
+ ## `geom_smooth()` using formula = 'y ~ x'
1185
+ ```
1186
+
705
1187
  ![](manual_files/figure-html/bubble-1.png)<!-- -->
706
1188
 
707
1189
  ## Ruby chunks
@@ -733,7 +1215,7 @@ functions. gKnit will knit standard Ruby code, or even general text without
733
1215
  any code.
734
1216
 
735
1217
 
736
- ```ruby
1218
+ ``` ruby
737
1219
  a = [1, 2, 3]
738
1220
  b = "US$ 250.000"
739
1221
  c = "The 'outputs' function"
@@ -745,7 +1227,7 @@ vec2 = R.c(10, 20, 30)
745
1227
  In the next block, variables 'a', 'vec' and 'vec2' are used and printed.
746
1228
 
747
1229
 
748
- ```ruby
1230
+ ``` ruby
749
1231
  puts a
750
1232
  puts vec * vec2
751
1233
  ```
@@ -869,24 +1351,24 @@ Here, for instance, is a table definition in HTML and its output in the document
869
1351
  </div>
870
1352
 
871
1353
  But manually creating HTML output is not always easy or desirable, specially
872
- if we intend the document to be rendered in other formats, for example, as Latex.
1354
+ if we intend the document to be rendered in other formats, for example, as LaTeX.
873
1355
  Also, The above
874
1356
  table looks ugly. The 'kableExtra' library is a great library for
875
1357
  creating beautiful tables. Take a look at https://cran.r-project.org/web/packages/kableExtra/vignettes/awesome_table_in_html.html
876
1358
 
877
1359
  In the next chunk, we output the 'mtcars' dataframe from R in a nicely formatted
878
- table. Note that we retrieve the mtcars dataframe by using '~:mtcars'.
1360
+ table. Note that we retrieve the mtcars dataframe by using '~R[:mtcars]'.
879
1361
 
880
1362
 
881
- ```ruby
1363
+ ``` ruby
882
1364
  R.install_and_loads('kableExtra')
883
- outputs (~:mtcars).kable.kable_styling
1365
+ outputs (~R[:mtcars]).kable.kable_styling
884
1366
  ```
885
1367
 
886
1368
  <table class="table" style="margin-left: auto; margin-right: auto;">
887
1369
  <thead>
888
1370
  <tr>
889
- <th style="text-align:left;"> </th>
1371
+ <th style="text-align:left;"> </th>
890
1372
  <th style="text-align:right;"> mpg </th>
891
1373
  <th style="text-align:right;"> cyl </th>
892
1374
  <th style="text-align:right;"> disp </th>
@@ -1377,7 +1859,7 @@ true, ruby's 'require\_relative' semantics is used to load the file, when false,
1377
1859
  ```
1378
1860
  ````
1379
1861
 
1380
- Bellow we include file 'model.rb', which is in the same directory of this blog.
1862
+ Below we include file 'model.rb', which is in the same directory of this blog.
1381
1863
  This code uses R 'caret' package to split a dataset in a train and test sets.
1382
1864
  The 'caret' package is a very important a useful package for doing Data Analysis,
1383
1865
  it has hundreds of functions for all steps of the Data Analysis workflow. To
@@ -1395,7 +1877,7 @@ will install the package if it is not already installed and can take a while.
1395
1877
  ````
1396
1878
 
1397
1879
 
1398
- ```include
1880
+ ``` include
1399
1881
  require 'galaaz'
1400
1882
 
1401
1883
  # Loads the R 'caret' package. If not present, installs it
@@ -1427,7 +1909,7 @@ class Model
1427
1909
  def partition(field)
1428
1910
 
1429
1911
  train_index =
1430
- R.createDataPartition(@data.send(field), p: @percet_train,
1912
+ R.createDataPartition(@data.send(field), p: @percent_train,
1431
1913
  list: false, times: 1)
1432
1914
  @train = @data[train_index, :all]
1433
1915
  @test = @data[-train_index, :all]
@@ -1435,12 +1917,11 @@ class Model
1435
1917
  end
1436
1918
 
1437
1919
  end
1438
-
1439
1920
  ```
1440
1921
 
1441
1922
 
1442
- ```ruby
1443
- mtcars = ~:mtcars
1923
+ ``` ruby
1924
+ mtcars = ~R[:mtcars]
1444
1925
  model = Model.new(mtcars, percent_train: 0.8)
1445
1926
  model.partition(:mpg)
1446
1927
  puts model.train.head
@@ -1448,20 +1929,18 @@ puts model.test.head
1448
1929
  ```
1449
1930
 
1450
1931
  ```
1451
- ## mpg cyl disp hp drat wt qsec vs am gear carb
1452
- ## Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
1453
- ## Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
1454
- ## Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
1455
- ## Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
1456
- ## Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
1457
- ## Merc 450SE 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3
1458
1932
  ## mpg cyl disp hp drat wt qsec vs am gear carb
1933
+ ## Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
1459
1934
  ## Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
1460
1935
  ## Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
1461
1936
  ## Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
1462
- ## Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
1937
+ ## Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
1463
1938
  ## Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
1464
- ## Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
1939
+ ## mpg cyl disp hp drat wt qsec vs am gear carb
1940
+ ## Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
1941
+ ## Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
1942
+ ## Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
1943
+ ## Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4
1465
1944
  ```
1466
1945
 
1467
1946
  ## Documenting Gems
@@ -1469,9 +1948,9 @@ puts model.test.head
1469
1948
  gKnit also allows developers to document and load files that are not in the same directory
1470
1949
  of the '.Rmd' file.
1471
1950
 
1472
- Here is an example of loading the 'find.rb' file from TruffleRuby. In this example, relative
1473
- is set to FALSE, so Ruby will look for the file in its $LOAD\_PATH, and the user does not
1474
- need to no it's directory.
1951
+ Here is an example of loading Ruby’s standard library file `find.rb`. In this example, relative
1952
+ is set to FALSE, so Ruby will look for the file in its `$LOAD_PATH`, and the user does not
1953
+ need to know its directory on disk.
1475
1954
 
1476
1955
  ````
1477
1956
  ```{include find, relative = FALSE}
@@ -1479,7 +1958,7 @@ need to no it's directory.
1479
1958
  ````
1480
1959
 
1481
1960
 
1482
- ```include
1961
+ ``` include
1483
1962
  # frozen_string_literal: true
1484
1963
  #
1485
1964
  # find.rb: the Find module for processing all files under a given directory.
@@ -1497,7 +1976,7 @@ need to no it's directory.
1497
1976
  #
1498
1977
  # Find.find(ENV["HOME"]) do |path|
1499
1978
  # if FileTest.directory?(path)
1500
- # if File.basename(path)[0] == ?.
1979
+ # if File.basename(path).start_with?('.')
1501
1980
  # Find.prune # Don't look any further into this directory.
1502
1981
  # else
1503
1982
  # next
@@ -1509,6 +1988,8 @@ need to no it's directory.
1509
1988
  #
1510
1989
  module Find
1511
1990
 
1991
+ VERSION = "0.2.0"
1992
+
1512
1993
  #
1513
1994
  # Calls the associated block with the name of every file and directory listed
1514
1995
  # as arguments, then recursively on their subdirectories, and so on.
@@ -1528,24 +2009,24 @@ module Find
1528
2009
  ps = [path]
1529
2010
  while file = ps.shift
1530
2011
  catch(:prune) do
1531
- yield file.dup.taint
2012
+ yield file.dup
1532
2013
  begin
1533
2014
  s = File.lstat(file)
1534
- rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
2015
+ rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG, Errno::EINVAL
1535
2016
  raise unless ignore_error
1536
2017
  next
1537
2018
  end
1538
2019
  if s.directory? then
1539
2020
  begin
1540
2021
  fs = Dir.children(file, encoding: enc)
1541
- rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
2022
+ rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG, Errno::EINVAL
1542
2023
  raise unless ignore_error
1543
2024
  next
1544
2025
  end
1545
2026
  fs.sort!
1546
2027
  fs.reverse_each {|f|
1547
2028
  f = File.join(file, f)
1548
- ps.unshift f.untaint
2029
+ ps.unshift f
1549
2030
  }
1550
2031
  end
1551
2032
  end
@@ -1582,9 +2063,9 @@ the Yaml header to generate this blog in PDF format instead of HTML:
1582
2063
 
1583
2064
  ```
1584
2065
  ---
1585
- title: "gKnit - Ruby and R Knitting with Galaaz in GraalVM"
2066
+ title: "gKnit - Ruby and R Knitting with Galaaz"
1586
2067
  author: "Rodrigo Botafogo"
1587
- tags: [Galaaz, Ruby, R, TruffleRuby, FastR, GraalVM, knitr, gknit]
2068
+ tags: [Galaaz, Ruby, R, JRuby, knitr, gknit]
1588
2069
  date: "29 October 2018"
1589
2070
  output:
1590
2071
  pdf\_document:
@@ -1596,7 +2077,7 @@ output:
1596
2077
 
1597
2078
  ## Template based documents generation
1598
2079
 
1599
- When a document is converted to PDF it follows a certain convertion template. We've seen above
2080
+ When a document is converted to PDF it follows a certain conversion template. We've seen above
1600
2081
  the use of 'galaaz.sty' as a basic template to generate a PDF document. Using the
1601
2082
  'gknit-draft' app that comes with Galaaz, the same .Rmd file can be compiled to different
1602
2083
  looking PDF documents. Galaaz automatically loads the 'rticles' R package that comes with
@@ -1646,21 +2127,21 @@ gknit-draft --filename my_r_article --template rjournal_article --package rticle
1646
2127
 
1647
2128
  # Accessing R variables
1648
2129
 
1649
- Galaaz allows Ruby to access variables created in R. For example, the 'mtcars' data set is
1650
- available in R and can be accessed from Ruby by using the 'tilda' operator followed by the
1651
- symbol for the variable, in this case ':mtcar'. In the code bellow method 'outputs' is
1652
- used to output the 'mtcars' data set nicely formatted in HTML by use of the 'kable' and
1653
- 'kable_styling' functions. Method 'outputs' is only available when used with 'gknit'.
2130
+ Galaaz allows Ruby to access variables created in R. For example, the `mtcars` data set is
2131
+ available in R and can be accessed from Ruby by using the tilde operator followed by the
2132
+ symbol for the variable, in this case `:mtcars`. In the code below, method `outputs` is
2133
+ used to output the `mtcars` data set nicely formatted in HTML by use of the `kable` and
2134
+ `kable_styling` functions. Method `outputs` is only available when used with gKnit.
1654
2135
 
1655
2136
 
1656
- ```ruby
1657
- outputs (~:mtcars).kable.kable_styling
2137
+ ``` ruby
2138
+ outputs (~R[:mtcars]).kable.kable_styling
1658
2139
  ```
1659
2140
 
1660
2141
  <table class="table" style="margin-left: auto; margin-right: auto;">
1661
2142
  <thead>
1662
2143
  <tr>
1663
- <th style="text-align:left;"> </th>
2144
+ <th style="text-align:left;"> </th>
1664
2145
  <th style="text-align:right;"> mpg </th>
1665
2146
  <th style="text-align:right;"> cyl </th>
1666
2147
  <th style="text-align:right;"> disp </th>
@@ -2141,7 +2622,7 @@ table.
2141
2622
  | logical | logical | logical |
2142
2623
  | integer | numeric | integer |
2143
2624
  | double | numeric | double |
2144
- | complex | complex | comples |
2625
+ | complex | complex | complex |
2145
2626
  | character | character | character |
2146
2627
  | raw | raw | raw |
2147
2628
 
@@ -2153,7 +2634,7 @@ vector is often referred to as a character string.
2153
2634
  To create a vector the 'c' (concatenate) method from the 'R' module should be used:
2154
2635
 
2155
2636
 
2156
- ```ruby
2637
+ ``` ruby
2157
2638
  vec = R.c(1, 2, 3)
2158
2639
  puts vec
2159
2640
  ```
@@ -2173,7 +2654,7 @@ Data frames will later be more carefully described. In R, the method used to cr
2173
2654
  data frame is 'data.frame', in Galaaz we use 'data\_\_frame'.
2174
2655
 
2175
2656
 
2176
- ```ruby
2657
+ ``` ruby
2177
2658
  df = R.data__frame(typeof: vec.typeof, mode: vec.mode, storage__mode: vec.storage__mode)
2178
2659
  puts df
2179
2660
  ```
@@ -2189,7 +2670,7 @@ like '1' is converted to float and to have an integer the R developer will use '
2189
2670
  follows normal Ruby rules and the number 1 is an integer and 1.0 is a float.
2190
2671
 
2191
2672
 
2192
- ```ruby
2673
+ ``` ruby
2193
2674
  vec = R.c(1.0, 2, 3)
2194
2675
  puts vec
2195
2676
  ```
@@ -2199,7 +2680,7 @@ puts vec
2199
2680
  ```
2200
2681
 
2201
2682
 
2202
- ```ruby
2683
+ ``` ruby
2203
2684
  df = R.data__frame(typeof: vec.typeof, mode: vec.mode, storage__mode: vec.storage__mode)
2204
2685
  outputs df.kable.kable_styling
2205
2686
  ```
@@ -2227,41 +2708,30 @@ the first with a message explaining what went wrong and the second with the full
2227
2708
  of the error.
2228
2709
 
2229
2710
 
2230
- ```ruby
2711
+ ``` ruby
2231
2712
  vec = R.c(1, hello, 5)
2232
2713
  ```
2233
2714
 
2234
2715
  ```
2235
- ## Message:
2236
- ## undefined local variable or method `hello' for #<RC:0x3d8 @out_list=nil>:RC
2716
+ ## undefined local variable or method 'hello' for an instance of RC
2237
2717
  ```
2238
2718
 
2239
2719
  ```
2240
- ## Message:
2241
- ## /home/rbotafogo/desenv/galaaz/lib/util/exec_ruby.rb:103:in `get_binding'
2242
- ## /home/rbotafogo/desenv/galaaz/lib/util/exec_ruby.rb:102:in `eval'
2243
- ## /home/rbotafogo/desenv/galaaz/lib/util/exec_ruby.rb:102:in `exec_ruby'
2244
- ## /home/rbotafogo/desenv/galaaz/lib/gknit/knitr_engine.rb:650:in `block in initialize'
2245
- ## /home/rbotafogo/desenv/galaaz/lib/R_interface/ruby_callback.rb:77:in `call'
2246
- ## /home/rbotafogo/desenv/galaaz/lib/R_interface/ruby_callback.rb:77:in `callback'
2247
- ## (eval):3:in `function(...) {\n rb_method(...)'
2248
- ## unknown.r:1:in `in_dir'
2249
- ## unknown.r:1:in `block_exec'
2250
- ## /usr/local/lib/graalvm-ce-java11-20.0.0/languages/R/library/knitr/R/block.R:92:in `call_block'
2251
- ## /usr/local/lib/graalvm-ce-java11-20.0.0/languages/R/library/knitr/R/block.R:6:in `process_group.block'
2252
- ## /usr/local/lib/graalvm-ce-java11-20.0.0/languages/R/library/knitr/R/block.R:3:in `<no source>'
2253
- ## unknown.r:1:in `withCallingHandlers'
2254
- ## unknown.r:1:in `process_file'
2255
- ## unknown.r:1:in `<no source>'
2256
- ## unknown.r:1:in `<no source>'
2257
- ## <REPL>:4:in `<repl wrapper>'
2258
- ## <REPL>:1
2720
+ ## /home/rbotafogo/desenv_linux/galaaz/lib/util/exec_ruby.rb:170:in 'exec_ruby'
2721
+ ## org/jruby/RubyKernel.java:1268:in 'eval'
2722
+ ## /home/rbotafogo/desenv_linux/galaaz/lib/util/exec_ruby.rb:169:in 'exec_ruby'
2723
+ ## /home/rbotafogo/desenv_linux/galaaz/lib/gknit/knitr_engine.rb:777:in 'block in initialize'
2724
+ ## org/jruby/RubyBasicObject.java:2695:in 'instance_eval'
2725
+ ## org/jruby/RubyBasicObject.java:2723:in 'instance_eval'
2726
+ ## /home/rbotafogo/desenv_linux/galaaz/lib/gknit/knitr_engine.rb:748:in 'block in initialize'
2727
+ ## /home/rbotafogo/desenv_linux/galaaz/lib/R_interface/new_bridge_adapter.rb:358:in 'block in register_callback_proc_stub'
2728
+ ## /home/rbotafogo/desenv_linux/galaaz/lib/new_bridge/session_client.rb:413:in 'block in handle_call'
2259
2729
  ```
2260
2730
 
2261
2731
  Here is a vector with logical values
2262
2732
 
2263
2733
 
2264
- ```ruby
2734
+ ``` ruby
2265
2735
  vec = R.c(true, true, false, false, true)
2266
2736
  puts vec
2267
2737
  ```
@@ -2275,7 +2745,7 @@ puts vec
2275
2745
  The 'c' functions used to create vectors can also be used to combine two vectors:
2276
2746
 
2277
2747
 
2278
- ```ruby
2748
+ ``` ruby
2279
2749
  vec1 = R.c(10.0, 20.0, 30.0)
2280
2750
  vec2 = R.c(4.0, 5.0, 6.0)
2281
2751
  vec = R.c(vec1, vec2)
@@ -2290,10 +2760,10 @@ In this next example, method 'c' is chainned after 'vec1'. This also looks like
2290
2760
  method of the vector, but in reallity, this is actually closer to the pipe operator. When
2291
2761
  Galaaz identifies that 'c' is not a method of 'vec' it actually tries to call 'R.c' with
2292
2762
  'vec1' as the first argument concatenated with all the other available arguments. The code
2293
- bellow is automatically converted to the code above.
2763
+ below is automatically converted to the code above.
2294
2764
 
2295
2765
 
2296
- ```ruby
2766
+ ``` ruby
2297
2767
  vec = vec1.c(vec2)
2298
2768
  puts vec
2299
2769
  ```
@@ -2307,7 +2777,7 @@ puts vec
2307
2777
  Arithmetic operations on vectors are performed element by element:
2308
2778
 
2309
2779
 
2310
- ```ruby
2780
+ ``` ruby
2311
2781
  puts vec1 + vec2
2312
2782
  ```
2313
2783
 
@@ -2316,7 +2786,7 @@ puts vec1 + vec2
2316
2786
  ```
2317
2787
 
2318
2788
 
2319
- ```ruby
2789
+ ``` ruby
2320
2790
  puts vec1 * 5
2321
2791
  ```
2322
2792
 
@@ -2327,7 +2797,7 @@ puts vec1 * 5
2327
2797
  When vectors have different length, a recycling rule is applied to the shorter vector:
2328
2798
 
2329
2799
 
2330
- ```ruby
2800
+ ``` ruby
2331
2801
  vec3 = R.c(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0)
2332
2802
  puts vec4 = vec1 + vec3
2333
2803
  ```
@@ -2341,7 +2811,7 @@ puts vec4 = vec1 + vec3
2341
2811
  Vectors can be indexed by using the '[]' operator:
2342
2812
 
2343
2813
 
2344
- ```ruby
2814
+ ``` ruby
2345
2815
  puts vec4[3]
2346
2816
  ```
2347
2817
 
@@ -2349,11 +2819,11 @@ puts vec4[3]
2349
2819
  ## [1] 33
2350
2820
  ```
2351
2821
 
2352
- We can also index a vector with another vector. For example, in the code bellow, we take elements
2822
+ We can also index a vector with another vector. For example, in the code below, we take elements
2353
2823
  1, 3, 5, and 7 from vec3:
2354
2824
 
2355
2825
 
2356
- ```ruby
2826
+ ``` ruby
2357
2827
  puts vec4[R.c(1, 3, 5, 7)]
2358
2828
  ```
2359
2829
 
@@ -2364,7 +2834,7 @@ puts vec4[R.c(1, 3, 5, 7)]
2364
2834
  Repeating an index and having indices out of order is valid code:
2365
2835
 
2366
2836
 
2367
- ```ruby
2837
+ ``` ruby
2368
2838
  puts vec4[R.c(1, 3, 3, 1)]
2369
2839
  ```
2370
2840
 
@@ -2376,7 +2846,7 @@ It is also possible to index a vector with a negative number or negative vector.
2376
2846
  the indexed values are not returned:
2377
2847
 
2378
2848
 
2379
- ```ruby
2849
+ ``` ruby
2380
2850
  puts vec4[-3]
2381
2851
  puts vec4[-R.c(1, 3, 5, 7)]
2382
2852
  ```
@@ -2389,7 +2859,7 @@ puts vec4[-R.c(1, 3, 5, 7)]
2389
2859
  If an index is out of range, a missing value (NA) will be reported.
2390
2860
 
2391
2861
 
2392
- ```ruby
2862
+ ``` ruby
2393
2863
  puts vec4[30]
2394
2864
  ```
2395
2865
 
@@ -2400,7 +2870,7 @@ puts vec4[30]
2400
2870
  It is also possible to index a vector by range:
2401
2871
 
2402
2872
 
2403
- ```ruby
2873
+ ``` ruby
2404
2874
  puts vec4[(2..5)]
2405
2875
  ```
2406
2876
 
@@ -2411,7 +2881,7 @@ puts vec4[(2..5)]
2411
2881
  Elements in a vector can be named using the 'names' attribute of a vector:
2412
2882
 
2413
2883
 
2414
- ```ruby
2884
+ ``` ruby
2415
2885
  full_name = R.c("Rodrigo", "A", "Botafogo")
2416
2886
  full_name.names = R.c("First", "Middle", "Last")
2417
2887
  puts full_name
@@ -2425,7 +2895,7 @@ puts full_name
2425
2895
  Or it can also be named by using the 'c' function with named paramenters:
2426
2896
 
2427
2897
 
2428
- ```ruby
2898
+ ``` ruby
2429
2899
  full_name = R.c(First: "Rodrigo", Middle: "A", Last: "Botafogo")
2430
2900
  puts full_name
2431
2901
  ```
@@ -2443,7 +2913,7 @@ R::Vector with other ruby classes it might be necessary to extract the actual Ru
2443
2913
  from the vector. In order to do this extraction the '>>' operator is used.
2444
2914
 
2445
2915
 
2446
- ```ruby
2916
+ ``` ruby
2447
2917
  puts vec4
2448
2918
  puts vec4 >> 0
2449
2919
  puts vec4 >> 4
@@ -2463,7 +2933,7 @@ A matrix is a collection of elements organized as a two dimensional table. A ma
2463
2933
  created by the 'matrix' function:
2464
2934
 
2465
2935
 
2466
- ```ruby
2936
+ ``` ruby
2467
2937
  mat = R.matrix(R.c(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0),
2468
2938
  nrow: 3,
2469
2939
  ncol: 3)
@@ -2481,7 +2951,7 @@ Note that matrices data is organized by column first. It is possible to organize
2481
2951
  memory by row first passing an extra argument to the 'matrix' function:
2482
2952
 
2483
2953
 
2484
- ```ruby
2954
+ ``` ruby
2485
2955
  mat_row = R.matrix(R.c(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0),
2486
2956
  nrow: 3,
2487
2957
  ncol: 3,
@@ -2502,7 +2972,7 @@ puts mat_row
2502
2972
  A matrix can be indexed by [row, column]:
2503
2973
 
2504
2974
 
2505
- ```ruby
2975
+ ``` ruby
2506
2976
  puts mat_row[1, 1]
2507
2977
  puts mat_row[2, 3]
2508
2978
  ```
@@ -2514,7 +2984,7 @@ puts mat_row[2, 3]
2514
2984
  It is possible to index an entire row or column with the ':all' keyword
2515
2985
 
2516
2986
 
2517
- ```ruby
2987
+ ``` ruby
2518
2988
  puts mat_row[1, :all]
2519
2989
  puts mat_row[:all, 2]
2520
2990
  ```
@@ -2528,7 +2998,7 @@ Indexing with a vector is also possible for matrices. In the following example w
2528
2998
  rows 1 and 3 and columns 2 and 3 building a 2 x 2 matrix.
2529
2999
 
2530
3000
 
2531
- ```ruby
3001
+ ``` ruby
2532
3002
  puts mat_row[R.c(1, 3), R.c(2, 3)]
2533
3003
  ```
2534
3004
 
@@ -2541,7 +3011,7 @@ puts mat_row[R.c(1, 3), R.c(2, 3)]
2541
3011
  Matrices can be combined with functions 'rbind':
2542
3012
 
2543
3013
 
2544
- ```ruby
3014
+ ``` ruby
2545
3015
  puts mat_row.rbind(mat)
2546
3016
  ```
2547
3017
 
@@ -2558,7 +3028,7 @@ puts mat_row.rbind(mat)
2558
3028
  and 'cbind':
2559
3029
 
2560
3030
 
2561
- ```ruby
3031
+ ``` ruby
2562
3032
  puts mat_row.cbind(mat)
2563
3033
  ```
2564
3034
 
@@ -2575,7 +3045,7 @@ A list is a data structure that can contain sublists of different types, while v
2575
3045
  can only hold one type of element.
2576
3046
 
2577
3047
 
2578
- ```ruby
3048
+ ``` ruby
2579
3049
  nums = R.c(1.0, 2.0, 3.0)
2580
3050
  strs = R.c("a", "b", "c", "d")
2581
3051
  bool = R.c(true, true, false)
@@ -2604,7 +3074,7 @@ first start with the '[]' operator. The list above has three sublist indexing wi
2604
3074
  return one of the sublists.
2605
3075
 
2606
3076
 
2607
- ```ruby
3077
+ ``` ruby
2608
3078
  puts lst[1]
2609
3079
  ```
2610
3080
 
@@ -2619,7 +3089,7 @@ the original list
2619
3089
 
2620
3090
 
2621
3091
 
2622
- ```ruby
3092
+ ``` ruby
2623
3093
  puts lst[[1]]
2624
3094
  ```
2625
3095
 
@@ -2630,7 +3100,7 @@ puts lst[[1]]
2630
3100
  When elements are named, as dones with lst, indexing can be done by name:
2631
3101
 
2632
3102
 
2633
- ```ruby
3103
+ ``` ruby
2634
3104
  puts lst[['bool']][[1]] >> 0
2635
3105
  ```
2636
3106
 
@@ -2647,11 +3117,11 @@ operator) and then the vector was indexed by its first element, extracting the n
2647
3117
 
2648
3118
  A data frame is a table like structure in which each column has the same number of
2649
3119
  rows. Data frames are the basic structure for storing data for data analysis. We have already
2650
- seen a data frame previously when we accessed variable '~:mtcars'. In order to create a
3120
+ seen a data frame previously when we accessed variable '~R[:mtcars]'. In order to create a
2651
3121
  data frame, function 'data__frame' is used:
2652
3122
 
2653
3123
 
2654
- ```ruby
3124
+ ``` ruby
2655
3125
  df = R.data__frame(
2656
3126
  year: R.c(2010, 2011, 2012),
2657
3127
  income: R.c(1000.0, 1500.0, 2000.0))
@@ -2672,10 +3142,10 @@ A data frame can be indexed the same way as a matrix, by using '[row, column]',
2672
3142
  column can either be a numeric or the name of the row or column
2673
3143
 
2674
3144
 
2675
- ```ruby
2676
- puts (~:mtcars).head
2677
- puts (~:mtcars)[1, 2]
2678
- puts (~:mtcars)['Datsun 710', 'mpg']
3145
+ ``` ruby
3146
+ puts (~R[:mtcars]).head
3147
+ puts (~R[:mtcars])[1, 2]
3148
+ puts (~R[:mtcars])['Datsun 710', 'mpg']
2679
3149
  ```
2680
3150
 
2681
3151
  ```
@@ -2694,52 +3164,48 @@ Extracting a column from a data frame as a vector can be done by using the doubl
2694
3164
  operator:
2695
3165
 
2696
3166
 
2697
- ```ruby
2698
- puts (~:mtcars)[['mpg']]
3167
+ ``` ruby
3168
+ puts (~R[:mtcars])[['mpg']]
2699
3169
  ```
2700
3170
 
2701
3171
  ```
2702
- ## [1] 21.0 21.0 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 17.8 16.4 17.3 15.2
2703
- ## [15] 10.4 10.4 14.7 32.4 30.4 33.9 21.5 15.5 15.2 13.3 19.2 27.3 26.0 30.4
2704
- ## [29] 15.8 19.7 15.0 21.4
3172
+ ## [1] 21.0 21.0 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 17.8 16.4 17.3 15.2 10.4
3173
+ ## [16] 10.4 14.7 32.4 30.4 33.9 21.5 15.5 15.2 13.3 19.2 27.3 26.0 30.4 15.8 19.7
3174
+ ## [31] 15.0 21.4
2705
3175
  ```
2706
3176
 
2707
3177
  A data frame column can also be accessed as if it were an instance variable of the data frame:
2708
3178
 
2709
3179
 
2710
- ```ruby
2711
- puts (~:mtcars).mpg
3180
+ ``` ruby
3181
+ puts (~R[:mtcars]).mpg
2712
3182
  ```
2713
3183
 
2714
3184
  ```
2715
- ## [1] 21.0 21.0 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 17.8 16.4 17.3 15.2
2716
- ## [15] 10.4 10.4 14.7 32.4 30.4 33.9 21.5 15.5 15.2 13.3 19.2 27.3 26.0 30.4
2717
- ## [29] 15.8 19.7 15.0 21.4
3185
+ ## [1] 21.0 21.0 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 17.8 16.4 17.3 15.2 10.4
3186
+ ## [16] 10.4 14.7 32.4 30.4 33.9 21.5 15.5 15.2 13.3 19.2 27.3 26.0 30.4 15.8 19.7
3187
+ ## [31] 15.0 21.4
2718
3188
  ```
2719
3189
 
2720
3190
  Slicing a data frame can be done by indexing it with a vector (we use 'head' to reduce the
2721
3191
  output):
2722
3192
 
2723
3193
 
2724
- ```ruby
2725
- puts (~:mtcars)[R.c('mpg', 'hp')].head
3194
+ ``` ruby
3195
+ puts (~R[:mtcars])[R.c('mpg', 'hp')].head
2726
3196
  ```
2727
3197
 
2728
3198
  ```
2729
- ## mpg hp
2730
- ## Mazda RX4 21.0 110
2731
- ## Mazda RX4 Wag 21.0 110
2732
- ## Datsun 710 22.8 93
2733
- ## Hornet 4 Drive 21.4 110
2734
- ## Hornet Sportabout 18.7 175
2735
- ## Valiant 18.1 105
3199
+ ## mpg cyl disp hp drat wt qsec vs am gear carb
3200
+ ## NA NA NA NA NA NA NA NA NA NA NA NA
3201
+ ## NA.1 NA NA NA NA NA NA NA NA NA NA NA
2736
3202
  ```
2737
3203
 
2738
3204
  A row slice can be obtained by indexing by row and using the ':all' keyword for the column:
2739
3205
 
2740
3206
 
2741
- ```ruby
2742
- puts (~:mtcars)[R.c('Datsun 710', 'Camaro Z28'), :all]
3207
+ ``` ruby
3208
+ puts (~R[:mtcars])[R.c('Datsun 710', 'Camaro Z28'), :all]
2743
3209
  ```
2744
3210
 
2745
3211
  ```
@@ -2750,29 +3216,29 @@ puts (~:mtcars)[R.c('Datsun 710', 'Camaro Z28'), :all]
2750
3216
 
2751
3217
  Finally, a data frame can also be indexed with a logical vector. In this next example, the
2752
3218
  'am' column of :mtcars is compared with 0 (with method 'eq'). When 'am' is equal to 0 the
2753
- car is automatic. So, by doing '(~:mtcars).am.eq 0' a logical vector is created with
3219
+ car is automatic. So, by doing '(~R[:mtcars]).am.eq 0' a logical vector is created with
2754
3220
  'true' whenever 'am' is 0 and 'false' otherwise.
2755
3221
 
2756
3222
 
2757
- ```ruby
3223
+ ``` ruby
2758
3224
  # obtain a vector with 'true' for cars with automatic transmission
2759
- automatic = (~:mtcars).am.eq 0
3225
+ automatic = (~R[:mtcars]).am.eq 0
2760
3226
  puts automatic
2761
3227
  ```
2762
3228
 
2763
3229
  ```
2764
- ## [1] FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
2765
- ## [12] TRUE TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE TRUE TRUE
2766
- ## [23] TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
3230
+ ## [1] FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
3231
+ ## [13] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE TRUE TRUE TRUE TRUE
3232
+ ## [25] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
2767
3233
  ```
2768
3234
 
2769
3235
  Using this logical vector, the data frame is indexed, returning a new data frame in
2770
3236
  which all cars have automatic transmission.
2771
3237
 
2772
3238
 
2773
- ```ruby
3239
+ ``` ruby
2774
3240
  # slice the data frame by using this vector
2775
- puts (~:mtcars)[automatic, :all]
3241
+ puts (~R[:mtcars])[automatic, :all]
2776
3242
  ```
2777
3243
 
2778
3244
  ```
@@ -2805,12 +3271,12 @@ Galaaz extends Ruby to work with complex expressions, similar to R's expressions
2805
3271
 
2806
3272
  ## Expressions from operators
2807
3273
 
2808
- The code bellow
3274
+ The code below
2809
3275
  creates an expression summing two symbols
2810
3276
 
2811
3277
 
2812
- ```ruby
2813
- exp1 = :a + :b
3278
+ ``` ruby
3279
+ exp1 = R[:a] + R[:b]
2814
3280
  puts exp1
2815
3281
  ```
2816
3282
 
@@ -2820,20 +3286,20 @@ puts exp1
2820
3286
  We can build any complex mathematical expression
2821
3287
 
2822
3288
 
2823
- ```ruby
2824
- exp2 = (:a + :b) * 2.0 + :c ** 2 / :z
3289
+ ``` ruby
3290
+ exp2 = (R[:a] + R[:b]) * 2.0 + R[:c] ** 2 / R[:z]
2825
3291
  puts exp2
2826
3292
  ```
2827
3293
 
2828
3294
  ```
2829
- ## (a + b) * 2 + c^2L/z
3295
+ ## a + b * 2.0 + c ^ 2L / z
2830
3296
  ```
2831
3297
 
2832
3298
  It is also possible to use inequality operators in building expressions
2833
3299
 
2834
3300
 
2835
- ```ruby
2836
- exp3 = (:a + :b) >= :z
3301
+ ``` ruby
3302
+ exp3 = (R[:a] + R[:b]) >= :z
2837
3303
  puts exp3
2838
3304
  ```
2839
3305
 
@@ -2846,8 +3312,8 @@ notation for those operators such as (.gt, .ge, etc.). So the same expression w
2846
3312
  above can also be written as
2847
3313
 
2848
3314
 
2849
- ```ruby
2850
- exp4 = (:a + :b).ge :z
3315
+ ``` ruby
3316
+ exp4 = (R[:a] + R[:b]).ge :z
2851
3317
  puts exp4
2852
3318
  ```
2853
3319
 
@@ -2860,8 +3326,8 @@ those are expressions involving '==', and '='. In order to write an expression
2860
3326
  need to use the method '.eq' and for '=' we need the function '.assign'
2861
3327
 
2862
3328
 
2863
- ```ruby
2864
- exp5 = (:a + :b).eq :z
3329
+ ``` ruby
3330
+ exp5 = (R[:a] + R[:b]).eq :z
2865
3331
  puts exp5
2866
3332
  ```
2867
3333
 
@@ -2870,8 +3336,8 @@ puts exp5
2870
3336
  ```
2871
3337
 
2872
3338
 
2873
- ```ruby
2874
- exp6 = :y.assign :a + :b
3339
+ ``` ruby
3340
+ exp6 = R[:y].assign R[:a] + R[:b]
2875
3341
  puts exp6
2876
3342
  ```
2877
3343
 
@@ -2882,12 +3348,12 @@ In general we think that using the functional notation is preferable to using th
2882
3348
  symbolic notation as otherwise, we end up writing invalid expressions such as
2883
3349
 
2884
3350
 
2885
- ```ruby
2886
- exp_wrong = (:a + :b) == :z
3351
+ ``` ruby
3352
+ exp_wrong = (R[:a] + R[:b]) == :z
2887
3353
  puts exp_wrong
2888
3354
  ```
2889
3355
  and it might be difficult to understand what is going on here. The problem lies with the fact that
2890
- when using '==' we are comparing expression (:a + :b) to expression :z with '=='. When the
3356
+ when using '==' we are comparing expression (R[:a] + R[:b]) to expression :z with '=='. When the
2891
3357
  comparison is executed, the system tries to evaluate :a, :b and :z, and those symbols at
2892
3358
  this time are not bound to anything and we get a "object 'a' not found" message.
2893
3359
  If we only use functional notation, this type of error will not occur.
@@ -2902,8 +3368,8 @@ When we want the function to be part of the expression, we call the function pre
2902
3368
  by the letter E, such as 'E.sin(x)'
2903
3369
 
2904
3370
 
2905
- ```ruby
2906
- exp7 = :y.assign E.sin(:x)
3371
+ ``` ruby
3372
+ exp7 = R[:y].assign E.sin(R[:x])
2907
3373
  puts exp7
2908
3374
  ```
2909
3375
 
@@ -2914,8 +3380,8 @@ puts exp7
2914
3380
  Expressions can also be written using '.' notation:
2915
3381
 
2916
3382
 
2917
- ```ruby
2918
- exp8 = :y.assign :x.sin
3383
+ ``` ruby
3384
+ exp8 = R[:y].assign R[:x].sin
2919
3385
  puts exp8
2920
3386
  ```
2921
3387
 
@@ -2926,8 +3392,8 @@ puts exp8
2926
3392
  When a function has multiple arguments, the first one can be used before the '.':
2927
3393
 
2928
3394
 
2929
- ```ruby
2930
- exp9 = :x.c(:y)
3395
+ ``` ruby
3396
+ exp9 = R[:x].c(R[:y])
2931
3397
  puts exp9
2932
3398
  ```
2933
3399
 
@@ -2941,19 +3407,19 @@ Expressions can be evaluated by calling function 'eval' with a binding. A bindin
2941
3407
  with a list:
2942
3408
 
2943
3409
 
2944
- ```ruby
2945
- exp = (:a + :b) * 2.0 + :c ** 2 / :z
3410
+ ``` ruby
3411
+ exp = (R[:a] + R[:b]) * 2.0 + R[:c] ** 2 / R[:z]
2946
3412
  puts exp.eval(R.list(a: 10, b: 20, c: 30, z: 40))
2947
3413
  ```
2948
3414
 
2949
3415
  ```
2950
- ## [1] 82.5
3416
+ ## [1] 72.5
2951
3417
  ```
2952
3418
 
2953
3419
  ... with a data frame:
2954
3420
 
2955
3421
 
2956
- ```ruby
3422
+ ``` ruby
2957
3423
  df = R.data__frame(
2958
3424
  a: R.c(1, 2, 3),
2959
3425
  b: R.c(10, 20, 30),
@@ -2964,13 +3430,13 @@ puts exp.eval(df)
2964
3430
  ```
2965
3431
 
2966
3432
  ```
2967
- ## [1] 32 64 96
3433
+ ## [1] 31 62 93
2968
3434
  ```
2969
3435
 
2970
3436
  # Manipulating Data
2971
3437
 
2972
3438
  One of the major benefits of Galaaz is to bring strong data manipulation to Ruby. The following
2973
- examples were extracted from Hardley's "R for Data Science" (https://r4ds.had.co.nz/). This
3439
+ examples were extracted from Hadley's "R for Data Science" (https://r4ds.had.co.nz/). This
2974
3440
  is a highly recommended book for those not already familiar with the 'tidyverse' style of
2975
3441
  programming in R. In the sections to follow, we will limit ourselves to convert the R code to
2976
3442
  Galaaz.
@@ -2982,62 +3448,60 @@ locally, and if not, installs it. This data frame contains all 336,776 flights t
2982
3448
  departed from New York City in 2013. The data comes from the US Bureau of
2983
3449
  Transportation Statistics.
2984
3450
 
2985
- Dplyr uses 'tibbles' in place of data frames; unfortunately, tibbles do not print yet properly in
2986
- Galaaz due to a bug in fastR. In order to print a tibble we need to convert it to a data frame
2987
- using the 'as\_\_data__frame' method.
3451
+ Dplyr often uses **tibbles** in place of classic data frames. In Galaaz, printing may differ from
3452
+ the R console; if you need a classic tabular printout, convert with **`as__data__frame`** (or use
3453
+ `head` / `str` in R via `R` calls).
2988
3454
 
2989
3455
 
2990
- ```ruby
3456
+ ``` ruby
2991
3457
  R.install_and_loads('nycflights13')
2992
3458
  R.library('dplyr')
2993
3459
  ```
2994
3460
 
2995
3461
 
2996
- ```ruby
2997
- flights = ~:flights
3462
+ ``` ruby
3463
+ flights = ~R[:flights]
2998
3464
  puts flights.head
2999
3465
  ```
3000
3466
 
3001
3467
  ```
3002
- ## # A tibble: 6 x 19
3003
- ## year month day dep_time sched_dep_time dep_delay arr_time
3004
- ## <int> <int> <int> <int> <int> <dbl> <int>
3005
- ## 1 2013 1 1 517 515 2 830
3006
- ## 2 2013 1 1 533 529 4 850
3007
- ## 3 2013 1 1 542 540 2 923
3008
- ## 4 2013 1 1 544 545 -1 1004
3009
- ## 5 2013 1 1 554 600 -6 812
3010
- ## 6 2013 1 1 554 558 -4 740
3011
- ## # with 12 more variables: sched_arr_time <int>, arr_delay <dbl>,
3012
- ## # carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
3013
- ## # air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
3014
- ## # time_hour <dttm>
3468
+ ## # A tibble: 6 × 19
3469
+ ## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
3470
+ ## <int> <int> <int> <int> <int> <dbl> <int> <int>
3471
+ ## 1 2013 1 1 517 515 2 830 819
3472
+ ## 2 2013 1 1 533 529 4 850 830
3473
+ ## 3 2013 1 1 542 540 2 923 850
3474
+ ## 4 2013 1 1 544 545 -1 1004 1022
3475
+ ## 5 2013 1 1 554 600 -6 812 837
3476
+ ## 6 2013 1 1 554 558 -4 740 728
3477
+ ## # 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
3478
+ ## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
3479
+ ## # hour <dbl>, minute <dbl>, time_hour <dttm>
3015
3480
  ```
3016
3481
 
3017
3482
  ## Filtering rows with Filter
3018
3483
 
3019
3484
  In this example we filter the flights data set by giving to the filter function two expressions:
3020
- the first :month.eq 1
3485
+ the first R[:month].eq 1
3021
3486
 
3022
3487
 
3023
- ```ruby
3024
- puts flights.filter((:month.eq 1), (:day.eq 1)).head
3488
+ ``` ruby
3489
+ puts flights.filter((R[:month].eq 1), (R[:day].eq 1)).head
3025
3490
  ```
3026
3491
 
3027
3492
  ```
3028
- ## # A tibble: 6 x 19
3029
- ## year month day dep_time sched_dep_time dep_delay arr_time
3030
- ## <int> <int> <int> <int> <int> <dbl> <int>
3031
- ## 1 2013 1 1 517 515 2 830
3032
- ## 2 2013 1 1 533 529 4 850
3033
- ## 3 2013 1 1 542 540 2 923
3034
- ## 4 2013 1 1 544 545 -1 1004
3035
- ## 5 2013 1 1 554 600 -6 812
3036
- ## 6 2013 1 1 554 558 -4 740
3037
- ## # with 12 more variables: sched_arr_time <int>, arr_delay <dbl>,
3038
- ## # carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
3039
- ## # air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
3040
- ## # time_hour <dttm>
3493
+ ## # A tibble: 6 × 19
3494
+ ## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
3495
+ ## <int> <int> <int> <int> <int> <dbl> <int> <int>
3496
+ ## 1 2013 1 1 517 515 2 830 819
3497
+ ## 2 2013 1 1 533 529 4 850 830
3498
+ ## 3 2013 1 1 542 540 2 923 850
3499
+ ## 4 2013 1 1 544 545 -1 1004 1022
3500
+ ## 5 2013 1 1 554 600 -6 812 837
3501
+ ## 6 2013 1 1 554 558 -4 740 728
3502
+ ## # 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
3503
+ ## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
3504
+ ## # hour <dbl>, minute <dbl>, time_hour <dttm>
3041
3505
  ```
3042
3506
 
3043
3507
  ## Logical Operators
@@ -3045,24 +3509,23 @@ puts flights.filter((:month.eq 1), (:day.eq 1)).head
3045
3509
  All flights that departed in November of December
3046
3510
 
3047
3511
 
3048
- ```ruby
3049
- puts flights.filter((:month.eq 11) | (:month.eq 12)).head
3512
+ ``` ruby
3513
+ puts flights.filter((R[:month].eq 11) | (R[:month].eq 12)).head
3050
3514
  ```
3051
3515
 
3052
3516
  ```
3053
- ## # A tibble: 6 x 19
3054
- ## year month day dep_time sched_dep_time dep_delay arr_time
3055
- ## <int> <int> <int> <int> <int> <dbl> <int>
3056
- ## 1 2013 11 1 5 2359 6 352
3057
- ## 2 2013 11 1 35 2250 105 123
3058
- ## 3 2013 11 1 455 500 -5 641
3059
- ## 4 2013 11 1 539 545 -6 856
3060
- ## 5 2013 11 1 542 545 -3 831
3061
- ## 6 2013 11 1 549 600 -11 912
3062
- ## # with 12 more variables: sched_arr_time <int>, arr_delay <dbl>,
3063
- ## # carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
3064
- ## # air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
3065
- ## # time_hour <dttm>
3517
+ ## # A tibble: 6 × 19
3518
+ ## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
3519
+ ## <int> <int> <int> <int> <int> <dbl> <int> <int>
3520
+ ## 1 2013 11 1 5 2359 6 352 345
3521
+ ## 2 2013 11 1 35 2250 105 123 2356
3522
+ ## 3 2013 11 1 455 500 -5 641 651
3523
+ ## 4 2013 11 1 539 545 -6 856 827
3524
+ ## 5 2013 11 1 542 545 -3 831 855
3525
+ ## 6 2013 11 1 549 600 -11 912 923
3526
+ ## # 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
3527
+ ## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
3528
+ ## # hour <dbl>, minute <dbl>, time_hour <dttm>
3066
3529
  ```
3067
3530
 
3068
3531
  The same as above, but using the 'in' operator. In R, it is possible to define many operators
@@ -3071,24 +3534,23 @@ operators from Galaaz the '._' method is used, where the first argument is the o
3071
3534
  symbol, in this case ':in' and the second argument is the vector:
3072
3535
 
3073
3536
 
3074
- ```ruby
3075
- puts flights.filter(:month._ :in, R.c(11, 12)).head
3537
+ ``` ruby
3538
+ puts flights.filter(R[:month]._ :in, R.c(11, 12)).head
3076
3539
  ```
3077
3540
 
3078
3541
  ```
3079
- ## # A tibble: 6 x 19
3080
- ## year month day dep_time sched_dep_time dep_delay arr_time
3081
- ## <int> <int> <int> <int> <int> <dbl> <int>
3082
- ## 1 2013 11 1 5 2359 6 352
3083
- ## 2 2013 11 1 35 2250 105 123
3084
- ## 3 2013 11 1 455 500 -5 641
3085
- ## 4 2013 11 1 539 545 -6 856
3086
- ## 5 2013 11 1 542 545 -3 831
3087
- ## 6 2013 11 1 549 600 -11 912
3088
- ## # with 12 more variables: sched_arr_time <int>, arr_delay <dbl>,
3089
- ## # carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
3090
- ## # air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
3091
- ## # time_hour <dttm>
3542
+ ## # A tibble: 6 × 19
3543
+ ## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
3544
+ ## <int> <int> <int> <int> <int> <dbl> <int> <int>
3545
+ ## 1 2013 11 1 5 2359 6 352 345
3546
+ ## 2 2013 11 1 35 2250 105 123 2356
3547
+ ## 3 2013 11 1 455 500 -5 641 651
3548
+ ## 4 2013 11 1 539 545 -6 856 827
3549
+ ## 5 2013 11 1 542 545 -3 831 855
3550
+ ## 6 2013 11 1 549 600 -11 912 923
3551
+ ## # 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
3552
+ ## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
3553
+ ## # hour <dbl>, minute <dbl>, time_hour <dttm>
3092
3554
  ```
3093
3555
 
3094
3556
  ## Filtering with NA (Not Available)
@@ -3099,17 +3561,17 @@ the values and the result of some subsetting operations that are more consistent
3099
3561
  what is obtained from data frame.
3100
3562
 
3101
3563
 
3102
- ```ruby
3564
+ ``` ruby
3103
3565
  df = R.tibble(x: R.c(1, R::NA, 3))
3104
3566
  puts df
3105
3567
  ```
3106
3568
 
3107
3569
  ```
3108
- ## # A tibble: 3 x 1
3570
+ ## # A tibble: 3 × 1
3109
3571
  ## x
3110
3572
  ## <int>
3111
3573
  ## 1 1
3112
- ## 2
3574
+ ## 2 NA
3113
3575
  ## 3 3
3114
3576
  ```
3115
3577
 
@@ -3117,12 +3579,12 @@ Now filtering by :x > 1 shows all lines that satisfy this condition, where the r
3117
3579
  not.
3118
3580
 
3119
3581
 
3120
- ```ruby
3121
- puts df.filter(:x > 1)
3582
+ ``` ruby
3583
+ puts df.filter(R[:x] > 1)
3122
3584
  ```
3123
3585
 
3124
3586
  ```
3125
- ## # A tibble: 1 x 1
3587
+ ## # A tibble: 1 × 1
3126
3588
  ## x
3127
3589
  ## <int>
3128
3590
  ## 1 3
@@ -3131,15 +3593,15 @@ puts df.filter(:x > 1)
3131
3593
  To match an NA use method 'is__na'
3132
3594
 
3133
3595
 
3134
- ```ruby
3135
- puts df.filter((:x.is__na) | (:x > 1))
3596
+ ``` ruby
3597
+ puts df.filter((R[:x].is__na) | (R[:x] > 1))
3136
3598
  ```
3137
3599
 
3138
3600
  ```
3139
- ## # A tibble: 2 x 1
3601
+ ## # A tibble: 2 × 1
3140
3602
  ## x
3141
3603
  ## <int>
3142
- ## 1
3604
+ ## 1 NA
3143
3605
  ## 2 3
3144
3606
  ```
3145
3607
 
@@ -3148,47 +3610,45 @@ puts df.filter((:x.is__na) | (:x > 1))
3148
3610
  Arrange reorders the rows of a data frame by the given arguments.
3149
3611
 
3150
3612
 
3151
- ```ruby
3613
+ ``` ruby
3152
3614
  puts flights.arrange(:year, :month, :day).head
3153
3615
  ```
3154
3616
 
3155
3617
  ```
3156
- ## # A tibble: 6 x 19
3157
- ## year month day dep_time sched_dep_time dep_delay arr_time
3158
- ## <int> <int> <int> <int> <int> <dbl> <int>
3159
- ## 1 2013 1 1 517 515 2 830
3160
- ## 2 2013 1 1 533 529 4 850
3161
- ## 3 2013 1 1 542 540 2 923
3162
- ## 4 2013 1 1 544 545 -1 1004
3163
- ## 5 2013 1 1 554 600 -6 812
3164
- ## 6 2013 1 1 554 558 -4 740
3165
- ## # with 12 more variables: sched_arr_time <int>, arr_delay <dbl>,
3166
- ## # carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
3167
- ## # air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
3168
- ## # time_hour <dttm>
3618
+ ## # A tibble: 6 × 19
3619
+ ## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
3620
+ ## <int> <int> <int> <int> <int> <dbl> <int> <int>
3621
+ ## 1 2013 1 1 517 515 2 830 819
3622
+ ## 2 2013 1 1 533 529 4 850 830
3623
+ ## 3 2013 1 1 542 540 2 923 850
3624
+ ## 4 2013 1 1 544 545 -1 1004 1022
3625
+ ## 5 2013 1 1 554 600 -6 812 837
3626
+ ## 6 2013 1 1 554 558 -4 740 728
3627
+ ## # 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
3628
+ ## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
3629
+ ## # hour <dbl>, minute <dbl>, time_hour <dttm>
3169
3630
  ```
3170
3631
 
3171
3632
  To arrange in descending order, use function 'desc'
3172
3633
 
3173
3634
 
3174
- ```ruby
3175
- puts flights.arrange(:dep_delay.desc).head
3635
+ ``` ruby
3636
+ puts flights.arrange(R[:dep_delay].desc).head
3176
3637
  ```
3177
3638
 
3178
3639
  ```
3179
- ## # A tibble: 6 x 19
3180
- ## year month day dep_time sched_dep_time dep_delay arr_time
3181
- ## <int> <int> <int> <int> <int> <dbl> <int>
3182
- ## 1 2013 1 9 641 900 1301 1242
3183
- ## 2 2013 6 15 1432 1935 1137 1607
3184
- ## 3 2013 1 10 1121 1635 1126 1239
3185
- ## 4 2013 9 20 1139 1845 1014 1457
3186
- ## 5 2013 7 22 845 1600 1005 1044
3187
- ## 6 2013 4 10 1100 1900 960 1342
3188
- ## # with 12 more variables: sched_arr_time <int>, arr_delay <dbl>,
3189
- ## # carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
3190
- ## # air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
3191
- ## # time_hour <dttm>
3640
+ ## # A tibble: 6 × 19
3641
+ ## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
3642
+ ## <int> <int> <int> <int> <int> <dbl> <int> <int>
3643
+ ## 1 2013 1 9 641 900 1301 1242 1530
3644
+ ## 2 2013 6 15 1432 1935 1137 1607 2120
3645
+ ## 3 2013 1 10 1121 1635 1126 1239 1810
3646
+ ## 4 2013 9 20 1139 1845 1014 1457 2210
3647
+ ## 5 2013 7 22 845 1600 1005 1044 1815
3648
+ ## 6 2013 4 10 1100 1900 960 1342 2211
3649
+ ## # 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
3650
+ ## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
3651
+ ## # hour <dbl>, minute <dbl>, time_hour <dttm>
3192
3652
  ```
3193
3653
 
3194
3654
  ## Selecting columns
@@ -3196,12 +3656,12 @@ puts flights.arrange(:dep_delay.desc).head
3196
3656
  To select specific columns from a dataset we use function 'select':
3197
3657
 
3198
3658
 
3199
- ```ruby
3659
+ ``` ruby
3200
3660
  puts flights.select(:year, :month, :day).head
3201
3661
  ```
3202
3662
 
3203
3663
  ```
3204
- ## # A tibble: 6 x 3
3664
+ ## # A tibble: 6 × 3
3205
3665
  ## year month day
3206
3666
  ## <int> <int> <int>
3207
3667
  ## 1 2013 1 1
@@ -3215,12 +3675,12 @@ puts flights.select(:year, :month, :day).head
3215
3675
  It is also possible to select column in a given range
3216
3676
 
3217
3677
 
3218
- ```ruby
3219
- puts flights.select(:year.up_to :day).head
3678
+ ``` ruby
3679
+ puts flights.select(R[:year].up_to(R[:day])).head
3220
3680
  ```
3221
3681
 
3222
3682
  ```
3223
- ## # A tibble: 6 x 3
3683
+ ## # A tibble: 6 × 3
3224
3684
  ## year month day
3225
3685
  ## <int> <int> <int>
3226
3686
  ## 1 2013 1 1
@@ -3234,12 +3694,12 @@ puts flights.select(:year.up_to :day).head
3234
3694
  Select all columns that start with a given name sequence
3235
3695
 
3236
3696
 
3237
- ```ruby
3697
+ ``` ruby
3238
3698
  puts flights.select(E.starts_with('arr')).head
3239
3699
  ```
3240
3700
 
3241
3701
  ```
3242
- ## # A tibble: 6 x 2
3702
+ ## # A tibble: 6 × 2
3243
3703
  ## arr_time arr_delay
3244
3704
  ## <int> <dbl>
3245
3705
  ## 1 830 11
@@ -3264,32 +3724,31 @@ Other functions that can be used:
3264
3724
  A helper function that comes in handy when we just want to rearrange column order is 'Everything':
3265
3725
 
3266
3726
 
3267
- ```ruby
3727
+ ``` ruby
3268
3728
  puts flights.select(:year, :month, :day, E.everything).head
3269
3729
  ```
3270
3730
 
3271
3731
  ```
3272
- ## # A tibble: 6 x 19
3273
- ## year month day dep_time sched_dep_time dep_delay arr_time
3274
- ## <int> <int> <int> <int> <int> <dbl> <int>
3275
- ## 1 2013 1 1 517 515 2 830
3276
- ## 2 2013 1 1 533 529 4 850
3277
- ## 3 2013 1 1 542 540 2 923
3278
- ## 4 2013 1 1 544 545 -1 1004
3279
- ## 5 2013 1 1 554 600 -6 812
3280
- ## 6 2013 1 1 554 558 -4 740
3281
- ## # with 12 more variables: sched_arr_time <int>, arr_delay <dbl>,
3282
- ## # carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
3283
- ## # air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
3284
- ## # time_hour <dttm>
3732
+ ## # A tibble: 6 × 19
3733
+ ## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
3734
+ ## <int> <int> <int> <int> <int> <dbl> <int> <int>
3735
+ ## 1 2013 1 1 517 515 2 830 819
3736
+ ## 2 2013 1 1 533 529 4 850 830
3737
+ ## 3 2013 1 1 542 540 2 923 850
3738
+ ## 4 2013 1 1 544 545 -1 1004 1022
3739
+ ## 5 2013 1 1 554 600 -6 812 837
3740
+ ## 6 2013 1 1 554 558 -4 740 728
3741
+ ## # 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
3742
+ ## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
3743
+ ## # hour <dbl>, minute <dbl>, time_hour <dttm>
3285
3744
  ```
3286
3745
 
3287
3746
  ## Add variables to a dataframe with 'mutate'
3288
3747
 
3289
3748
 
3290
- ```ruby
3749
+ ``` ruby
3291
3750
  flights_sm = flights.
3292
- select((:year.up_to :day),
3751
+ select((R[:year].up_to(R[:day])),
3293
3752
  E.ends_with('delay'),
3294
3753
  :distance,
3295
3754
  :air_time)
@@ -3298,7 +3757,7 @@ puts flights_sm.head
3298
3757
  ```
3299
3758
 
3300
3759
  ```
3301
- ## # A tibble: 6 x 7
3760
+ ## # A tibble: 6 × 7
3302
3761
  ## year month day dep_delay arr_delay distance air_time
3303
3762
  ## <int> <int> <int> <dbl> <dbl> <dbl> <dbl>
3304
3763
  ## 1 2013 1 1 2 11 1400 227
@@ -3310,15 +3769,15 @@ puts flights_sm.head
3310
3769
  ```
3311
3770
 
3312
3771
 
3313
- ```ruby
3772
+ ``` ruby
3314
3773
  flights_sm = flights_sm.
3315
- mutate(gain: :dep_delay - :arr_delay,
3316
- speed: :distance / :air_time * 60)
3774
+ mutate(gain: R[:dep_delay] - R[:arr_delay],
3775
+ speed: R[:distance] / R[:air_time] * 60)
3317
3776
  puts flights_sm.head
3318
3777
  ```
3319
3778
 
3320
3779
  ```
3321
- ## # A tibble: 6 x 9
3780
+ ## # A tibble: 6 × 9
3322
3781
  ## year month day dep_delay arr_delay distance air_time gain speed
3323
3782
  ## <int> <int> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
3324
3783
  ## 1 2013 1 1 2 11 1400 227 -9 370.
@@ -3335,30 +3794,30 @@ Function 'summarise' calculates summaries for the data frame. When no 'group_by'
3335
3794
  a single value is obtained from the data frame:
3336
3795
 
3337
3796
 
3338
- ```ruby
3797
+ ``` ruby
3339
3798
  puts flights.summarise(delay: E.mean(:dep_delay, na__rm: true))
3340
3799
  ```
3341
3800
 
3342
3801
  ```
3343
- ## # A tibble: 1 x 1
3802
+ ## # A tibble: 1 × 1
3344
3803
  ## delay
3345
3804
  ## <dbl>
3346
3805
  ## 1 12.6
3347
3806
  ```
3348
3807
 
3349
- When a data frame is groupe with 'group_by' summaries apply to the given group:
3808
+ When a data frame is grouped with 'group_by' summaries apply to the given group:
3350
3809
 
3351
3810
 
3352
- ```ruby
3811
+ ``` ruby
3353
3812
  by_day = flights.group_by(:year, :month, :day)
3354
- puts by_day.summarise(delay: :dep_delay.mean(na__rm: true)).head
3813
+ puts by_day.summarise(delay: R[:dep_delay].mean(na__rm: true)).head
3355
3814
  ```
3356
3815
 
3357
3816
  ```
3358
- ## # A tibble: 6 x 4
3817
+ ## # A tibble: 6 × 4
3359
3818
  ## # Groups: year, month [1]
3360
3819
  ## year month day delay
3361
- ## * <int> <int> <int> <dbl>
3820
+ ## <int> <int> <int> <dbl>
3362
3821
  ## 1 2013 1 1 11.5
3363
3822
  ## 2 2013 1 2 13.9
3364
3823
  ## 3 2013 1 3 11.0
@@ -3370,20 +3829,20 @@ puts by_day.summarise(delay: :dep_delay.mean(na__rm: true)).head
3370
3829
  Next we put many operations together by pipping them one after the other:
3371
3830
 
3372
3831
 
3373
- ```ruby
3832
+ ``` ruby
3374
3833
  delays = flights.
3375
3834
  group_by(:dest).
3376
3835
  summarise(
3377
3836
  count: E.n,
3378
- dist: :distance.mean(na__rm: true),
3379
- delay: :arr_delay.mean(na__rm: true)).
3380
- filter(:count > 20, :dest != "NHL")
3837
+ dist: R[:distance].mean(na__rm: true),
3838
+ delay: R[:arr_delay].mean(na__rm: true)).
3839
+ filter(R[:count] > 20, R[:dest] != "NHL")
3381
3840
 
3382
3841
  puts delays.head
3383
3842
  ```
3384
3843
 
3385
3844
  ```
3386
- ## # A tibble: 6 x 4
3845
+ ## # A tibble: 6 × 4
3387
3846
  ## dest count dist delay
3388
3847
  ## <chr> <int> <dbl> <dbl>
3389
3848
  ## 1 ABQ 254 1826 4.38
@@ -3396,48 +3855,67 @@ puts delays.head
3396
3855
 
3397
3856
  # Using Data Table
3398
3857
 
3858
+ The next chunk converts the **nycflights13** `flights` tibble already loaded above into a
3859
+ **`data.table`**. That keeps the manual offline and avoids downloading a remote CSV during gknit
3860
+ (network stalls look like bridge hangs when the transfer runs inside a single R eval).
3399
3861
 
3400
- ```ruby
3862
+
3863
+ ``` ruby
3401
3864
  R.library('data.table')
3402
- R.install_and_loads('curl')
3403
3865
 
3404
- input = "https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv"
3405
- flights = R.fread(input)
3406
- puts flights
3866
+ flights = R.as__data__table(~R[:flights])
3407
3867
  puts flights.dim
3408
- ```
3409
-
3410
- ```
3411
- ## year month day dep_delay arr_delay carrier origin dest air_time
3412
- ## 1: 2014 1 1 14 13 AA JFK LAX 359
3413
- ## 2: 2014 1 1 -3 13 AA JFK LAX 363
3414
- ## 3: 2014 1 1 2 9 AA JFK LAX 351
3415
- ## 4: 2014 1 1 -8 -26 AA LGA PBI 157
3416
- ## 5: 2014 1 1 2 1 AA JFK LAX 350
3417
- ## ---
3418
- ## 253312: 2014 10 31 1 -30 UA LGA IAH 201
3419
- ## 253313: 2014 10 31 -5 -14 UA EWR IAH 189
3420
- ## 253314: 2014 10 31 -8 16 MQ LGA RDU 83
3421
- ## 253315: 2014 10 31 -4 15 MQ LGA DTW 75
3422
- ## 253316: 2014 10 31 -5 1 MQ LGA SDF 110
3423
- ## distance hour
3424
- ## 1: 2475 9
3425
- ## 2: 2475 11
3426
- ## 3: 2475 19
3427
- ## 4: 1035 7
3428
- ## 5: 2475 13
3429
- ## ---
3430
- ## 253312: 1416 14
3431
- ## 253313: 1400 8
3432
- ## 253314: 431 11
3433
- ## 253315: 502 11
3434
- ## 253316: 659 8
3435
- ## [1] 253316 11
3436
- ```
3437
-
3438
-
3439
- ```ruby
3440
-
3868
+ puts R.head(flights, 12)
3869
+ ```
3870
+
3871
+ ```
3872
+ ## [1] 336776 19
3873
+ ## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
3874
+ ## <int> <int> <int> <int> <int> <num> <int> <int>
3875
+ ## 1: 2013 1 1 517 515 2 830 819
3876
+ ## 2: 2013 1 1 533 529 4 850 830
3877
+ ## 3: 2013 1 1 542 540 2 923 850
3878
+ ## 4: 2013 1 1 544 545 -1 1004 1022
3879
+ ## 5: 2013 1 1 554 600 -6 812 837
3880
+ ## 6: 2013 1 1 554 558 -4 740 728
3881
+ ## 7: 2013 1 1 555 600 -5 913 854
3882
+ ## 8: 2013 1 1 557 600 -3 709 723
3883
+ ## 9: 2013 1 1 557 600 -3 838 846
3884
+ ## 10: 2013 1 1 558 600 -2 753 745
3885
+ ## 11: 2013 1 1 558 600 -2 849 851
3886
+ ## 12: 2013 1 1 558 600 -2 853 856
3887
+ ## arr_delay carrier flight tailnum origin dest air_time distance hour
3888
+ ## <num> <char> <int> <char> <char> <char> <num> <num> <num>
3889
+ ## 1: 11 UA 1545 N14228 EWR IAH 227 1400 5
3890
+ ## 2: 20 UA 1714 N24211 LGA IAH 227 1416 5
3891
+ ## 3: 33 AA 1141 N619AA JFK MIA 160 1089 5
3892
+ ## 4: -18 B6 725 N804JB JFK BQN 183 1576 5
3893
+ ## 5: -25 DL 461 N668DN LGA ATL 116 762 6
3894
+ ## 6: 12 UA 1696 N39463 EWR ORD 150 719 5
3895
+ ## 7: 19 B6 507 N516JB EWR FLL 158 1065 6
3896
+ ## 8: -14 EV 5708 N829AS LGA IAD 53 229 6
3897
+ ## 9: -8 B6 79 N593JB JFK MCO 140 944 6
3898
+ ## 10: 8 AA 301 N3ALAA LGA ORD 138 733 6
3899
+ ## 11: -2 B6 49 N793JB JFK PBI 149 1028 6
3900
+ ## 12: -3 B6 71 N657JB JFK TPA 158 1005 6
3901
+ ## minute time_hour
3902
+ ## <num> <POSc>
3903
+ ## 1: 15 2013-01-01 05:00:00
3904
+ ## 2: 29 2013-01-01 05:00:00
3905
+ ## 3: 40 2013-01-01 05:00:00
3906
+ ## 4: 45 2013-01-01 05:00:00
3907
+ ## 5: 0 2013-01-01 06:00:00
3908
+ ## 6: 58 2013-01-01 05:00:00
3909
+ ## 7: 0 2013-01-01 06:00:00
3910
+ ## 8: 0 2013-01-01 06:00:00
3911
+ ## 9: 0 2013-01-01 06:00:00
3912
+ ## 10: 0 2013-01-01 06:00:00
3913
+ ## 11: 0 2013-01-01 06:00:00
3914
+ ## 12: 0 2013-01-01 06:00:00
3915
+ ```
3916
+
3917
+
3918
+ ``` ruby
3441
3919
  data_table = R.data__table(
3442
3920
  ID: R.c("b","b","b","a","a","c"),
3443
3921
  a: (1..6),
@@ -3450,20 +3928,21 @@ puts data_table.ID
3450
3928
  ```
3451
3929
 
3452
3930
  ```
3453
- ## ID a b c
3454
- ## 1: b 1 7 13
3455
- ## 2: b 2 8 14
3456
- ## 3: b 3 9 15
3457
- ## 4: a 4 10 16
3458
- ## 5: a 5 11 17
3459
- ## 6: c 6 12 18
3931
+ ## ID a b c
3932
+ ## <char> <int> <int> <int>
3933
+ ## 1: b 1 7 13
3934
+ ## 2: b 2 8 14
3935
+ ## 3: b 3 9 15
3936
+ ## 4: a 4 10 16
3937
+ ## 5: a 5 11 17
3938
+ ## 6: c 6 12 18
3460
3939
  ## [1] "b" "b" "b" "a" "a" "c"
3461
3940
  ```
3462
3941
 
3463
3942
 
3464
- ```ruby
3943
+ ``` ruby
3465
3944
  # subset rows in i
3466
- ans = flights[(:origin.eq "JFK") & (:month.eq 6)]
3945
+ ans = flights[(R[:origin].eq "JFK") & (R[:month].eq 6)]
3467
3946
  puts ans.head
3468
3947
 
3469
3948
  # Get the first two rows from flights.
@@ -3471,98 +3950,350 @@ puts ans.head
3471
3950
  ans = flights[(1..2)]
3472
3951
  puts ans
3473
3952
 
3474
- # Sort flights first by column origin in ascending order, and then by dest in descending order:
3475
-
3953
+ # Sort by origin asc, then dest desc (example kept commented):
3476
3954
  # ans = flights[E.order(:origin, -(:dest))]
3477
3955
  # puts ans.head
3478
3956
  ```
3479
3957
 
3480
3958
  ```
3481
- ## year month day dep_delay arr_delay carrier origin dest air_time
3482
- ## 1: 2014 6 1 -9 -5 AA JFK LAX 324
3483
- ## 2: 2014 6 1 -10 -13 AA JFK LAX 329
3484
- ## 3: 2014 6 1 18 -1 AA JFK LAX 326
3485
- ## 4: 2014 6 1 -6 -16 AA JFK LAX 320
3486
- ## 5: 2014 6 1 -4 -45 AA JFK LAX 326
3487
- ## 6: 2014 6 1 -6 -23 AA JFK LAX 329
3488
- ## distance hour
3489
- ## 1: 2475 8
3490
- ## 2: 2475 12
3491
- ## 3: 2475 7
3492
- ## 4: 2475 10
3493
- ## 5: 2475 18
3494
- ## 6: 2475 14
3495
- ## year month day dep_delay arr_delay carrier origin dest air_time
3496
- ## 1: 2014 1 1 14 13 AA JFK LAX 359
3497
- ## 2: 2014 1 1 -3 13 AA JFK LAX 363
3498
- ## distance hour
3499
- ## 1: 2475 9
3500
- ## 2: 2475 11
3501
- ```
3502
-
3503
-
3504
- ```ruby
3959
+ ## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
3960
+ ## <int> <int> <int> <int> <int> <num> <int> <int>
3961
+ ## 1: 2013 6 1 2 2359 3 341 350
3962
+ ## 2: 2013 6 1 538 545 -7 925 922
3963
+ ## 3: 2013 6 1 539 540 -1 832 840
3964
+ ## 4: 2013 6 1 553 600 -7 700 711
3965
+ ## 5: 2013 6 1 554 600 -6 851 908
3966
+ ## 6: 2013 6 1 557 600 -3 934 942
3967
+ ## arr_delay carrier flight tailnum origin dest air_time distance hour
3968
+ ## <num> <char> <int> <char> <char> <char> <num> <num> <num>
3969
+ ## 1: -9 B6 739 N618JB JFK PSE 200 1617 23
3970
+ ## 2: 3 B6 725 N806JB JFK BQN 203 1576 5
3971
+ ## 3: -8 AA 701 N5EAAA JFK MIA 140 1089 5
3972
+ ## 4: -11 EV 5716 N835AS JFK IAD 42 228 6
3973
+ ## 5: -17 UA 1159 N33132 JFK LAX 330 2475 6
3974
+ ## 6: -8 B6 715 N766JB JFK SJU 198 1598 6
3975
+ ## minute time_hour
3976
+ ## <num> <POSc>
3977
+ ## 1: 59 2013-06-01 23:00:00
3978
+ ## 2: 45 2013-06-01 05:00:00
3979
+ ## 3: 40 2013-06-01 05:00:00
3980
+ ## 4: 0 2013-06-01 06:00:00
3981
+ ## 5: 0 2013-06-01 06:00:00
3982
+ ## 6: 0 2013-06-01 06:00:00
3983
+ ## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
3984
+ ## <int> <int> <int> <int> <int> <num> <int> <int>
3985
+ ## 1: 2013 1 1 517 515 2 830 819
3986
+ ## 2: 2013 1 1 533 529 4 850 830
3987
+ ## arr_delay carrier flight tailnum origin dest air_time distance hour
3988
+ ## <num> <char> <int> <char> <char> <char> <num> <num> <num>
3989
+ ## 1: 11 UA 1545 N14228 EWR IAH 227 1400 5
3990
+ ## 2: 20 UA 1714 N24211 LGA IAH 227 1416 5
3991
+ ## minute time_hour
3992
+ ## <num> <POSc>
3993
+ ## 1: 15 2013-01-01 05:00:00
3994
+ ## 2: 29 2013-01-01 05:00:00
3995
+ ```
3996
+
3997
+
3998
+ ``` ruby
3505
3999
  # Select column(s) in j
3506
4000
  # select arr_delay column, but return it as a vector.
3507
4001
 
3508
4002
  ans = flights[:all, :arr_delay]
3509
4003
  puts ans.head
3510
4004
 
3511
- # Select arr_delay column, but return as a data.table instead.
4005
+ # arr_delay as data.table (not plain vector).
3512
4006
 
3513
- ans = flights[:all, :arr_delay.list]
4007
+ ans = flights[:all, R[:arr_delay].list]
3514
4008
  puts ans.head
3515
4009
 
3516
- ans = flights[:all, E.list(:arr_delay, :dep_delay)]
4010
+ ans = flights[:all, E.list(R[:arr_delay], R[:dep_delay])]
3517
4011
  ```
3518
4012
 
3519
4013
  ```
3520
- ## [1] 13 13 9 -26 1 0
4014
+ ## [1] 11 20 33 -18 -25 12
3521
4015
  ## arr_delay
3522
- ## 1: 13
3523
- ## 2: 13
3524
- ## 3: 9
3525
- ## 4: -26
3526
- ## 5: 1
3527
- ## 6: 0
4016
+ ## <num>
4017
+ ## 1: 11
4018
+ ## 2: 20
4019
+ ## 3: 33
4020
+ ## 4: -18
4021
+ ## 5: -25
4022
+ ## 6: 12
4023
+ ```
4024
+
4025
+ # Apache Arrow
4026
+
4027
+ [Apache Arrow](https://arrow.apache.org/) is a **columnar** in-memory format used heavily in R
4028
+ and Python for analytics. In Galaaz, **Ruby does not hold an Arrow C++ table itself**; instead you
4029
+ build ordinary Ruby structures (arrays of row hashes), and **`R::Arrow.from_ruby_batches`** creates
4030
+ a real **Arrow `Table` inside GNU R**. From there you use R’s **`arrow`** and **`dplyr`** packages
4031
+ as usual: **`group_by`** on the Arrow table, **`summarise`** for aggregates, then **`collect()`** to
4032
+ materialize a tibble when you need in-memory R rows.
4033
+
4034
+ That pattern matches production use: **JRuby threads** (or sequential code) assemble many rows in
4035
+ Ruby; you pay **one** bridge-heavy handoff to R; **dplyr** runs vectorised work on the Arrow table
4036
+ in R.
4037
+
4038
+ **Prerequisites:** install R packages **`arrow`** and **`dplyr`**. Run scripts with
4039
+ **`bin/galaaz-jruby`** (or the same JVM flags as in **`docs/testing.md`**) so the Arrow JNI stack is
4040
+ available.
4041
+
4042
+ ## Other `R::Arrow` helpers
4043
+
4044
+ The Ruby module **`R::Arrow`** (see `lib/R_interface/r_arrow.rb`) also includes:
4045
+
4046
+ * **`R::Arrow.table_from(df)`** — wrap an R `data.frame` / tibble as an Arrow table.
4047
+ * **`R::Arrow.read_feather` / `write_feather`**, **`read_parquet`**, **`dataset(path)`** — file and
4048
+ dataset IO on paths visible to R.
4049
+
4050
+ ## Example: many Ruby rows → Arrow in R → grouped statistics
4051
+
4052
+ The repository test **`slow-specs/arrow_large_pipeline_spec.rb`** builds **200k rows** in parallel
4053
+ (eight threads × 25,000 rows), pushes them through **`R::Arrow.from_ruby_batches`**, then checks that
4054
+ **dplyr** group summaries match a Ruby reference calculation. The same logic appears below at a
4055
+ **smaller scale** so this manual can knit quickly; increase `thread_count` and `rows_per_thread`
4056
+ when experimenting locally.
4057
+
4058
+
4059
+ ``` ruby
4060
+ # Scaled-down version of slow-specs/arrow_large_pipeline_spec.rb.
4061
+ unless R::Support.eval("requireNamespace('arrow', quietly=TRUE) && requireNamespace('dplyr', quietly=TRUE)") == true
4062
+ puts '(Skip: need arrow + dplyr in R; use bin/galaaz-jruby outside gKnit.)'
4063
+ else
4064
+ thread_count = 4
4065
+ rows_per_thread = 500
4066
+ group_count = 5
4067
+
4068
+ batches = []
4069
+ mutex = Mutex.new
4070
+ threads = []
4071
+
4072
+ thread_count.times do |tid|
4073
+ threads << Thread.new do
4074
+ start = tid * rows_per_thread
4075
+ local = (start...(start + rows_per_thread)).map do |i|
4076
+ {
4077
+ id: i,
4078
+ grp: "g#{i % group_count}",
4079
+ value: (i % 17) + 1,
4080
+ weight: ((i % 5) + 1) * 0.5
4081
+ }
4082
+ end
4083
+ mutex.synchronize { batches << local }
4084
+ end
4085
+ end
4086
+ threads.each(&:join)
4087
+
4088
+ tbl = R::Arrow.from_ruby_batches(batches)
4089
+ puts "R class after from_ruby_batches: #{tbl.rclass}"
4090
+
4091
+ grouped = R.dplyr___group_by(tbl, :grp)
4092
+ summarised = R.dplyr___summarise(
4093
+ grouped,
4094
+ n: E.n(),
4095
+ total: E.sum(:value),
4096
+ wsum: E.sum(R[:value] * R[:weight])
4097
+ )
4098
+ out = R.dplyr___collect(summarised)
4099
+
4100
+ puts 'Per-group summary (first rows):'
4101
+ puts R.as__data__frame(out).head(10)
4102
+
4103
+ total_n = 0
4104
+ (1..(out.nrow >> 0)).each { |i| total_n += (out[['n']][i] >> 0) }
4105
+ puts "Sum of group counts n (should equal #{thread_count * rows_per_thread}): #{total_n}"
4106
+ end
4107
+ ```
4108
+
4109
+ ```
4110
+ ## R class after from_ruby_batches: Table
4111
+ ## Per-group summary (first rows):
4112
+ ## grp n total wsum
4113
+ ## 1 g0 400 3589 1794.5
4114
+ ## 2 g1 400 3598 3598.0
4115
+ ## 3 g2 400 3590 5385.0
4116
+ ## 4 g3 400 3599 7198.0
4117
+ ## 5 g4 400 3591 8977.5
4118
+ ## Sum of group counts n (should equal 2000): 2000
4119
+ ```
4120
+
4121
+ **What to notice:** (1) Ruby only sees **`Hash`** rows and Ruby **`Thread`** objects; (2) a single
4122
+ **`from_ruby_batches`** call creates the Arrow table in R; (3) **`dplyr___group_by`** /
4123
+ **`dplyr___summarise`** / **`dplyr___collect`** mirror **`dplyr::group_by`** /
4124
+ **`dplyr::summarise`** / **`dplyr::collect`** on an Arrow-backed table. For a lighter test, see
4125
+ **`specs/arrow_from_ruby_batches_spec.rb`**; for the full-size benchmark, run
4126
+ **`bin/run_slow_rspec slow-specs/arrow_large_pipeline_spec.rb`**.
4127
+
4128
+ # Bioconductor and DESeq2
4129
+
4130
+ **Bioconductor** packages are ordinary R packages installed from the Bioconductor repositories.
4131
+ Galaaz does not treat them specially: once installed in **GNU R**, you load them with
4132
+ **`R.library`** like any CRAN package.
4133
+
4134
+ ## Installing Bioconductor packages
4135
+
4136
+ From an R session (or `R -e '...'`), use **BiocManager** (see
4137
+ [bioconductor.org](https://bioconductor.org/install/)):
4138
+
4139
+ ```r
4140
+ if (!requireNamespace("BiocManager", quietly = TRUE))
4141
+ install.packages("BiocManager")
4142
+ BiocManager::install(c("DESeq2", "airway"))
4143
+ ```
4144
+
4145
+ The **`airway`** package ships the example **`SummarizedExperiment`** used below. **DESeq2**
4146
+ pulls in several dependencies; the first install can take several minutes.
4147
+
4148
+ ## Example: DESeq2 on the airway dataset
4149
+
4150
+ The script **`examples/bioconductor_deseq2_airway/deseq2_airway_galaaz.rb`** is the canonical
4151
+ version in the repository. Run it from the **Galaaz repository root** with JRuby, for example:
4152
+
4153
+ ```text
4154
+ bin/galaaz-jruby examples/bioconductor_deseq2_airway/deseq2_airway_galaaz.rb
4155
+ ```
4156
+
4157
+ The workflow in Ruby mirrors a standard DESeq2 vignette:
4158
+
4159
+ 1. **`R.library('DESeq2')`** and **`R.library('airway')`**, then **`R.data('airway')`** so the
4160
+ object exists in R’s global environment.
4161
+ 2. **`airway = ~R[:airway]`** pulls the experiment into a Galaaz wrapper so you can pass it to R
4162
+ functions as a Ruby value.
4163
+ 3. **`R.DESeqDataSet(..., design: (R[:all].til R[:cell] + R[:dex]))`** builds the **`DESeqDataSet`**. The
4164
+ **`(R[:all].til R[:cell] + R[:dex])`** form is Galaaz’s way of passing the one-sided formula
4165
+ **`~ cell + dex`** (adjust for the design you need).
4166
+ 4. Prefilter rows with almost no counts: **`keep = R.rowSums(R.counts(dds)) >= 10`** and
4167
+ **`dds = dds[keep, :all]`**.
4168
+ 5. **`dds = R.DESeq(dds)`** fits the model; **`res = R.results(dds, contrast: R.c('dex', 'trt', 'untrt'))`**
4169
+ extracts the treatment contrast (adjust **`contrast`** for your experiment).
4170
+ 6. Summaries use normal Ruby string interpolation on **`R.nrow`**, **`R.ncol`**, **`R.colnames`**, etc.
4171
+ 7. **`R.pdf(...); R.plotMA(res, ...); R.dev__off`** writes DESeq2’s MA plot (path is relative to the
4172
+ process working directory—use the repo root when running the bundled script).
4173
+
4174
+ Related benchmarks and warm-run notes live under **`docs/deseq2_airway_benchmark.md`** and
4175
+ **`examples/bioconductor_deseq2_airway/bench_*.rb`**.
4176
+
4177
+ Below is the full listing (same as the file in the repository). It is **not** executed while this
4178
+ manual is knitted, because **DESeq2** is heavy and may be absent on the build machine.
4179
+
4180
+
4181
+ ``` ruby
4182
+ # Canonical script: examples/bioconductor_deseq2_airway/deseq2_airway_galaaz.rb
4183
+ # Run: bin/galaaz-jruby examples/.../deseq2_airway_galaaz.rb (repo root).
4184
+
4185
+ require 'galaaz'
4186
+
4187
+ R.library('DESeq2')
4188
+ R.library('airway')
4189
+ R.data('airway')
4190
+
4191
+ airway = ~R[:airway]
4192
+
4193
+ # Build DESeq2 dataset with one-sided formula: ~ cell + dex.
4194
+ dds = R.DESeqDataSet(airway, design: (R[:all].til R[:cell] + R[:dex]))
4195
+
4196
+ # Prefilter genes with almost no counts.
4197
+ keep = R.rowSums(R.counts(dds)) >= 10
4198
+ dds = dds[keep, :all]
4199
+
4200
+ # Fit DE model and extract treatment effect.
4201
+ dds = R.DESeq(dds)
4202
+ res = R.results(dds, contrast: R.c('dex', 'trt', 'untrt'))
4203
+
4204
+ # Compact sanity outputs for quick verification.
4205
+ puts "Samples: #{R.ncol(dds)}"
4206
+ puts "Genes after prefilter: #{R.nrow(dds)}"
4207
+ puts "Result rows: #{R.nrow(res)}"
4208
+ puts "Result columns: #{R.colnames(res)}"
4209
+ puts "Significant genes (padj < 0.05): #{R.sum(res.padj < 0.05, na__rm: true)}"
4210
+
4211
+ res_ordered = res[R.order(res.padj), :all]
4212
+ puts R.head(R.as__data__frame(res_ordered), 10)
4213
+
4214
+ # Standard DESeq2 plot call written to file.
4215
+ R.pdf('examples/bioconductor_deseq2_airway/plotMA_galaaz.pdf')
4216
+ R.plotMA(res, ylim: R.c(-5, 5))
4217
+ R.dev__off
4218
+ ```
4219
+
4220
+ If **DESeq2** and **airway** are installed, the next chunk loads the data and prints a short
4221
+ preview (it does **not** run **`DESeq`** so the manual knits quickly).
4222
+
4223
+
4224
+ ``` ruby
4225
+ unless R::Support.eval("requireNamespace('DESeq2', quietly=TRUE) && requireNamespace('airway', quietly=TRUE)")
4226
+ puts '(Skip: install DESeq2 and airway via BiocManager in R to run the full example.)'
4227
+ else
4228
+ R.library('DESeq2')
4229
+ R.library('airway')
4230
+ R.data('airway')
4231
+ airway = ~R[:airway]
4232
+ puts 'airway object (head of assay / dims via R):'
4233
+ puts "ncol(samples): #{R.ncol(airway)}"
4234
+ puts R.head(R.assay(airway), 3)
4235
+ end
4236
+ ```
4237
+
4238
+ ```
4239
+ ## airway object (head of assay / dims via R):
4240
+ ## ncol(samples): [1] 8
4241
+ ## SRR1039508 SRR1039509 SRR1039512 SRR1039513 SRR1039516
4242
+ ## ENSG00000000003 679 448 873 408 1138
4243
+ ## ENSG00000000005 0 0 0 0 0
4244
+ ## ENSG00000000419 467 515 621 365 587
4245
+ ## SRR1039517 SRR1039520 SRR1039521
4246
+ ## ENSG00000000003 1047 770 572
4247
+ ## ENSG00000000005 0 0 0
4248
+ ## ENSG00000000419 799 417 508
3528
4249
  ```
3529
4250
 
4251
+ # Performance
4252
+
4253
+ For realistic analyses, **most wall-clock time is spent inside GNU R** (model fitting, I/O inside
4254
+ R, graphics). The Galaaz **bridge** adds overhead mainly from **starting a session**, **serializing
4255
+ requests**, and **wrapping results** in Ruby objects—not from reimplementing R’s numerical work.
4256
+
4257
+ Practical tips:
4258
+
4259
+ * Keep **hot loops** in R or vectorized code when possible; use Ruby for orchestration, I/O, and
4260
+ glue.
4261
+ * **Reuse one process**: running many short scripts cold-starts Ruby, the JVM, and R each time;
4262
+ a long-lived process or repeated calls in one run amortize setup (see benchmarks below).
4263
+ * **Batch data**: merge shards in Ruby, then call **`R::Arrow.from_ruby_batches`** (or build one
4264
+ data frame) instead of millions of tiny R calls.
4265
+
4266
+ For measured discussion (including DESeq2-style workloads and warm comparisons), see
4267
+ **`docs/performance.md`** and **`docs/deseq2_airway_benchmark.md`** in the Galaaz repository.
4268
+
3530
4269
  # Graphics in Galaaz
3531
4270
 
3532
4271
  Creating graphics in Galaaz is quite easy, as it can use all the power of ggplot2. There are
3533
- many resources in the web that teaches ggplot, so here we give a quick example of ggplot
4272
+ many resources on the web that teach ggplot, so here we give a quick example of ggplot
3534
4273
  integration with Ruby. We continue to use the :mtcars dataset and we will plot a diverging
3535
- bar plot, showing cars that have 'above' or 'below' gas consuption. Let's first prepare
4274
+ bar plot, showing cars that have 'above' or 'below' gas consumption. Let's first prepare
3536
4275
  the data frame with the necessary data:
3537
4276
 
3538
4277
 
3539
- ```ruby
3540
- # copy the R variable :mtcars to the Ruby mtcars variable
3541
- mtcars = ~:mtcars
3542
-
3543
- # create a new column 'car_name' to store the car names so that it can be
3544
- # used for plotting. The 'rownames' of the data frame cannot be used as
3545
- # data for plotting
3546
- mtcars.car_name = R.rownames(:mtcars)
3547
-
3548
- # compute normalized mpg and add it to a new column called mpg_z
3549
- # Note that the mean value for mpg can be obtained by calling the 'mean'
3550
- # function on the vector 'mtcars.mpg'. The same with the standard
3551
- # deviation 'sd'. The vector is then rounded to two digits with 'round 2'
4278
+ ``` ruby
4279
+ # :mtcars -> Ruby handle
4280
+ mtcars = ~R[:mtcars]
4281
+
4282
+ # Row labels are not a plot column; copy them to car_name.
4283
+ mtcars.car_name = R.rownames(R[:mtcars])
4284
+
4285
+ # Z-score mpg (mean/sd on mtcars.mpg); round to 2 decimals.
3552
4286
  mtcars.mpg_z = ((mtcars.mpg - mtcars.mpg.mean)/mtcars.mpg.sd).round 2
3553
4287
 
3554
- # create a new column 'mpg_type'. Function 'ifelse' is a vectorized function
3555
- # that looks at every element of the mpg_z vector and if the value is below
3556
- # 0, returns 'below', otherwise returns 'above'
4288
+ # ifelse is vectorized: below / above average mpg_z.
3557
4289
  mtcars.mpg_type = (mtcars.mpg_z < 0).ifelse("below", "above")
3558
4290
 
3559
- # order the mtcar data set by the mpg_z vector from smaler to larger values
4291
+ # Sort rows by mpg_z.
3560
4292
  mtcars = mtcars[mtcars.mpg_z.order, :all]
3561
4293
 
3562
- # convert the car_name column to a factor to retain sorted order in plot
4294
+ # Factor car_name so plot order follows sort.
3563
4295
  mtcars.car_name = mtcars.car_name.factor levels: mtcars.car_name
3564
4296
 
3565
- # let's look at the final data frame
3566
4297
  puts mtcars.head
3567
4298
  ```
3568
4299
 
@@ -3582,24 +4313,24 @@ puts mtcars.head
3582
4313
  ## Chrysler Imperial Chrysler Imperial -0.89 below
3583
4314
  ## Maserati Bora Maserati Bora -0.84 below
3584
4315
  ```
3585
- Now, lets plot the diverging bar plot. When using gKnit, there is no need to call
3586
- 'R.awt' to create a plotting device, since gKnit does take care of it. Galaaz
4316
+ Now, let's plot the diverging bar plot. When using gKnit, you normally do **not** need to open a
4317
+ graphics device manually; gKnit arranges the figure device for chunk output. Galaaz
3587
4318
  provides integration with ggplot. The interested reader should check online for more
3588
4319
  information on ggplot, since it is outside the scope of this manual describing
3589
- how ggplot works. We give here but a brief description on how this plot is generated.
4320
+ how ggplot works. Here we give only a brief description of how this plot is generated.
3590
4321
 
3591
- ggplot implements the 'grammar of graphics'. In this approach, plots are build by
4322
+ ggplot implements the 'grammar of graphics'. In this approach, plots are built by
3592
4323
  adding layers to the plot. On the first layer we describe what we want on the 'x'
3593
4324
  and 'y' axis of the plot. In this case, we have 'car_name' on the 'x' axis and
3594
4325
  'mpg\_z' on the 'y' axis. Then the type of graph is specified by adding
3595
4326
  'geom\_bar' (for a bar graph). We specify that our bars should be filled using
3596
- 'mpg\_type', which is either 'above' or 'bellow' giving then two colours for
4327
+ 'mpg\_type', which is either 'above' or 'below' giving then two colours for
3597
4328
  filling. On the next layer we specify the labels for the graph, then we add the
3598
4329
  title and subtitle. Finally, in a bar chart usually bars go on the vertical direction,
3599
- but in this graph we want the bars to be horizontally layed so we add 'coord\_flip'.
4330
+ but in this graph we want the bars to be horizontally laid so we add 'coord\_flip'.
3600
4331
 
3601
4332
 
3602
- ```ruby
4333
+ ``` ruby
3603
4334
  require 'ggplot'
3604
4335
 
3605
4336
  puts mtcars.ggplot(E.aes(x: :car_name, y: :mpg_z, label: :mpg_z)) +
@@ -3613,16 +4344,16 @@ puts mtcars.ggplot(E.aes(x: :car_name, y: :mpg_z, label: :mpg_z)) +
3613
4344
  ```
3614
4345
 
3615
4346
 
3616
- ![](manual_files/figure-html/diverging_bar.png)<!-- -->
4347
+ ![](manual_files/figure-html/diverging_bar.png)
3617
4348
 
3618
4349
  # Coding with Tidyverse
3619
4350
 
3620
4351
  In R, and when coding with 'tidyverse', arguments to a function are usually not
3621
- *referencially transparent*. That is, you can’t replace a value with a seemingly equivalent
4352
+ *referentially transparent*. That is, you can’t replace a value with a seemingly equivalent
3622
4353
  object that you’ve defined elsewhere. To see the problem, let's first define a data frame:
3623
4354
 
3624
4355
 
3625
- ```ruby
4356
+ ``` ruby
3626
4357
  df = R.data__frame(x: (1..3), y: (3..1))
3627
4358
  puts df
3628
4359
  ```
@@ -3637,21 +4368,21 @@ puts df
3637
4368
  and now, let's look at this code:
3638
4369
 
3639
4370
 
3640
- ```r
4371
+ ``` r
3641
4372
  my_var <- x
3642
4373
  filter(df, my_var == 1)
3643
4374
  ```
3644
4375
  It generates the following error: "object 'x' not found.
3645
4376
 
3646
- However, in Galaaz, arguments are referencially transparent as can be seen by the
3647
- code bellow. Note initally that 'my_var = :x' will not give the error "object 'x' not found"
4377
+ However, in Galaaz, arguments are referentially transparent as can be seen by the
4378
+ code below. Note initially that 'my_var = R[:x]' will not give the error "object 'x' not found"
3648
4379
  since ':x' is treated as an expression and assigned to my\_var. Then when doing (my\_var.eq 1),
3649
- my\_var is a variable that resolves to ':x' and it becomes equivalent to (:x.eq 1) which is
4380
+ my\_var is a variable that resolves to ':x' and it becomes equivalent to (R[:x].eq 1) which is
3650
4381
  what we want.
3651
4382
 
3652
4383
 
3653
- ```ruby
3654
- my_var = :x
4384
+ ``` ruby
4385
+ my_var = R[:x]
3655
4386
  puts df.filter(my_var.eq 1)
3656
4387
  ```
3657
4388
 
@@ -3659,7 +4390,7 @@ puts df.filter(my_var.eq 1)
3659
4390
  ## x y
3660
4391
  ## 1 1 3
3661
4392
  ```
3662
- As stated by Hardley
4393
+ As stated by Hadley
3663
4394
 
3664
4395
  > dplyr code is ambiguous. Depending on what variables are defined where,
3665
4396
  > filter(df, x == y) could be equivalent to any of:
@@ -3671,18 +4402,18 @@ df[x == df$y, ]
3671
4402
  df[x == y, ]
3672
4403
  ```
3673
4404
  In galaaz this ambiguity does not exist, filter(df, x.eq y) is not a valid expression as
3674
- expressions are build with symbols. In doing filter(df, :x.eq y) we are looking for elements
4405
+ expressions are build with symbols. In doing filter(df, R[:x].eq y) we are looking for elements
3675
4406
  of the 'x' column that are equal to a previously defined y variable. Finally in
3676
- filter(df, :x.eq :y) we are looking for elements in which the 'x' column value is equal to
4407
+ filter(df, R[:x].eq R[:y]) we are looking for elements in which the 'x' column value is equal to
3677
4408
  the 'y' column value. This can be seen in the following two chunks of code:
3678
4409
 
3679
4410
 
3680
- ```ruby
4411
+ ``` ruby
3681
4412
  y = 1
3682
4413
  x = 2
3683
4414
 
3684
4415
  # looking for values where the 'x' column is equal to the 'y' column
3685
- puts df.filter(:x.eq :y)
4416
+ puts df.filter(R[:x].eq R[:y])
3686
4417
  ```
3687
4418
 
3688
4419
  ```
@@ -3691,10 +4422,10 @@ puts df.filter(:x.eq :y)
3691
4422
  ```
3692
4423
 
3693
4424
 
3694
- ```ruby
4425
+ ``` ruby
3695
4426
  # looking for values where the 'x' column is equal to the 'y' variable
3696
4427
  # in this case, the number 1
3697
- puts df.filter(:x.eq y)
4428
+ puts df.filter(R[:x].eq y)
3698
4429
  ```
3699
4430
 
3700
4431
  ```
@@ -3726,18 +4457,19 @@ Unfortunately, in R, this function can fail silently if one of the variables isn
3726
4457
  in the data frame, but is present in the global environment. We will not go through here how
3727
4458
  to solve this problem in R.
3728
4459
 
3729
- In Galaaz the method mutate_y bellow will work fine and will never fail silently.
4460
+ In Galaaz the method mutate_y below will work fine and will never fail silently.
3730
4461
 
3731
4462
 
3732
- ```ruby
4463
+ ``` ruby
3733
4464
  def mutate_y(df)
3734
- df.mutate(:y.assign :a + :x)
4465
+ # Mutate column names are Ruby kwargs (y: …). Use .assign only for R `<-` expressions.
4466
+ df.mutate(y: R[:a] + R[:x])
3735
4467
  end
3736
4468
  ```
3737
4469
  Here we create a data frame that has only one column named 'x':
3738
4470
 
3739
4471
 
3740
- ```ruby
4472
+ ``` ruby
3741
4473
  df1 = R.data__frame(x: (1..3))
3742
4474
  puts df1
3743
4475
  ```
@@ -3749,34 +4481,31 @@ puts df1
3749
4481
  ## 3 3
3750
4482
  ```
3751
4483
 
3752
- Note that method mutate_y will fail independetly from the fact that variable 'a' is defined and
3753
- in the scope of the method. Variable 'a' has no relationship with the symbol ':a' used in the
4484
+ Note that method mutate_y will fail independently from the fact that variable 'a' is defined and
4485
+ in the scope of the method. Variable 'a' has no relationship with the symbol `R[:a]` used in the
3754
4486
  definition of 'mutate\_y' above:
3755
4487
 
3756
4488
 
3757
- ```ruby
4489
+ ``` ruby
3758
4490
  a = 10
3759
4491
  mutate_y(df1)
3760
4492
  ```
3761
4493
 
3762
4494
  ```
3763
- ## Message:
3764
- ## Error in mutate_impl(.data, dots) :
3765
- ## Evaluation error: object 'a' not found.
3766
- ## In addition: Warning message:
3767
- ## In mutate_impl(.data, dots) :
3768
- ## mismatched protect/unprotect (unprotect with empty protect stack) (RError)
3769
- ## Translated to internal error
4495
+ ## Error: ℹ In argument: `y = a + x`.
4496
+ ## Caused by error:
4497
+ ## ! object 'a' not found
3770
4498
  ```
3771
4499
  ## Different expressions
3772
4500
 
3773
- Let's move to the next problem as presented by Hardley where trying to write a function in R
4501
+ Let's move to the next problem as presented by Hadley where trying to write a function in R
3774
4502
  that will receive two argumens, the first a variable and the second an expression is not trivial.
3775
- Bellow we create a data frame and we want to write a function that groups data by a variable and
4503
+ Below we create a data frame and we want to write a function that groups data by a variable and
3776
4504
  summarises it by an expression:
3777
4505
 
3778
4506
 
3779
- ```r
4507
+ ``` r
4508
+ library(dplyr)
3780
4509
  set.seed(123)
3781
4510
 
3782
4511
  df <- data.frame(
@@ -3798,7 +4527,7 @@ as.data.frame(df)
3798
4527
  ## 5 2 1 1 4
3799
4528
  ```
3800
4529
 
3801
- ```r
4530
+ ``` r
3802
4531
  d2 <- df %>%
3803
4532
  group_by(g1) %>%
3804
4533
  summarise(a = mean(a))
@@ -3812,7 +4541,7 @@ as.data.frame(d2)
3812
4541
  ## 2 2 3.333333
3813
4542
  ```
3814
4543
 
3815
- ```r
4544
+ ``` r
3816
4545
  d2 <- df %>%
3817
4546
  group_by(g2) %>%
3818
4547
  summarise(a = mean(a))
@@ -3826,10 +4555,10 @@ as.data.frame(d2)
3826
4555
  ## 2 2 3
3827
4556
  ```
3828
4557
 
3829
- As shown by Hardley, one might expect this function to do the trick:
4558
+ As shown by Hadley, one might expect this function to do the trick:
3830
4559
 
3831
4560
 
3832
- ```r
4561
+ ``` r
3833
4562
  my_summarise <- function(df, group_var) {
3834
4563
  df %>%
3835
4564
  group_by(group_var) %>%
@@ -3842,15 +4571,15 @@ my_summarise <- function(df, group_var) {
3842
4571
 
3843
4572
  In order to solve this problem, coding with dplyr requires the introduction of many new concepts
3844
4573
  and functions such as 'quo', 'quos', 'enquo', 'enquos', '!!' (bang bang), '!!!' (triple bang).
3845
- Again, we'll leave to Hardley the explanation on how to use all those functions.
4574
+ Again, we'll leave to Hadley the explanation on how to use all those functions.
3846
4575
 
3847
4576
  Now, let's try to implement the same function in galaaz. The next code block first prints the
3848
- 'df' data frame defined previously in R (to access an R variable from Galaaz, we use the tilda
3849
- operator '~' applied to the R variable name as symbol, i.e., ':df'.
4577
+ 'df' data frame defined previously in R (to access an R variable from Galaaz, we use the tilde
4578
+ operator `~` applied to the R variable name as a symbol, e.g. `:df`).
3850
4579
 
3851
4580
 
3852
- ```ruby
3853
- puts ~:df
4581
+ ``` ruby
4582
+ puts ~R[:df]
3854
4583
  ```
3855
4584
 
3856
4585
  ```
@@ -3863,20 +4592,20 @@ puts ~:df
3863
4592
  ```
3864
4593
 
3865
4594
  We then create the 'my_summarize' method and call it passing the R data frame and
3866
- the group by variable ':g1':
4595
+ the group by variable 'R[:g1]':
3867
4596
 
3868
4597
 
3869
- ```ruby
4598
+ ``` ruby
3870
4599
  def my_summarize(df, group_var)
3871
4600
  df.group_by(group_var).
3872
- summarize(a: :a.mean)
4601
+ summarize(a: R[:a].mean)
3873
4602
  end
3874
4603
 
3875
- puts my_summarize(:df, :g1)
4604
+ puts my_summarize(~R[:df], R[:g1])
3876
4605
  ```
3877
4606
 
3878
4607
  ```
3879
- ## # A tibble: 2 x 2
4608
+ ## # A tibble: 2 × 2
3880
4609
  ## g1 a
3881
4610
  ## <dbl> <dbl>
3882
4611
  ## 1 1 2.5
@@ -3886,12 +4615,12 @@ puts my_summarize(:df, :g1)
3886
4615
  It works!!! Well, let's make sure this was not just some coincidence
3887
4616
 
3888
4617
 
3889
- ```ruby
3890
- puts my_summarize(:df, :g2)
4618
+ ``` ruby
4619
+ puts my_summarize(~R[:df], R[:g2])
3891
4620
  ```
3892
4621
 
3893
4622
  ```
3894
- ## # A tibble: 2 x 2
4623
+ ## # A tibble: 2 × 2
3895
4624
  ## g2 a
3896
4625
  ## <dbl> <dbl>
3897
4626
  ## 1 1 3
@@ -3907,7 +4636,7 @@ In the previous section we've managed to get rid of all NSE formulation for a si
3907
4636
  does this remain true for more complex examples, or will the Galaaz way prove inpractical for
3908
4637
  more complex code?
3909
4638
 
3910
- In the next example Hardley proposes us to write a function that given an expression such as 'a'
4639
+ In the next example Hadley proposes us to write a function that given an expression such as 'a'
3911
4640
  or 'a * b', calculates three summaries. What we want a function that does the same as these R
3912
4641
  statements:
3913
4642
 
@@ -3928,7 +4657,7 @@ summarise(df, mean = mean(a * b), sum = sum(a * b), n = n())
3928
4657
  Let's try it in galaaz:
3929
4658
 
3930
4659
 
3931
- ```ruby
4660
+ ``` ruby
3932
4661
  def my_summarise2(df, expr)
3933
4662
  df.summarize(
3934
4663
  mean: E.mean(expr),
@@ -3937,9 +4666,10 @@ def my_summarise2(df, expr)
3937
4666
  )
3938
4667
  end
3939
4668
 
3940
- puts my_summarise2((~:df), :a)
3941
- puts "\n"
3942
- puts my_summarise2((~:df), :a * :b)
4669
+ puts my_summarise2((~R[:df]), :a)
4670
+ puts "
4671
+ "
4672
+ puts my_summarise2((~R[:df]), R[:a] * R[:b])
3943
4673
  ```
3944
4674
 
3945
4675
  ```
@@ -3955,7 +4685,7 @@ careful about is the use of 'E' to build expressions from functions 'mean', 'sum
3955
4685
 
3956
4686
  ## Different input and output variable
3957
4687
 
3958
- Now the next challenge presented by Hardley is to vary the name of the output variables based on
4688
+ Now the next challenge presented by Hadley is to vary the name of the output variables based on
3959
4689
  the received expression. So, if the input expression is 'a', we want our data frame columns to
3960
4690
  be named 'mean\_a' and 'sum\_a'. Now, if the input expression is 'b', columns
3961
4691
  should be named 'mean\_b' and 'sum\_b'.
@@ -3981,13 +4711,13 @@ mutate(df, mean_b = mean(b), sum_b = sum(b))
3981
4711
  #> 4 2 2 5 4 3 15
3982
4712
  #> # … with 1 more row
3983
4713
  ```
3984
- In order to solve this problem in R, Hardley needs to introduce some more new functions and notations:
4714
+ In order to solve this problem in R, Hadley needs to introduce some more new functions and notations:
3985
4715
  'quo_name' and the ':=' operator from package 'rlang'
3986
4716
 
3987
4717
  Here is our Ruby code:
3988
4718
 
3989
4719
 
3990
- ```ruby
4720
+ ``` ruby
3991
4721
  def my_mutate(df, expr)
3992
4722
  mean_name = "mean_#{expr.to_s}"
3993
4723
  sum_name = "sum_#{expr.to_s}"
@@ -3996,9 +4726,10 @@ def my_mutate(df, expr)
3996
4726
  sum_name => E.sum(expr))
3997
4727
  end
3998
4728
 
3999
- puts my_mutate((~:df), :a)
4000
- puts "\n"
4001
- puts my_mutate((~:df), :b)
4729
+ puts my_mutate((~R[:df]), :a)
4730
+ puts "
4731
+ "
4732
+ puts my_mutate((~R[:df]), :b)
4002
4733
  ```
4003
4734
 
4004
4735
  ```
@@ -4026,25 +4757,25 @@ and variable mean\_name is not followed by ':' but by '=>'. This is standard Ru
4026
4757
 
4027
4758
  ## Capturing multiple variables
4028
4759
 
4029
- Moving on with new complexities, Hardley proposes us to solve the problem in which the
4760
+ Moving on with new complexities, Hadley proposes us to solve the problem in which the
4030
4761
  summarise function will receive any number of grouping variables.
4031
4762
 
4032
4763
  This again is quite standard Ruby. In order to receive an undefined number of paramenters
4033
4764
  the paramenter is preceded by '*':
4034
4765
 
4035
4766
 
4036
- ```ruby
4767
+ ``` ruby
4037
4768
  def my_summarise3(df, *group_vars)
4038
4769
  df.group_by(*group_vars).
4039
4770
  summarise(a: E.mean(:a))
4040
4771
  end
4041
4772
 
4042
- puts my_summarise3((~:df), :g1, :g2)
4773
+ puts my_summarise3((~R[:df]), R[:g1], R[:g2])
4043
4774
  ```
4044
4775
 
4045
4776
  ```
4046
- ## # A tibble: 4 x 3
4047
- ## # Groups: g1 [?]
4777
+ ## # A tibble: 4 × 3
4778
+ ## # Groups: g1 [2]
4048
4779
  ## g1 g2 a
4049
4780
  ## <dbl> <dbl> <dbl>
4050
4781
  ## 1 1 1 3
@@ -4068,7 +4799,7 @@ In Ruby, there is no lazy evaluation of parameters and 'a' is always a variable
4068
4799
  Variables assume their value as soon as they are used, so 'x = a' is immediately evaluate and
4069
4800
  variable 'x' will receive the value of variable 'a' as soon as the Ruby statement is executed.
4070
4801
  Ruby also provides the notion of a symbol; ':a' is a symbol and does not evaluate to anything.
4071
- Galaaz uses Ruby symbols to build expressions that are not bound to anything: ':a.eq :b' is
4802
+ Galaaz uses Ruby symbols to build expressions that are not bound to anything: 'R[:a].eq R[:b]' is
4072
4803
  clearly an expression and has no relationship whatsoever with the statment 'a = b'. By using
4073
4804
  symbols, variables and expressions all the possible ambiguities that are found in R are
4074
4805
  eliminated in Galaaz.
@@ -4078,7 +4809,7 @@ of input they are expecting, they might be expecting regular variables or they m
4078
4809
  expecting expressions and the R function will know how to deal with an input of the form
4079
4810
  'a = b', now for the Ruby developer it might not be immediately clear if it should call the
4080
4811
  function passing the value 'true' if variable 'a' is equal to variable 'b' or if it should
4081
- call the function passing the expression ':a.eq :b'.
4812
+ call the function passing the expression 'R[:a].eq R[:b]'.
4082
4813
 
4083
4814
 
4084
4815
  ## Advanced dplyr features
@@ -4101,28 +4832,29 @@ In the following examples, we show the use of functions 'group\_by\_at', 'summar
4101
4832
  features of characters in the Starwars movies:
4102
4833
 
4103
4834
 
4104
- ```ruby
4105
- puts (~:starwars).head
4835
+ ``` ruby
4836
+ puts (~R[:starwars]).head
4106
4837
  ```
4107
4838
 
4108
4839
  ```
4109
- ## # A tibble: 6 x 13
4110
- ## name height mass hair_color skin_color eye_color birth_year gender
4111
- ## <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr>
4112
- ## 1 Luke… 172 77 blond fair blue 19 male
4113
- ## 2 C-3PO 167 75 <NA> gold yellow 112 <NA>
4114
- ## 3 R2-D2 96 32 <NA> white, bl… red 33 <NA>
4115
- ## 4 Dart… 202 136 none white yellow 41.9 male
4116
- ## 5 Leia… 150 49 brown light brown 19 female
4117
- ## 6 Owen 178 120 brown, gr… light blue 52 male
4118
- ## # with 5 more variables: homeworld <chr>, species <chr>, films <list>,
4840
+ ## # A tibble: 6 × 14
4841
+ ## name height mass hair_color skin_color eye_color birth_year sex gender
4842
+ ## <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
4843
+ ## 1 Luke Sky… 172 77 blond fair blue 19 male mascu…
4844
+ ## 2 C-3PO 167 75 <NA> gold yellow 112 none mascu…
4845
+ ## 3 R2-D2 96 32 <NA> white, bl… red 33 none mascu…
4846
+ ## 4 Darth Va… 202 136 none white yellow 41.9 male mascu…
4847
+ ## 5 Leia Org… 150 49 brown light brown 19 fema… femin…
4848
+ ## 6 Owen Lars 178 120 brown, gr… light blue 52 male mascu…
4849
+ ## # 5 more variables: homeworld <chr>, species <chr>, films <list>,
4119
4850
  ## # vehicles <list>, starships <list>
4120
4851
  ```
4121
- The grouped_mean function bellow will receive a grouping variable and calculate summaries for
4852
+ The grouped_mean function below will receive a grouping variable and calculate summaries for
4122
4853
  the value\_variables given:
4123
4854
 
4124
4855
 
4125
- ```r
4856
+ ``` r
4857
+ library(dplyr)
4126
4858
  grouped_mean <- function(data, grouping_variables, value_variables) {
4127
4859
  data %>%
4128
4860
  group_by_at(grouping_variables) %>%
@@ -4133,7 +4865,22 @@ grouped_mean <- function(data, grouping_variables, value_variables) {
4133
4865
 
4134
4866
  gm = starwars %>%
4135
4867
  grouped_mean("eye_color", c("mass", "birth_year"))
4868
+ ```
4136
4869
 
4870
+ ```
4871
+ ## Warning: `funs()` was deprecated in dplyr 0.8.0.
4872
+ ## ℹ Please use a list of either functions or lambdas:
4873
+ ##
4874
+ ## # Simple named list: list(mean = mean, median = median)
4875
+ ##
4876
+ ## # Auto named with `tibble::lst()`: tibble::lst(mean, median)
4877
+ ##
4878
+ ## # Using lambdas list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))
4879
+ ## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
4880
+ ## generated.
4881
+ ```
4882
+
4883
+ ``` r
4137
4884
  as.data.frame(gm)
4138
4885
  ```
4139
4886
 
@@ -4159,20 +4906,20 @@ as.data.frame(gm)
4159
4906
  The same code with Galaaz, becomes:
4160
4907
 
4161
4908
 
4162
- ```ruby
4909
+ ``` ruby
4163
4910
  def grouped_mean(data, grouping_variables, value_variables)
4164
4911
  data.
4165
4912
  group_by_at(grouping_variables).
4166
4913
  mutate(count: E.n).
4167
- summarise_at(E.c(value_variables, "count"), ~:mean, na__rm: true).
4914
+ summarise_at(E.c(value_variables, "count"), ~R[:mean], na__rm: true).
4168
4915
  rename_at(value_variables, E.funs(E.paste0("mean_", value_variables)))
4169
4916
  end
4170
4917
 
4171
- puts grouped_mean((~:starwars), "eye_color", E.c("mass", "birth_year"))
4918
+ puts grouped_mean((~R[:starwars]), "eye_color", E.c("mass", "birth_year"))
4172
4919
  ```
4173
4920
 
4174
4921
  ```
4175
- ## # A tibble: 15 x 4
4922
+ ## # A tibble: 15 × 4
4176
4923
  ## eye_color mean_mass mean_birth_year count
4177
4924
  ## <chr> <dbl> <dbl> <dbl>
4178
4925
  ## 1 black 76.3 33 10
@@ -4192,17 +4939,19 @@ puts grouped_mean((~:starwars), "eye_color", E.c("mass", "birth_year"))
4192
4939
  ## 15 yellow 81.1 76.4 11
4193
4940
  ```
4194
4941
 
4195
-
4196
- [TO BE CONTINUED...]
4197
-
4942
+ The examples above cover programmatic dplyr with string column names and `_at` helpers. The same
4943
+ Galaaz patterns (symbols, `E.*` for expression-safe functions, and Ruby methods on R-backed objects)
4944
+ extend to other tidyverse workflows; consult R package documentation for function-specific
4945
+ arguments.
4198
4946
 
4199
4947
  # Contributing
4200
4948
 
4201
4949
  * Fork it
4202
- * Create your feature branch (git checkout -b my-new-feature)
4203
- * Write Tests!
4204
- * Commit your changes (git commit -am 'Add some feature')
4205
- * Push to the branch (git push origin my-new-feature)
4206
- * Create new Pull Request
4950
+ * Create your feature branch (`git checkout -b my-new-feature`)
4951
+ * Write tests — use **`bin/run_rspec`** or **`bin/run_all_rspec`** (JRuby or CRuby via
4952
+ **`GALAAZ_RUBY`**) so JVM flags and the load path match **`docs/testing.md`**
4953
+ * Commit your changes (`git commit -am 'Add some feature'`)
4954
+ * Push to the branch (`git push origin my-new-feature`)
4955
+ * Open a pull request
4207
4956
 
4208
4957
  # References