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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -0
- data/LICENSE +0 -0
- data/README.md +1416 -667
- data/Rakefile +68 -41
- data/bin/galaaz-bootstrap +137 -0
- data/bin/galaaz-jruby +11 -0
- data/bin/galaaz-ruby +16 -0
- data/bin/galaaz_jruby_env.inc.sh +6 -0
- data/bin/galaaz_ruby_env.inc.sh +36 -0
- data/bin/gbookdown +63 -0
- data/bin/gknit +83 -13
- data/bin/gknit-draft.rb +0 -0
- data/bin/gstudio +5 -3
- data/bin/gstudio_irb.rb +0 -0
- data/bin/gstudio_pry.rb +0 -0
- data/bin/install-tinytex +6 -0
- data/bin/run_all_rspec +44 -0
- data/bin/run_example +17 -0
- data/bin/run_old_rspec +20 -0
- data/bin/run_rspec +24 -0
- data/bin/run_rspec_subset +38 -0
- data/bin/run_slow_rspec +20 -0
- data/blogs/R-on-Rails-Planning-Document.md +940 -0
- data/blogs/README.md +100 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot.Rmd +38 -66
- data/blogs/galaaz_ggplot/galaaz_ggplot.log +754 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot.md +115 -155
- data/blogs/galaaz_ggplot/galaaz_ggplot.tex +607 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-gfm/midwest_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-gfm/scatter_plot_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-html/midwest_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-html/scatter_plot_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-markdown_github/midwest_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-markdown_github/scatter_plot_rb.png +0 -0
- data/blogs/galaaz_ggplot/midwest.Rmd +3 -3
- data/blogs/galaaz_ggplot/midwest_external_png +0 -0
- data/blogs/gknit/gknit.Rmd +47 -52
- data/blogs/gknit/gknit.md +1430 -0
- data/blogs/gknit/gknit_files/figure-gfm/bubble-1.png +0 -0
- data/blogs/gknit/gknit_files/figure-gfm/diverging_bar.png +0 -0
- data/blogs/gknit/gknit_files/figure-html/bubble-1.png +0 -0
- data/blogs/gknit/gknit_files/figure-html/diverging_bar.png +0 -0
- data/blogs/gknit/lst.rds +0 -0
- data/blogs/gknit/model.rb +1 -1
- data/blogs/gknit/stats.bib +0 -0
- data/blogs/manual/include_model_local_repro.Rmd +14 -0
- data/blogs/manual/include_model_local_repro.md +75 -0
- data/blogs/manual/lst.rds +0 -0
- data/blogs/manual/manual.Rmd +855 -239
- data/blogs/manual/manual.log +1786 -0
- data/blogs/manual/manual.md +1416 -667
- data/blogs/manual/manual.tex +1883 -1161
- data/blogs/manual/manual_files/figure-html/bubble-1.png +0 -0
- data/blogs/manual/manual_files/figure-html/diverging_bar.png +0 -0
- data/blogs/manual/manual_files/figure-latex/bubble-1.png +0 -0
- data/blogs/manual/model.rb +1 -1
- data/blogs/nse_dplyr/nse_dplyr.Rmd +84 -111
- data/blogs/nse_dplyr/nse_dplyr.log +928 -0
- data/blogs/nse_dplyr/nse_dplyr.md +198 -229
- data/blogs/oh_my/not_so.rb +0 -0
- data/blogs/oh_my/oh_my.Rmd +1234 -25
- data/blogs/oh_my/oh_my.log +804 -0
- data/blogs/oh_my/oh_my.md +1663 -86
- data/blogs/oh_my/oh_my.tex +821 -0
- data/blogs/oh_my/old.Rmd +15 -14
- data/blogs/ruby_plot/ruby_plot.Rmd +58 -82
- data/blogs/ruby_plot/ruby_plot.log +885 -0
- data/blogs/ruby_plot/ruby_plot.md +71 -102
- data/blogs/ruby_plot/ruby_plot.tex +940 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-gfm/dose_len.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facet_by_delivery.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facet_by_dose.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facets_by_delivery_color.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facets_by_delivery_color2.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facets_with_decorations.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facets_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facets_with_points.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-gfm/final_box_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-gfm/final_violin_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-gfm/violin_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/dose_len.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_delivery.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_dose.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color2.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_decorations.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_points.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/final_box_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/final_violin_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/violin_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/dose_len.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facet_by_delivery.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facet_by_dose.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_by_delivery_color.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_by_delivery_color2.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_decorations.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_points.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/final_box_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/final_violin_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/violin_with_jitter.png +0 -0
- data/blogs/test/test.Rmd +14 -0
- data/blogs/test/test.md +10 -0
- data/examples/50Plots_MasterList/Images/midwest-scatterplot.PNG +0 -0
- data/examples/50Plots_MasterList/ScatterPlot.rb +2 -1
- data/examples/50Plots_MasterList/scatter_plot.rb +1 -0
- data/examples/Bibliography/master.bib +0 -0
- data/examples/Bibliography/stats.bib +0 -0
- data/examples/R/calc.R +0 -0
- data/examples/R/java_interop.R +0 -0
- data/examples/bioconductor_deseq2_airway/Documentation/DESeq2-airway-walkthrough.md +56 -0
- data/examples/bioconductor_deseq2_airway/bench_galaaz_three_same_process.rb +54 -0
- data/examples/bioconductor_deseq2_airway/bench_r_three_same_process.R +34 -0
- data/examples/bioconductor_deseq2_airway/deseq2_airway_galaaz.rb +34 -0
- data/examples/bioconductor_deseq2_airway/deseq2_airway_galaaz_optimized.rb +35 -0
- data/examples/bioconductor_deseq2_airway/deseq2_airway_minimal.R +30 -0
- data/examples/bioconductor_deseq2_airway/deseq2_airway_pipeline_for_bench.R +36 -0
- data/examples/islr/all.rb +14 -0
- data/examples/islr/ch2.spec.rb +38 -7
- data/examples/islr/ch3.spec.rb +12 -2
- data/examples/islr/ch3_boston.rb +28 -0
- data/examples/islr/ch3_multiple_regression.rb +1 -0
- data/examples/islr/ch6.spec.rb +25 -1
- data/examples/islr/x_y_rnorm.jpg +0 -0
- data/examples/latex_templates/Test-acm_article/acm_proc_article-sp.cls +0 -0
- data/examples/latex_templates/Test-acm_article/sigproc.bib +0 -0
- data/examples/latex_templates/Test-acs_article/acs-Test-acs_article.bib +0 -0
- data/examples/latex_templates/Test-acs_article/acs-my_output.bib +0 -0
- data/examples/latex_templates/Test-aea_article/BibFile.bib +0 -0
- data/examples/latex_templates/Test-aea_article/Test-aea_article.Rmd +0 -0
- data/examples/latex_templates/Test-aea_article/references.bib +0 -0
- data/examples/latex_templates/Test-amq_article/Test-amq_article.Rmd +0 -0
- data/examples/latex_templates/Test-amq_article/Test-amq_article.pdfsync +0 -0
- data/examples/latex_templates/Test-ieee_article/IEEEtran.bst +0 -0
- data/examples/latex_templates/Test-ieee_article/mybibfile.bib +0 -0
- data/examples/latex_templates/Test-rjournal_article/RJournal.sty +0 -0
- data/examples/latex_templates/Test-rjournal_article/RJreferences.bib +0 -0
- data/examples/latex_templates/Test-rjournal_article/Test-rjournal_article.Rmd +0 -0
- data/examples/misc/baseball.csv +0 -0
- data/examples/misc/ggplot.rb +5 -3
- data/examples/misc/moneyball.rb +1 -0
- data/examples/misc/subsetting.rb +1 -0
- data/examples/multithread_shards_to_r/shards_to_r.rb +68 -0
- data/examples/rmarkdown/svm-rmarkdown-anon-ms-example/svm-rmarkdown-anon-ms-example.Rmd +0 -0
- data/examples/rmarkdown/svm-rmarkdown-article-example/svm-rmarkdown-article-example.Rmd +0 -0
- data/examples/rmarkdown/svm-rmarkdown-beamer-example/svm-rmarkdown-beamer-example.Rmd +0 -0
- data/examples/rmarkdown/svm-rmarkdown-cv/svm-rmarkdown-cv.Rmd +0 -0
- data/examples/rmarkdown/svm-rmarkdown-syllabus-example/attend-grade-relationships.csv +0 -0
- data/examples/rmarkdown/svm-rmarkdown-syllabus-example/svm-rmarkdown-syllabus-example.Rmd +0 -0
- data/examples/rmarkdown/svm-xaringan-example/svm-xaringan-example.Rmd +0 -0
- data/examples/sthda_ggplot/README.md +0 -0
- data/examples/sthda_ggplot/RUN.md +41 -0
- data/examples/sthda_ggplot/all.rb +1 -0
- data/examples/sthda_ggplot/one_variable_continuous/density_gg.rb +1 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_area.rb +1 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_density.rb +3 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_dotplot.rb +1 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_freqpoly.rb +1 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_histogram.rb +1 -0
- data/examples/sthda_ggplot/one_variable_continuous/histogram_density.rb +1 -0
- data/examples/sthda_ggplot/one_variable_continuous/stat.rb +1 -0
- data/examples/sthda_ggplot/one_variable_discrete/bar.rb +1 -0
- data/examples/sthda_ggplot/qplots/box_violin_dot.rb +1 -0
- data/examples/sthda_ggplot/qplots/scatter_plots.rb +1 -0
- data/examples/sthda_ggplot/scatter_gg.rb +1 -0
- data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_bin2d.rb +1 -0
- data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_density2d.rb +1 -0
- data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_hex.rb +1 -0
- data/examples/sthda_ggplot/two_variables_cont_cont/geom_point.rb +1 -0
- data/examples/sthda_ggplot/two_variables_cont_cont/geom_smooth.rb +1 -0
- data/examples/sthda_ggplot/two_variables_cont_cont/misc.rb +1 -0
- data/examples/sthda_ggplot/two_variables_cont_function/geom_area.rb +5 -3
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_bar.rb +1 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_boxplot.rb +1 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_dotplot.rb +1 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_jitter.rb +1 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_line.rb +1 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_violin.rb +1 -0
- data/examples/sthda_ggplot/two_variables_disc_disc/geom_jitter.rb +1 -0
- data/examples/sthda_ggplot/two_variables_error/geom_crossbar.rb +1 -0
- data/ext/new_bridge/Makefile +46 -0
- data/ext/new_bridge/galaaz_gatekeeper_phase0.cpp +12 -0
- data/ext/new_bridge/galaaz_gatekeeper_phase1.cpp +1639 -0
- data/lib/R_interface/galaaz_device.R +20 -0
- data/lib/R_interface/include_engine.R +109 -0
- data/lib/R_interface/new_bridge_adapter.rb +824 -0
- data/lib/R_interface/r.rb +177 -25
- data/lib/R_interface/r_arrow.rb +113 -0
- data/lib/R_interface/r_libs.R +3 -3
- data/lib/R_interface/r_methods.rb +13 -126
- data/lib/R_interface/r_module_s.rb +0 -0
- data/lib/R_interface/rbinary_operators.rb +20 -2
- data/lib/R_interface/rclosure.rb +5 -1
- data/lib/R_interface/rdata_frame.rb +34 -70
- data/lib/R_interface/rdevice.rb +125 -0
- data/lib/R_interface/rdevices.R +0 -0
- data/lib/R_interface/renvironment.rb +10 -4
- data/lib/R_interface/rexpression.rb +5 -1
- data/lib/R_interface/rindexed_object.rb +41 -13
- data/lib/R_interface/rlanguage.rb +20 -62
- data/lib/R_interface/rlist.rb +115 -25
- data/lib/R_interface/rlogical_operators.rb +0 -0
- data/lib/R_interface/rmatrix.rb +2 -11
- data/lib/R_interface/rmd_indexed_object.rb +5 -1
- data/lib/R_interface/robject.rb +348 -290
- data/lib/R_interface/rpkg.rb +0 -0
- data/lib/R_interface/rsupport.rb +609 -328
- data/lib/R_interface/rsupport_scope.rb +2 -1
- data/lib/R_interface/rsymbol.rb +50 -0
- data/lib/R_interface/ruby_callback.rb +2 -3
- data/lib/R_interface/ruby_extensions.rb +225 -175
- data/lib/R_interface/runary_operators.rb +0 -0
- data/lib/R_interface/rvector.rb +162 -31
- data/lib/galaaz.rb +0 -0
- data/lib/galaaz_jruby.rb +22 -0
- data/lib/galaaz_ruby.rb +34 -0
- data/lib/gknit/diagnostics.rb +50 -0
- data/lib/gknit/draft.rb +23 -17
- data/lib/gknit/include_engine.rb +15 -7
- data/lib/gknit/knitr_engine.rb +223 -74
- data/lib/gknit/rb_engine.rb +3 -3
- data/lib/gknit/ruby_engine.rb +0 -0
- data/lib/gknit.rb +1 -0
- data/lib/new_bridge/bootstrap/windows_bootstrap.rb +285 -0
- data/lib/new_bridge/envelope.rb +51 -0
- data/lib/new_bridge/eval_result.rb +26 -0
- data/lib/new_bridge/framing.rb +39 -0
- data/lib/new_bridge/instance_pool_client.rb +38 -0
- data/lib/new_bridge/r_instance_manager.rb +404 -0
- data/lib/new_bridge/session_client.rb +530 -0
- data/lib/new_bridge/tcp_framed.rb +44 -0
- data/lib/new_bridge.rb +9 -0
- data/lib/util/exec_ruby.rb +95 -20
- data/lib/util/inline_file.rb +35 -30
- data/new_bridge_specs/benchmark_phase5_5_unboxing_spec.rb +96 -0
- data/new_bridge_specs/eval_r_async_spec.rb +113 -0
- data/new_bridge_specs/integration_phase5_1_concurrent_spec.rb +50 -0
- data/new_bridge_specs/integration_phase5_1_eval_spec.rb +16 -0
- data/new_bridge_specs/integration_phase5_1_r_api_spec.rb +25 -0
- data/new_bridge_specs/integration_phase5_1_smoke_spec.rb +31 -0
- data/new_bridge_specs/integration_phase5_2_dataframe_unboxing_spec.rb +19 -0
- data/new_bridge_specs/integration_phase5_2_handle_eval_unboxing_spec.rb +25 -0
- data/new_bridge_specs/integration_phase5_3_callback_args_spec.rb +28 -0
- data/new_bridge_specs/integration_phase5_3_callback_error_spec.rb +22 -0
- data/new_bridge_specs/integration_phase5_3_callback_timeout_spec.rb +28 -0
- data/new_bridge_specs/integration_phase5_3_callbacks_smoke_spec.rb +22 -0
- data/new_bridge_specs/integration_phase5_3_edge_cases_spec.rb +52 -0
- data/new_bridge_specs/integration_phase5_3_nested_spec.rb +30 -0
- data/new_bridge_specs/integration_phase5_4_concurrent_sessions_spec.rb +53 -0
- data/new_bridge_specs/integration_phase5_4_nested_session_callbacks_spec.rb +49 -0
- data/new_bridge_specs/integration_phase5_4_session_routing_spec.rb +38 -0
- data/new_bridge_specs/integration_phase5_5_stress_concurrency_spec.rb +52 -0
- data/new_bridge_specs/integration_phase5_5_unbox_walk_spec.rb +46 -0
- data/new_bridge_specs/phase0_protocol_spec.rb +96 -0
- data/new_bridge_specs/phase1_req_ret_spec.rb +66 -0
- data/new_bridge_specs/phase2_multi_instance_spec.rb +67 -0
- data/new_bridge_specs/phase3_callbacks_spec.rb +71 -0
- data/new_bridge_specs/phase4_2_hardening_spec.rb +252 -0
- data/new_bridge_specs/phase4_3_r_instance_manager_spec.rb +85 -0
- data/new_bridge_specs/phase4_nested_callbacks_spec.rb +123 -0
- data/r_requires/ggplot.rb +0 -0
- data/r_requires/knitr.rb +0 -0
- data/specs/all.rb +15 -11
- data/specs/arrow_from_ruby_batches_spec.rb +50 -0
- data/specs/arrow_semantics_spec.rb +64 -0
- data/specs/bridge_concurrent_spec.rb +46 -0
- data/specs/bridge_nested_spec.rb +25 -0
- data/specs/dataframe_semantics_spec.rb +122 -0
- data/specs/dataframe_single_index_logical_filter_spec.rb +21 -0
- data/specs/dispatch_probe_cache_spec.rb +38 -0
- data/specs/dispatch_probe_error_class_fallback_spec.rb +20 -0
- data/specs/dispatch_probe_fallback_spec.rb +18 -0
- data/specs/environment_semantics_spec.rb +89 -0
- data/specs/field_access_spec.rb +31 -0
- data/specs/figures/bg.jpeg +0 -0
- data/specs/figures/bg.png +0 -0
- data/specs/figures/bg.svg +168 -57
- data/specs/figures/dose_len.png +0 -0
- data/specs/figures/no_args.jpeg +0 -0
- data/specs/figures/no_args.png +0 -0
- data/specs/figures/no_args.svg +168 -57
- data/specs/figures/width_height.jpeg +0 -0
- data/specs/figures/width_height.png +0 -0
- data/specs/figures/width_height_units1.jpeg +0 -0
- data/specs/figures/width_height_units1.png +0 -0
- data/specs/figures/width_height_units2.jpeg +0 -0
- data/specs/figures/width_height_units2.png +0 -0
- data/specs/formula_semantics_spec.rb +81 -0
- data/specs/galaaz_util_exec_ruby_spec.rb +85 -0
- data/specs/galaaz_util_inline_file_spec.rb +54 -0
- data/specs/gknit_cli_option_permutation_spec.rb +24 -0
- data/specs/gknit_include_engine_spec.rb +72 -0
- data/specs/gknit_install_timeout_report_spec.rb +69 -0
- data/specs/gknit_internal_error_report_spec.rb +57 -0
- data/specs/gknit_vector_map_output_spec.rb +59 -0
- data/specs/globalenv_guardrail_spec.rb +52 -0
- data/specs/language_expression_semantics_spec.rb +145 -0
- data/specs/list_semantics_spec.rb +111 -0
- data/specs/new_bridge_bulk_dataframe_transfer_spec.rb +44 -0
- data/specs/new_bridge_bulk_vector_transfer_spec.rb +73 -0
- data/specs/new_bridge_callback_timeout_spec.rb +69 -0
- data/specs/new_bridge_eval_r_fallback_spec.rb +55 -0
- data/specs/nil_null_spec.rb +42 -0
- data/specs/object_build_phase2_spec.rb +53 -0
- data/specs/phase1_callback_bridge_spec.rb +84 -0
- data/specs/phase2_gknit_generic_rendering_guardrail_spec.rb +46 -0
- data/specs/phase2_gknit_no_raw_code_leakage_spec.rb +43 -0
- data/specs/phase3_gknit_generic_graphics_capture_spec.rb +71 -0
- data/specs/plot_device_semantics_spec.rb +28 -0
- data/specs/plot_snapshot_semantics_spec.rb +58 -0
- data/specs/protocol_result_spec.rb +236 -0
- data/specs/r_batch_fail_fast_spec.rb +47 -0
- data/specs/r_bridge_bootstrap_spec.rb +11 -0
- data/specs/r_devices.spec.rb +1 -1
- data/specs/r_eval.spec.rb +16 -18
- data/specs/r_function.spec.rb +1 -1
- data/specs/r_instance_manager_spec.rb +285 -0
- data/specs/r_list_apply.spec.rb +15 -15
- data/specs/r_matrix.spec.rb +0 -0
- data/specs/r_nse.spec.rb +5 -5
- data/specs/r_object_send_dispatch_spec.rb +13 -0
- data/specs/r_vector_comparator_spec.rb +8 -0
- data/specs/r_vector_creation.spec.rb +0 -0
- data/specs/r_vector_functions.spec.rb +0 -0
- data/specs/r_vector_object.spec.rb +0 -0
- data/specs/r_vector_operators.spec.rb +0 -0
- data/specs/r_vector_structured_scalar_reads_spec.rb +35 -0
- data/specs/r_vector_subsetting.spec.rb +0 -0
- data/specs/range_helper_spec.rb +21 -0
- data/specs/rsupport_scope_spec.rb +28 -0
- data/specs/rsupport_var_name_thread_safety_spec.rb +24 -0
- data/specs/scalar_character_spec.rb +44 -0
- data/specs/scoped_symbol_dsl_refinement_spec.rb +40 -0
- data/specs/session_env_bridge_spec.rb +25 -0
- data/specs/simplecov_bootstrap_spec.rb +10 -0
- data/specs/spec_helper.rb +10 -0
- data/specs/tmp.rb +0 -0
- data/specs/unboxing_recursion_regression_spec.rb +30 -0
- data/specs/unboxing_spec.rb +49 -0
- data/specs/verify_callbacks.rb +42 -0
- data/sty/galaaz.sty +0 -0
- data/version.rb +1 -1
- metadata +219 -63
- data/blogs/galaaz_ggplot/galaaz_ggplot.html +0 -520
- data/blogs/galaaz_ggplot/galaaz_ggplot.pdf +0 -0
- data/blogs/galaaz_ggplot/midwest.html +0 -188
- data/blogs/gknit/gknit.html +0 -2266
- data/blogs/gknit/gknit.pdf +0 -0
- data/blogs/manual/manual.html +0 -4638
- data/blogs/manual/manual.pdf +0 -0
- data/blogs/manual/manual_files/figure-latex/diverging_bar.pdf +0 -0
- data/blogs/nse_dplyr/nse_dplyr.html +0 -878
- data/blogs/nse_dplyr/nse_dplyr.pdf +0 -0
- data/blogs/oh_my/oh_my.html +0 -568
- data/blogs/ruby_plot/ruby_plot.html +0 -544
- data/blogs/ruby_plot/ruby_plot.pdf +0 -0
- data/examples/latex_templates/Test-acs_article/Test-acs_article.pdf +0 -0
- data/examples/latex_templates/Test-aea_article/Test-aea_article.pdf +0 -0
- data/examples/latex_templates/Test-amq_article/Test-amq_article.pdf +0 -0
- data/examples/latex_templates/Test-amq_article/pics/Figure2.pdf +0 -0
- data/examples/latex_templates/Test-asa_article/Test-asa_article.pdf +0 -0
- data/examples/latex_templates/Test-ieee_article/Test-ieee_article.pdf +0 -0
- data/examples/latex_templates/Test-rjournal_article/RJwrapper.pdf +0 -0
- data/examples/latex_templates/Test-springer_article/Test-springer_article.pdf +0 -0
- data/examples/rmarkdown/svm-rmarkdown-anon-ms-example/svm-rmarkdown-anon-ms-example.pdf +0 -0
- data/examples/rmarkdown/svm-rmarkdown-article-example/svm-rmarkdown-article-example.pdf +0 -0
- data/examples/rmarkdown/svm-rmarkdown-beamer-example/svm-rmarkdown-beamer-example.pdf +0 -0
- data/examples/rmarkdown/svm-rmarkdown-cv/svm-rmarkdown-cv.pdf +0 -0
- data/examples/rmarkdown/svm-rmarkdown-syllabus-example/svm-rmarkdown-syllabus-example.pdf +0 -0
- data/specs/r_dataframe.spec.rb +0 -379
- data/specs/r_environment.spec.rb +0 -140
- data/specs/r_formula.spec.rb +0 -232
- data/specs/r_language.spec.rb +0 -112
- data/specs/r_list.spec.rb +0 -293
- data/specs/r_plots.spec.rb +0 -72
- data/specs/ruby_expression.spec.rb +0 -316
data/blogs/manual/manual.Rmd
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Galaaz Manual"
|
|
3
|
-
subtitle: "
|
|
3
|
+
subtitle: "R-on-Rails: GNU R meets Ruby for the web"
|
|
4
4
|
author: "Rodrigo Botafogo"
|
|
5
|
-
tags: [Galaaz, Ruby, R,
|
|
6
|
-
date: "
|
|
7
|
-
bibliography: "/
|
|
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
|
+
html_document:
|
|
10
|
+
self_contained: true
|
|
11
|
+
keep_md: true
|
|
12
|
+
toc: true
|
|
13
|
+
toc_depth: 3
|
|
14
|
+
number_sections: true
|
|
9
15
|
pdf_document:
|
|
10
16
|
includes:
|
|
11
17
|
in_header: "../../sty/galaaz.sty"
|
|
@@ -13,15 +19,15 @@ output:
|
|
|
13
19
|
number_sections: yes
|
|
14
20
|
toc: true
|
|
15
21
|
toc_depth: 3
|
|
16
|
-
html_document:
|
|
17
|
-
self_contained: true
|
|
18
|
-
keep_md: true
|
|
19
22
|
md_document:
|
|
20
23
|
variant: markdown_github
|
|
21
24
|
fontsize: 11pt
|
|
22
25
|
---
|
|
23
26
|
|
|
24
27
|
```{ruby setup, echo=FALSE}
|
|
28
|
+
# Bridge default is 60s; some chunks (Arrow, large dplyr pipes) need more.
|
|
29
|
+
ENV['GALAAZ_BRIDGE_TIMEOUT_SEC'] ||= '300'
|
|
30
|
+
|
|
25
31
|
R.options(crayon__enabled: false)
|
|
26
32
|
R.install_and_loads('kableExtra')
|
|
27
33
|
```
|
|
@@ -32,8 +38,9 @@ Galaaz is a system for tightly coupling Ruby and R. Ruby is a powerful language,
|
|
|
32
38
|
community, a very large set of libraries and great for web development. However, it lacks
|
|
33
39
|
libraries for data science, statistics, scientific plotting and machine learning. On the
|
|
34
40
|
other hand, R is considered one of the most powerful languages for solving all of the above
|
|
35
|
-
problems.
|
|
36
|
-
|
|
41
|
+
problems. **Python** is a strong competitor: NumPy, pandas, SciPy, and scikit-learn are
|
|
42
|
+
widely used building blocks, and **PyPI** hosts many thousands of other packages for
|
|
43
|
+
numerical work, machine learning, and beyond.
|
|
37
44
|
|
|
38
45
|
With Galaaz we do not intend to re-implement any of the scientific libraries in R, we allow
|
|
39
46
|
for very tight coupling between the two languages to the point that the Ruby developer does
|
|
@@ -44,59 +51,39 @@ general-purpose programming language. It was designed and developed in the mid-1
|
|
|
44
51
|
"Matz" Matsumoto in Japan." It reached high popularity with the development of Ruby on Rails
|
|
45
52
|
(RoR) by David Heinemeier Hansson. RoR is a web application framework first released
|
|
46
53
|
around 2005. It makes extensive use of Ruby's metaprogramming features. With RoR,
|
|
47
|
-
Ruby became very popular. According to [Ruby
|
|
48
|
-
it
|
|
49
|
-
|
|
50
|
-
most popular language.
|
|
54
|
+
Ruby became very popular. According to [Ruby’s place in the TIOBE index](https://www.tiobe.com/tiobe-index/ruby/)
|
|
55
|
+
it peaked in popularity around 2008, then declined until 2015 when it started picking up again.
|
|
56
|
+
Ruby remains a significant language in web development and general-purpose scripting.
|
|
51
57
|
|
|
52
58
|
Python, a language similar to Ruby, ranks 4th in the index. Java, C and C++ take the
|
|
53
59
|
first three positions. Ruby is often criticized for its focus on web applications.
|
|
54
60
|
But Ruby can do [much more](https://github.com/markets/awesome-ruby) than just web applications.
|
|
55
|
-
Yet, for scientific computing, Ruby lags
|
|
56
|
-
|
|
61
|
+
Yet, for scientific computing, Ruby lags behind Python and R. Python offers Django and
|
|
62
|
+
similar frameworks for the web, plus NumPy, pandas, and a deep catalog of science and ML libraries.
|
|
57
63
|
R is a free software environment for statistical computing and graphics with thousands
|
|
58
64
|
of libraries for data analysis.
|
|
59
65
|
|
|
60
66
|
Until recently, there was no real perspective for Ruby to bridge this gap.
|
|
61
67
|
Implementing a complete scientific computing infrastructure would take too long.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
> * That library is not available in my language. I need to rewrite it.
|
|
82
|
-
> * That language would be the perfect fit for my problem, but we cannot
|
|
83
|
-
> run it in our environment.
|
|
84
|
-
> * That problem is already solved in my language, but the language is
|
|
85
|
-
> too slow.
|
|
86
|
-
>
|
|
87
|
-
> With GraalVM we aim to allow developers to freely choose the right language for
|
|
88
|
-
> the task at hand without making compromises.
|
|
89
|
-
|
|
90
|
-
As stated above, GraalVM is a _universal_ virtual machine that allows Ruby and R (and other
|
|
91
|
-
languages) to run on the same environment. GraalVM allows polyglot applications to
|
|
92
|
-
_seamlessly_ interact with one another and pass values from one language to the other.
|
|
93
|
-
Although a great idea, GraalVM still requires application writers to know several languages.
|
|
94
|
-
To eliminate that requirement, we built Galaaz, a gem for Ruby, to tightly couple
|
|
95
|
-
Ruby and R and allow those languages to interact in a way that the user will be unaware
|
|
96
|
-
of such interaction. In other words, a Ruby programmer will be able to use all
|
|
97
|
-
the capabilities of R without knowing the R syntax.
|
|
98
|
-
|
|
99
|
-
Library wrapping is a usual way of bringing features from one language into another.
|
|
68
|
+
|
|
69
|
+
**Galaaz 2.0** couples **JRuby** (Ruby on the JVM) with **GNU R**—the same R you use for
|
|
70
|
+
CRAN and Bioconductor. Ruby and R run in **separate processes**; the **Galaaz bridge**
|
|
71
|
+
sends requests to R and returns results to Ruby. From your point of view you still write
|
|
72
|
+
Ruby: `R.c(...)`, `R.library('ggplot2')`, `~R[:mtcars]`, and dplyr-style chains on R objects.
|
|
73
|
+
You do not need to learn R syntax to get a lot done, though reading R documentation for
|
|
74
|
+
individual packages remains useful.
|
|
75
|
+
|
|
76
|
+
Earlier experiments with Galaaz used Oracle’s **GraalVM** with TruffleRuby and FastR so that
|
|
77
|
+
Ruby and R could share one runtime. That path is no longer the focus: **standard GNU R**
|
|
78
|
+
gives full compatibility with the R package ecosystem (including compiled extensions and
|
|
79
|
+
Bioconductor) while JRuby gives a mature Ruby with **real multithreading** for application
|
|
80
|
+
and I/O code.
|
|
81
|
+
|
|
82
|
+
The bridge handles **communication and typing** between the two worlds; large tables can
|
|
83
|
+
also flow through **Apache Arrow** on the R side when you use the optional helpers described
|
|
84
|
+
later in this manual.
|
|
85
|
+
|
|
86
|
+
Library wrapping is a common way to bring features from one language into another.
|
|
100
87
|
To improve performance, Python often wraps more efficient C libraries. For the
|
|
101
88
|
Python developer, the existence of such C libraries is hidden. The problem with
|
|
102
89
|
library wrapping is that for any new library, there is the need to handcraft a new
|
|
@@ -121,27 +108,203 @@ Galaaz is the Portuguese name for "Galahad". From Wikipedia:
|
|
|
121
108
|
His name should not be mistaken with Galehaut, a different knight from
|
|
122
109
|
Arthurian legend.
|
|
123
110
|
|
|
111
|
+
# Command-line tools (`bin/`)
|
|
112
|
+
|
|
113
|
+
The Galaaz repository ships many helpers under **`bin/`**. When working from a **clone**, call
|
|
114
|
+
them as **`bin/<name>`** from the project root (or `./bin/<name>`). If you install the **gem**,
|
|
115
|
+
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.
|
|
116
|
+
|
|
117
|
+
Below, **current (Galaaz 2.0 + JRuby or CRuby + GNU R)** means the tool uses **`bin/galaaz-ruby`**
|
|
118
|
+
/ **`GALAAZ_RUBY`** (default: `ruby` on `PATH`) and applies JVM flags only when the interpreter is
|
|
119
|
+
JRuby (`bin/galaaz_jruby_env.inc.sh` / `lib/galaaz_jruby.rb`). **`bin/galaaz-jruby`** forces JRuby.
|
|
120
|
+
**Legacy** means the script still targets **GraalVM** polyglot Ruby / FastR-era invocation and is
|
|
121
|
+
**not** expected to work on a typical JRuby or CRuby NewBridge setup.
|
|
122
|
+
|
|
123
|
+
**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.
|
|
124
|
+
|
|
125
|
+
```{r bin-tables-helper, echo=FALSE}
|
|
126
|
+
bin_tbl <- function(df) {
|
|
127
|
+
k <- knitr::kable(df, row.names = FALSE, booktabs = TRUE, linesep = "",
|
|
128
|
+
col.names = c("Script", "Role", "2.0?"))
|
|
129
|
+
if (knitr::is_latex_output()) {
|
|
130
|
+
k <- kableExtra::kable_styling(k, font_size = 9, latex_options = "scale_down")
|
|
131
|
+
k <- kableExtra::column_spec(k, 1, width = "2.5cm")
|
|
132
|
+
k <- kableExtra::column_spec(k, 2, width = "9.5cm")
|
|
133
|
+
k <- kableExtra::column_spec(k, 3, width = "2.8cm")
|
|
134
|
+
} else {
|
|
135
|
+
k <- kableExtra::kable_styling(k, bootstrap_options = c("striped", "condensed"), full_width = TRUE)
|
|
136
|
+
}
|
|
137
|
+
k
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
```{r bin-tables-bootstrap, echo=FALSE}
|
|
142
|
+
df_boot <- data.frame(
|
|
143
|
+
Script = c("galaaz-bootstrap", "galaaz-jruby", "galaaz_jruby_env.inc.sh", "install-tinytex"),
|
|
144
|
+
Role = c(
|
|
145
|
+
"WSL2 helper: Docker checks; optional TinyTeX or poppler for gKnit PDF.",
|
|
146
|
+
"JRuby with repo lib/ on LOAD_PATH and required JVM flags (e.g. Arrow).",
|
|
147
|
+
"Sourced by bash wrappers; sets GALAAZ_REQUIRED_JRUBY_J_ARGS.",
|
|
148
|
+
"Install TinyTeX for PDF output."
|
|
149
|
+
),
|
|
150
|
+
X2 = c("Yes*", "Yes", "Yes†", "Yes"),
|
|
151
|
+
stringsAsFactors = FALSE
|
|
152
|
+
)
|
|
153
|
+
bin_tbl(df_boot)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
\* Where WSL/Docker apply. **`galaaz-bootstrap` flags:** `--check`, `--apply`, `--runtime` (`docker` \| `local` \| `auto`), `--[no-]prompt-doc-tools`.
|
|
157
|
+
|
|
158
|
+
† Not run directly.
|
|
159
|
+
|
|
160
|
+
**`galaaz-jruby` examples** (from repo root):
|
|
161
|
+
|
|
162
|
+
```text
|
|
163
|
+
bin/galaaz-jruby my_script.rb
|
|
164
|
+
bin/galaaz-jruby -S rspec
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Interactive use, examples, and Rake
|
|
168
|
+
|
|
169
|
+
```{r bin-tables-interactive, echo=FALSE}
|
|
170
|
+
df_ix <- data.frame(
|
|
171
|
+
Script = c("gstudio", "run_example", "galaaz"),
|
|
172
|
+
Role = c(
|
|
173
|
+
"IRB or Pry with Galaaz preloaded (JRuby + JVM flags).",
|
|
174
|
+
"Run one Ruby file using the same JRuby/JVM setup as tests.",
|
|
175
|
+
"Forward arguments to rake (needs rake; usually JRuby)."
|
|
176
|
+
),
|
|
177
|
+
X2 = c("Yes", "Yes", "Yes"),
|
|
178
|
+
stringsAsFactors = FALSE
|
|
179
|
+
)
|
|
180
|
+
bin_tbl(df_ix)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## gKnit and document drafts
|
|
184
|
+
|
|
185
|
+
```{r bin-tables-gknit, echo=FALSE}
|
|
186
|
+
df_gk <- data.frame(
|
|
187
|
+
Script = c("gknit", "gknit-draft", "gknit-draft.rb", "gknit_Rscript"),
|
|
188
|
+
Role = c(
|
|
189
|
+
"Knit .Rmd via JRuby and R Markdown render.",
|
|
190
|
+
"Drafts from rticles-style templates; wrapper still uses legacy polyglot ruby.",
|
|
191
|
+
"Ruby entry: GKnit.draft (use with JRuby + LOAD_PATH).",
|
|
192
|
+
"Polyglot Rscript launcher; hard-coded LOAD_PATH sample."
|
|
193
|
+
),
|
|
194
|
+
X2 = c("Yes", "Legacy", "JRuby", "No"),
|
|
195
|
+
stringsAsFactors = FALSE
|
|
196
|
+
)
|
|
197
|
+
bin_tbl(df_gk)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**`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.
|
|
201
|
+
|
|
202
|
+
Prefer **`galaaz-jruby`** for **`gknit-draft`** workflows until that wrapper matches the **`gknit`** stack.
|
|
203
|
+
|
|
204
|
+
## Tests
|
|
205
|
+
|
|
206
|
+
```{r bin-tables-tests, echo=FALSE}
|
|
207
|
+
df_ts <- data.frame(
|
|
208
|
+
Script = c("run_rspec", "run_all_rspec", "run_slow_rspec", "run_old_rspec", "run_rspec_subset"),
|
|
209
|
+
Role = c(
|
|
210
|
+
"Top-level specs/*_spec.rb with spec_helper (see docs/testing.md).",
|
|
211
|
+
"Compile ext/new_bridge; run specs/ and new_bridge_specs/ together.",
|
|
212
|
+
"Suites under slow-specs/ (read script header for spec_helper).",
|
|
213
|
+
"Legacy suites under old_specs/.",
|
|
214
|
+
"Numbered subset 1–18 (Documentation/Spec_Subsets.md)."
|
|
215
|
+
),
|
|
216
|
+
X2 = c("Yes", "Yes", "Yes", "Yes", "Yes"),
|
|
217
|
+
stringsAsFactors = FALSE
|
|
218
|
+
)
|
|
219
|
+
bin_tbl(df_ts)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Other
|
|
223
|
+
|
|
224
|
+
```{r bin-tables-other, echo=FALSE}
|
|
225
|
+
df_ot <- data.frame(
|
|
226
|
+
Script = c("grun", "gstudio_irb.rb / gstudio_pry.rb"),
|
|
227
|
+
Role = c(
|
|
228
|
+
"Graal-era launcher: polyglot ruby with --jvm. Use galaaz-jruby -S instead.",
|
|
229
|
+
"Loaded by gstudio; not meant to be run standalone."
|
|
230
|
+
),
|
|
231
|
+
X2 = c("No", "Yes"),
|
|
232
|
+
stringsAsFactors = FALSE
|
|
233
|
+
)
|
|
234
|
+
bin_tbl(df_ot)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
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`**.
|
|
238
|
+
|
|
124
239
|
# System Compatibility
|
|
125
240
|
|
|
126
|
-
|
|
127
|
-
* Ubuntu 18.04 LTS
|
|
128
|
-
* Ubuntu 16.04 LTS
|
|
129
|
-
* Fedora 28
|
|
130
|
-
* macOS 10.14 (Mojave)
|
|
131
|
-
* macOS 10.13 (High Sierra)
|
|
241
|
+
Typical development and CI targets:
|
|
132
242
|
|
|
133
|
-
|
|
243
|
+
* **Linux** — recent Ubuntu LTS or comparable distributions (x86_64).
|
|
244
|
+
* **macOS** — recent releases with JRuby and GNU R available.
|
|
245
|
+
* **Windows** — use **WSL2** (same Linux stack as above); native Windows is not the primary target.
|
|
134
246
|
|
|
135
|
-
|
|
136
|
-
* FastR
|
|
247
|
+
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.
|
|
137
248
|
|
|
249
|
+
# Dependencies
|
|
250
|
+
|
|
251
|
+
* **JRuby** — Galaaz 2.0 requires JRuby (tested with **10.1.1.0**) and a matching **JDK** (tested with **Java 21**). MRI Ruby is not supported.
|
|
252
|
+
* **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.
|
|
253
|
+
* **galaaz gem** — runtime dependency `msgpack` is pulled in by `gem install`.
|
|
254
|
+
* Optional: **Docker** — if you run R in a container (common on WSL2); see bootstrap below.
|
|
255
|
+
* 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).
|
|
138
256
|
|
|
139
257
|
# Installation
|
|
140
258
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
259
|
+
The supported install is **`gem install` + compile the gatekeeper**. You do not need a git clone.
|
|
260
|
+
|
|
261
|
+
1. Install **JRuby**, a compatible **JDK**, and **GNU R** (with `Rscript` and a C++ compiler).
|
|
262
|
+
2. In R, install **Rcpp**: `install.packages("Rcpp")`.
|
|
263
|
+
3. Install the gem: `jruby -S gem install galaaz`
|
|
264
|
+
4. Compile the native gatekeeper from the installed gem:
|
|
265
|
+
|
|
266
|
+
```
|
|
267
|
+
gem_dir="$(jruby -e "puts Gem::Specification.find_by_name('galaaz').full_gem_path")"
|
|
268
|
+
make -C "${gem_dir}/ext/new_bridge" all
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
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).
|
|
272
|
+
|
|
273
|
+
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.
|
|
274
|
+
|
|
275
|
+
A **table of all `bin/` scripts** (bootstrap, JRuby wrapper, gstudio, gknit, test runners, and which ones are legacy) is in the section **Command-line tools (`bin/`)** earlier in this manual.
|
|
276
|
+
|
|
277
|
+
### From a repository checkout (contributors)
|
|
278
|
+
|
|
279
|
+
1. Install **bundler** if needed, then run **`jruby -S bundle install`** in the repository root.
|
|
280
|
+
2. Build the bridge native code: **`make -C ext/new_bridge all`** (or **`rake compile_gatekeeper`**).
|
|
281
|
+
3. Run scripts with **`bin/galaaz-jruby`** (sources **`bin/galaaz_jruby_env.inc.sh`** and adds **`-I lib`**).
|
|
282
|
+
|
|
283
|
+
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`**.
|
|
284
|
+
|
|
285
|
+
## Windows + WSL2 (optional: Docker / R in a container)
|
|
286
|
+
|
|
287
|
+
If you run Galaaz on Windows through WSL2 and want containerized R instances,
|
|
288
|
+
Docker Desktop is the supported setup.
|
|
289
|
+
|
|
290
|
+
1. Install Docker Desktop on Windows:
|
|
291
|
+
- https://www.docker.com/products/docker-desktop/
|
|
292
|
+
2. Open Docker Desktop and enable WSL integration:
|
|
293
|
+
- Settings > Resources > WSL Integration
|
|
294
|
+
- Enable integration for your target distro
|
|
295
|
+
- Apply & Restart Docker Desktop
|
|
296
|
+
3. In WSL, run Galaaz bootstrap:
|
|
297
|
+
|
|
298
|
+
> ruby bin/galaaz-bootstrap --apply
|
|
299
|
+
> ruby bin/galaaz-bootstrap --check
|
|
300
|
+
|
|
301
|
+
Expected result:
|
|
302
|
+
- docker CLI available
|
|
303
|
+
- docker compose available
|
|
304
|
+
- docker daemon reachable (`docker info` works)
|
|
305
|
+
|
|
306
|
+
If bootstrap reports daemon is unreachable, check Docker Desktop is running and
|
|
307
|
+
WSL integration is enabled for the distro where Galaaz is installed.
|
|
145
308
|
|
|
146
309
|
# Usage
|
|
147
310
|
|
|
@@ -170,7 +333,7 @@ Galaaz is the Portuguese name for "Galahad". From Wikipedia:
|
|
|
170
333
|
|
|
171
334
|
> galaaz -T
|
|
172
335
|
|
|
173
|
-
Shows a list with all available
|
|
336
|
+
Shows a list with all available executable tasks. To execute a task, substitute the
|
|
174
337
|
'rake' word in the list with 'galaaz'. For instance, the following line shows up
|
|
175
338
|
after 'galaaz -T'
|
|
176
339
|
|
|
@@ -180,20 +343,230 @@ Galaaz is the Portuguese name for "Galahad". From Wikipedia:
|
|
|
180
343
|
|
|
181
344
|
> galaaz master_list:scatter_plot
|
|
182
345
|
|
|
346
|
+
# JRuby, CRuby, multithreading, and the R bridge
|
|
347
|
+
|
|
348
|
+
Galaaz 2.0 supports **JRuby** and **CRuby** equally for NewBridge. On **JRuby**, your
|
|
349
|
+
application can use **real parallel threads** for I/O-bound work (HTTP clients, database
|
|
350
|
+
connections, message consumers, and so on). On **CRuby**, prefer multi-process scaling for
|
|
351
|
+
CPU-bound concurrency. R itself is still executed in a **single GNU R process** behind the
|
|
352
|
+
Galaaz bridge (use multiple R workers when you need more R throughput).
|
|
353
|
+
|
|
354
|
+
When several Ruby threads call into R at the same time, the bridge **serializes** those calls:
|
|
355
|
+
each request is matched to a reply using an internal per-call **queue**, so you do not need to
|
|
356
|
+
add your own mutex around every `R.foo` from application threads. (You should still use normal
|
|
357
|
+
Ruby synchronization when **Ruby** data structures are shared between threads—for example, when
|
|
358
|
+
appending rows from each thread into a shared array before sending them to R.)
|
|
359
|
+
|
|
360
|
+
A practical pattern is:
|
|
361
|
+
|
|
362
|
+
1. Use threads (or a connection pool) to read from **multiple databases or shards** in parallel.
|
|
363
|
+
2. Merge the rows in Ruby under a `Mutex` if you collect into one structure.
|
|
364
|
+
3. Hand the merged table to R **once** (for example with `R::Arrow.from_ruby_batches` and dplyr,
|
|
365
|
+
or by building a data frame) so heavy statistics run in R with fewer bridge round-trips.
|
|
366
|
+
|
|
367
|
+
A runnable sketch lives in
|
|
368
|
+
`examples/multithread_shards_to_r/shards_to_r.rb` (simulated shard queries; swap in your DB
|
|
369
|
+
driver). For concurrency tests on the bridge itself, see `specs/bridge_concurrent_spec.rb` and
|
|
370
|
+
`specs/arrow_from_ruby_batches_spec.rb`.
|
|
371
|
+
|
|
372
|
+
## Long-running R calls and a completion block
|
|
373
|
+
|
|
374
|
+
For R work that can take a long time, the bridge can avoid a Ruby-side **wait timeout** by
|
|
375
|
+
scheduling the call and resuming in a **block** when the `RET` arrives.
|
|
376
|
+
|
|
377
|
+
- **`R.eval_r_async(code, timeout: nil) { |result| ... }`** — string eval; on success, `result.value`
|
|
378
|
+
is the same formatted string as **`R.eval_r`** (use `timeout: nil` for no Ruby-side limit).
|
|
379
|
+
- **`R::Async.<rname>(...) { |result| ... }`** — same dispatch as **`R.<rname>(...)`**, but async;
|
|
380
|
+
on success, `result.value` is an **`R::Object`** (or unboxed Ruby value / Symbol), like synchronous
|
|
381
|
+
**`R.<rname>`**. Optional keyword **`timeout:`** applies a Ruby-side wait limit (completion receives
|
|
382
|
+
**`NewBridge::SessionClient::TimeoutError`** if R is too slow).
|
|
383
|
+
|
|
384
|
+
**Important:** **`R.foo(...) { |x| }`** is already used for dplyr-style scopes (`R::Support.new_scope`),
|
|
385
|
+
so async R calls must use **`R::Async`** or **`R.eval_r_async`**, not a bare **`R.foo` with a block.**
|
|
386
|
+
|
|
387
|
+
`NewBridge::EvalResult` exposes **`#ok?`**, **`#value`**, and **`#error`**. The completion block runs on a
|
|
388
|
+
**background thread** (not the bridge reader thread).
|
|
389
|
+
|
|
390
|
+
The example below is **plain Ruby** (no Rails). The R snippet sleeps (standing in for heavy work) and then
|
|
391
|
+
returns an integer so the success branch shows a **non-nil** value. (`Sys.sleep` alone returns **NULL** in R;
|
|
392
|
+
on success **`result.value`** is then **`nil`** in Ruby—that is expected, not a bridge error.)
|
|
393
|
+
|
|
394
|
+
```{ruby long_r_completion_block}
|
|
395
|
+
require 'thread'
|
|
396
|
+
|
|
397
|
+
completion = Queue.new
|
|
398
|
+
|
|
399
|
+
R.eval_r_async('({ Sys.sleep(0.3); 42L })', timeout: nil) do |result|
|
|
400
|
+
if result.ok?
|
|
401
|
+
puts "[completion] R finished; eval_r-style value: #{result.value.inspect}"
|
|
402
|
+
else
|
|
403
|
+
puts "[completion] R/bridge error: #{result.error.class}: #{result.error.message}"
|
|
404
|
+
end
|
|
405
|
+
completion.push(:done)
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
3.times do |i|
|
|
409
|
+
puts "[main] other Ruby work step #{i + 1}"
|
|
410
|
+
sleep 0.05
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
completion.pop
|
|
414
|
+
puts "[main] R completion has run; exiting."
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
In a **web application**, the HTTP response usually ends before R finishes, so you would not
|
|
418
|
+
`Queue#pop` in the controller; you would persist an identifier, let the completion block write
|
|
419
|
+
the outcome to storage, and notify the client (poll, WebSocket, Turbo Stream, etc.). The plain
|
|
420
|
+
Ruby pattern above is only to show **when** the result exists (inside the block, or after data
|
|
421
|
+
written there is observed elsewhere). Runnable specs live in **`new_bridge_specs/eval_r_async_spec.rb`**.
|
|
422
|
+
|
|
423
|
+
## Galaaz + Rails (R-on-Rails) integration baseline
|
|
424
|
+
|
|
425
|
+
This is the practical **R-on-Rails** starter: an R scientist’s analysis behind a small Rails
|
|
426
|
+
app. The baseline we used in WSL aimed at:
|
|
427
|
+
|
|
428
|
+
1. Rails boots under **JRuby or CRuby** (same bridge; see Installation).
|
|
429
|
+
2. Galaaz is loaded from a local checkout (before publishing to RubyGems).
|
|
430
|
+
3. A request path can execute **`R.eval(...)`** and return a result.
|
|
431
|
+
|
|
432
|
+
### 1) Create the app with Ruby-friendly options
|
|
433
|
+
|
|
434
|
+
Rails defaults can pull gems that are awkward on some setups (for example sqlite native
|
|
435
|
+
extension paths on JRuby, or deployment extras you do not need). A minimal app avoids early friction:
|
|
436
|
+
|
|
437
|
+
```bash
|
|
438
|
+
cd /home/rbotafogo/desenv_linux
|
|
439
|
+
jruby -S rails new hedi --skip-git --minimal --skip-kamal --skip-solid --skip-active-record
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
Then install gems:
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
cd /home/rbotafogo/desenv_linux/hedi
|
|
446
|
+
jruby -S bundle install
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### 2) Use Galaaz as a local path gem
|
|
450
|
+
|
|
451
|
+
For local development we keep a stable path:
|
|
452
|
+
|
|
453
|
+
- `~/gems/galaaz` -> symlink to your Galaaz checkout
|
|
454
|
+
- optional built gem archive in `~/gems/pkg/`
|
|
455
|
+
|
|
456
|
+
In Rails `Gemfile`:
|
|
457
|
+
|
|
458
|
+
```ruby
|
|
459
|
+
gem "galaaz", path: "/home/rbotafogo/gems/galaaz", require: false
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
And load after Rails boot in `config/application.rb`:
|
|
463
|
+
|
|
464
|
+
```ruby
|
|
465
|
+
config.after_initialize { require "galaaz" }
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
Why `require: false` + `after_initialize`? In this integration, loading Galaaz too early via
|
|
469
|
+
`Bundler.require` triggered Rails/JRuby initialization failures.
|
|
470
|
+
|
|
471
|
+
### 3) Simple request-path smoke test
|
|
472
|
+
|
|
473
|
+
A direct smoke test from Rails runner:
|
|
474
|
+
|
|
475
|
+
```bash
|
|
476
|
+
cd /home/rbotafogo/desenv_linux/hedi
|
|
477
|
+
jruby -S bundle exec rails runner "puts R.eval('sum(c(1,2,3,4,5))').inspect"
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
Expected output:
|
|
481
|
+
|
|
482
|
+
```text
|
|
483
|
+
15.0
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### 4) HTTP endpoint pattern
|
|
487
|
+
|
|
488
|
+
For this baseline, a small Rack endpoint was the most stable first step to prove request-time R
|
|
489
|
+
evaluation. (A full ActionController stack can be enabled later as the app evolves.)
|
|
490
|
+
|
|
491
|
+
Minimal pattern:
|
|
492
|
+
|
|
493
|
+
1. Define a Rack app class under `lib/` that runs `R.eval(...)` and returns HTML/JSON.
|
|
494
|
+
2. Point a route to that Rack app (`root to: MyRackApp`).
|
|
495
|
+
3. Verify with browser/curl.
|
|
496
|
+
|
|
497
|
+
### 5) Running from WSL and opening from Windows
|
|
498
|
+
|
|
499
|
+
Recommended bind:
|
|
500
|
+
|
|
501
|
+
```bash
|
|
502
|
+
jruby -S bundle exec rails server -b 0.0.0.0 -p 3000
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Then open from Windows:
|
|
506
|
+
|
|
507
|
+
- `http://localhost:3000` (usually works with WSL localhost forwarding), or
|
|
508
|
+
- `http://<wsl-ip>:3000` if needed.
|
|
509
|
+
|
|
510
|
+
In development, if Host Authorization blocks requests with unexpected Host headers, use:
|
|
511
|
+
|
|
512
|
+
```ruby
|
|
513
|
+
# config/environments/development.rb
|
|
514
|
+
config.hosts.clear
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### 6) Troubleshooting checklist
|
|
518
|
+
|
|
519
|
+
- `Could not find ... in locally installed gems`:
|
|
520
|
+
run `jruby -S bundle install` in the Rails app directory.
|
|
521
|
+
- Stale PID after crash:
|
|
522
|
+
remove `tmp/pids/server.pid`.
|
|
523
|
+
- Local Galaaz path changed:
|
|
524
|
+
verify `Gemfile` path target exists and rerun bundler.
|
|
525
|
+
- R runtime issues:
|
|
526
|
+
confirm GNU R is installed and on `PATH` in the same shell where Rails runs.
|
|
527
|
+
|
|
528
|
+
As new Rails features are added (controllers, jobs, websockets, background rendering, plot
|
|
529
|
+
generation), extend this section with concrete, runnable snippets and the associated operational
|
|
530
|
+
checks.
|
|
183
531
|
|
|
184
532
|
# Accessing R from Ruby
|
|
185
533
|
|
|
186
|
-
One of the nice aspects of Galaaz
|
|
187
|
-
be easily accessed from Ruby. For instance, to access the
|
|
188
|
-
in Ruby, we use the
|
|
189
|
-
value of the
|
|
534
|
+
One of the nice aspects of Galaaz is that variables and functions defined in R can
|
|
535
|
+
be easily accessed from Ruby. For instance, to access the `mtcars` data frame from R
|
|
536
|
+
in Ruby, we use the symbol `:mtcars` preceded by the `~` operator: `~R[:mtcars]` retrieves the
|
|
537
|
+
value of the `mtcars` object in R.
|
|
190
538
|
|
|
191
539
|
```{ruby access_r}
|
|
192
|
-
puts
|
|
540
|
+
puts ~R[:mtcars]
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
## Scoped symbols and lexical scoping
|
|
544
|
+
|
|
545
|
+
Galaaz 2.0 uses **scoped symbols** by default. The canonical style is `R[:name]`:
|
|
546
|
+
|
|
547
|
+
- `~R[:mtcars]` fetches an R object by name.
|
|
548
|
+
- `R[:a] + R[:b]` builds an expression.
|
|
549
|
+
- `R[:year].up_to(R[:day])` builds range expressions.
|
|
550
|
+
|
|
551
|
+
If you prefer the terse `:x` syntax, you can opt in with lexical scoping using a Ruby refinement:
|
|
552
|
+
|
|
553
|
+
```ruby
|
|
554
|
+
module MyScript
|
|
555
|
+
using Galaaz::SymbolDSL
|
|
556
|
+
|
|
557
|
+
def self.run
|
|
558
|
+
expr = :a + :b
|
|
559
|
+
puts expr
|
|
560
|
+
puts ~:mtcars
|
|
561
|
+
end
|
|
562
|
+
end
|
|
193
563
|
```
|
|
194
564
|
|
|
195
|
-
|
|
196
|
-
|
|
565
|
+
`using Galaaz::SymbolDSL` is **lexically scoped**: only code in that module/file scope gets `:x` DSL behavior.
|
|
566
|
+
Outside that scope, plain Ruby `Symbol` behavior is unchanged.
|
|
567
|
+
|
|
568
|
+
To access an R function from Ruby, the R function needs to be preceded by `R.` scoping.
|
|
569
|
+
Below we see an example of creating a R::Vector by calling the 'c' R function
|
|
197
570
|
|
|
198
571
|
```{ruby call_r_func}
|
|
199
572
|
puts vec = R.c(1.0, 2.0, 3.0, 4.0)
|
|
@@ -224,7 +597,7 @@ The call above to the 'c' function can also be done using '.' notation:
|
|
|
224
597
|
```{ruby concat_with_dot}
|
|
225
598
|
puts vec.c(10, 20, 30)
|
|
226
599
|
```
|
|
227
|
-
We will talk about vector indexing in a
|
|
600
|
+
We will talk about vector indexing in a later section. But notice here that indexing
|
|
228
601
|
an R::Vector will return another R::Vector:
|
|
229
602
|
|
|
230
603
|
```{ruby indexing}
|
|
@@ -258,12 +631,12 @@ puts vec.map { |x| x + 2 }
|
|
|
258
631
|
|
|
259
632
|
# gKnitting a Document
|
|
260
633
|
|
|
261
|
-
This manual has been formatted
|
|
262
|
-
a document in Ruby or R and output it in any of the available formats for R
|
|
263
|
-
gKnit runs
|
|
634
|
+
This manual has been formatted using gKnit. gKnit uses knitr and R Markdown to knit
|
|
635
|
+
a document in Ruby or R and output it in any of the available formats for R Markdown.
|
|
636
|
+
gKnit runs with **JRuby**, **GNU R**, and Galaaz. In gKnit, Ruby variables are persisted between
|
|
264
637
|
chunks, making it an ideal solution for literate programming. Also, since it is based
|
|
265
|
-
on Galaaz, Ruby chunks can have access to R variables and
|
|
266
|
-
|
|
638
|
+
on Galaaz, Ruby chunks can have access to R variables and combining Ruby with R in one
|
|
639
|
+
document is natural.
|
|
267
640
|
|
|
268
641
|
The idea of "literate programming" was first introduced by Donald Knuth in the
|
|
269
642
|
1980's [@Knuth:literate_programming].
|
|
@@ -282,7 +655,7 @@ single document or set of documents that when distributed to peers could be reru
|
|
|
282
655
|
the same output and reports.
|
|
283
656
|
|
|
284
657
|
The R community has put a great deal of effort in reproducible research. In 2002, Sweave was
|
|
285
|
-
introduced and it allowed mixing R code with
|
|
658
|
+
introduced and it allowed mixing R code with LaTeX, generating high-quality PDF documents. A
|
|
286
659
|
Sweave document could include code, the results of executing the code, graphics and text
|
|
287
660
|
such that it contained the whole narrative to reproduce the research. In
|
|
288
661
|
2012, Knitr, developed by Yihui Xie from RStudio was released to replace Sweave and to
|
|
@@ -291,7 +664,7 @@ were necessary for Sweave.
|
|
|
291
664
|
|
|
292
665
|
With Knitr, __R markdown__ was also developed, an extension to the
|
|
293
666
|
Markdown format. With __R markdown__ and Knitr it is possible to generate reports in a multitude
|
|
294
|
-
of formats such as HTML,
|
|
667
|
+
of formats such as HTML, Markdown, LaTeX, PDF, DVI, etc. __R markdown__ also allows the use of
|
|
295
668
|
multiple programming languages such as R, Ruby, Python, etc. in the same document.
|
|
296
669
|
|
|
297
670
|
In __R markdown__, text is interspersed with
|
|
@@ -326,7 +699,7 @@ Now, any single code has dozens of variables that we might want to use and reuse
|
|
|
326
699
|
Clearly, such an approach becomes quickly unmanageable. Probably, because of
|
|
327
700
|
this problem, it is very rare to see any __R markdown__ document in the Ruby community.
|
|
328
701
|
|
|
329
|
-
When variables can be used
|
|
702
|
+
When variables can be used across chunks, then no overhead is needed:
|
|
330
703
|
|
|
331
704
|
```{ruby persistence}
|
|
332
705
|
lst = R.list(a: 1, b: 2, c: 3)
|
|
@@ -338,8 +711,8 @@ puts lst
|
|
|
338
711
|
```
|
|
339
712
|
|
|
340
713
|
In the Python community, the same effort to have code and text in an integrated environment
|
|
341
|
-
started around the first decade of
|
|
342
|
-
Fernando Pérez
|
|
714
|
+
started around the first decade of the 2000s. In 2006 IPython 0.7.2 was released. In 2014,
|
|
715
|
+
Fernando Pérez spun off the Jupyter project from IPython, creating a web-based interactive
|
|
343
716
|
computation environment. Jupyter can now be used with many languages, including Ruby with the
|
|
344
717
|
iruby gem (https://github.com/SciRuby/iruby). In order to have multiple languages in a Jupyter
|
|
345
718
|
notebook the SoS kernel was developed (https://vatlab.github.io/sos-docs/).
|
|
@@ -353,8 +726,8 @@ have in a single document, text and code.
|
|
|
353
726
|
|
|
354
727
|
In gKnit, Ruby variables are persisted between
|
|
355
728
|
chunks, making it an ideal solution for literate programming in this language. Also,
|
|
356
|
-
since it is based on
|
|
357
|
-
|
|
729
|
+
since it is based on Galaaz, Ruby chunks can access R variables (`~R[:name]`, `R.*`) through the
|
|
730
|
+
**Galaaz bridge** while knitr drives **GNU R**—no GraalVM polyglot runtime is required.
|
|
358
731
|
|
|
359
732
|
This is not a blog post on __R markdown__, and the interested user is directed to the following links
|
|
360
733
|
for detailed information on its capabilities and use.
|
|
@@ -368,7 +741,7 @@ gKnitting Ruby and R documents quickly.
|
|
|
368
741
|
## The Yaml header
|
|
369
742
|
|
|
370
743
|
An __R markdown__ document should start with a Yaml header and be stored in a file with
|
|
371
|
-
'.Rmd' extension. This document has the following header for
|
|
744
|
+
'.Rmd' extension. This document has the following header for gKnitting an HTML document.
|
|
372
745
|
|
|
373
746
|
```
|
|
374
747
|
---
|
|
@@ -376,7 +749,7 @@ title: "How to do reproducible research in Ruby with gKnit"
|
|
|
376
749
|
author:
|
|
377
750
|
- "Rodrigo Botafogo"
|
|
378
751
|
- "Daniel Mossé - University of Pittsburgh"
|
|
379
|
-
tags: [Tech, Data Science, Ruby, R,
|
|
752
|
+
tags: [Tech, Data Science, Ruby, R, JRuby, Galaaz]
|
|
380
753
|
date: "20/02/2019"
|
|
381
754
|
output:
|
|
382
755
|
html_document:
|
|
@@ -391,6 +764,35 @@ output:
|
|
|
391
764
|
|
|
392
765
|
For more information on the options in the Yaml header, [check here](https://bookdown.org/yihui/rmarkdown/html-document.html).
|
|
393
766
|
|
|
767
|
+
## Choosing the output format when calling gknit
|
|
768
|
+
|
|
769
|
+
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`**.
|
|
770
|
+
|
|
771
|
+
* **`--output_format FORMAT`** — name of the format, as in the YAML `output:` block. Examples:
|
|
772
|
+
* **`html_document`** — HTML (often the default you list first under `output:`).
|
|
773
|
+
* **`pdf_document`** — PDF (you need a working LaTeX setup, e.g. TinyTeX; see **`bin/install-tinytex`**).
|
|
774
|
+
* **`md_document`**, **`github_document`**, or any other format defined in your YAML.
|
|
775
|
+
* **`all`** — render **every** format declared under `output:` in the document (same idea as in R Markdown).
|
|
776
|
+
|
|
777
|
+
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).
|
|
778
|
+
|
|
779
|
+
Other useful flags:
|
|
780
|
+
|
|
781
|
+
* **`--output_file NAME`** — output file name (optional path; see also **`--output_dir`**).
|
|
782
|
+
* **`--output_dir DIR`** — directory for the rendered file (created if missing).
|
|
783
|
+
* **`--bridge_timeout_sec`** / **`--callback_timeout_ms`** — longer R or install steps (see elsewhere in this manual).
|
|
784
|
+
|
|
785
|
+
Examples (run from the directory where paths make sense, or use absolute paths):
|
|
786
|
+
|
|
787
|
+
```text
|
|
788
|
+
bin/gknit blogs/manual/manual.Rmd
|
|
789
|
+
bin/gknit --output_format html_document blogs/manual/manual.Rmd
|
|
790
|
+
bin/gknit --output_format pdf_document blogs/manual/manual.Rmd
|
|
791
|
+
bin/gknit --output_format all blogs/manual/manual.Rmd
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
Use **`gknit -h`** for the full option list.
|
|
795
|
+
|
|
394
796
|
## __R Markdown__ formatting
|
|
395
797
|
|
|
396
798
|
Document formatting can be done with simple markups such as:
|
|
@@ -435,7 +837,7 @@ Running and executing Ruby and R code is actually what really interests us is th
|
|
|
435
837
|
Inserting a code chunk is done by adding code in a block delimited by three back ticks
|
|
436
838
|
followed by an open
|
|
437
839
|
curly brace ('{') followed with the engine name (r, ruby, rb, include, ...), an
|
|
438
|
-
any optional chunk_label and options, as shown
|
|
840
|
+
any optional chunk_label and options, as shown below:
|
|
439
841
|
|
|
440
842
|
````
|
|
441
843
|
```{engine_name [chunk_label], [chunk_options]}`r ''`
|
|
@@ -524,7 +926,7 @@ grammar of graphics" [@Wilkinson:grammar_of_graphics]. The idea of the grammar o
|
|
|
524
926
|
is to build a graphics by adding layers to the plot. More information can be found in
|
|
525
927
|
https://towardsdatascience.com/a-comprehensive-guide-to-the-grammar-of-graphics-for-effective-visualization-of-multi-dimensional-1f92b4ed4149.
|
|
526
928
|
|
|
527
|
-
In the plot
|
|
929
|
+
In the plot below the 'mpg' dataset from base R is used. "The data concerns city-cycle fuel
|
|
528
930
|
consumption in miles per gallon, to be predicted in terms of 3 multivalued discrete and 5
|
|
529
931
|
continuous attributes." (Quinlan, 1993)
|
|
530
932
|
|
|
@@ -713,17 +1115,17 @@ Here, for instance, is a table definition in HTML and its output in the document
|
|
|
713
1115
|
</div>
|
|
714
1116
|
|
|
715
1117
|
But manually creating HTML output is not always easy or desirable, specially
|
|
716
|
-
if we intend the document to be rendered in other formats, for example, as
|
|
1118
|
+
if we intend the document to be rendered in other formats, for example, as LaTeX.
|
|
717
1119
|
Also, The above
|
|
718
1120
|
table looks ugly. The 'kableExtra' library is a great library for
|
|
719
1121
|
creating beautiful tables. Take a look at https://cran.r-project.org/web/packages/kableExtra/vignettes/awesome_table_in_html.html
|
|
720
1122
|
|
|
721
1123
|
In the next chunk, we output the 'mtcars' dataframe from R in a nicely formatted
|
|
722
|
-
table. Note that we retrieve the mtcars dataframe by using '
|
|
1124
|
+
table. Note that we retrieve the mtcars dataframe by using '~R[:mtcars]'.
|
|
723
1125
|
|
|
724
1126
|
```{ruby nice_table}
|
|
725
1127
|
R.install_and_loads('kableExtra')
|
|
726
|
-
outputs (
|
|
1128
|
+
outputs (~R[:mtcars]).kable.kable_styling
|
|
727
1129
|
```
|
|
728
1130
|
|
|
729
1131
|
## Including Ruby files in a chunk
|
|
@@ -751,7 +1153,7 @@ true, ruby's 'require\_relative' semantics is used to load the file, when false,
|
|
|
751
1153
|
```
|
|
752
1154
|
````
|
|
753
1155
|
|
|
754
|
-
|
|
1156
|
+
Below we include file 'model.rb', which is in the same directory of this blog.
|
|
755
1157
|
This code uses R 'caret' package to split a dataset in a train and test sets.
|
|
756
1158
|
The 'caret' package is a very important a useful package for doing Data Analysis,
|
|
757
1159
|
it has hundreds of functions for all steps of the Data Analysis workflow. To
|
|
@@ -772,7 +1174,7 @@ will install the package if it is not already installed and can take a while.
|
|
|
772
1174
|
```
|
|
773
1175
|
|
|
774
1176
|
```{ruby model_partition}
|
|
775
|
-
mtcars =
|
|
1177
|
+
mtcars = ~R[:mtcars]
|
|
776
1178
|
model = Model.new(mtcars, percent_train: 0.8)
|
|
777
1179
|
model.partition(:mpg)
|
|
778
1180
|
puts model.train.head
|
|
@@ -784,9 +1186,9 @@ puts model.test.head
|
|
|
784
1186
|
gKnit also allows developers to document and load files that are not in the same directory
|
|
785
1187
|
of the '.Rmd' file.
|
|
786
1188
|
|
|
787
|
-
Here is an example of loading
|
|
788
|
-
is set to FALSE, so Ruby will look for the file in its
|
|
789
|
-
need to
|
|
1189
|
+
Here is an example of loading Ruby’s standard library file `find.rb`. In this example, relative
|
|
1190
|
+
is set to FALSE, so Ruby will look for the file in its `$LOAD_PATH`, and the user does not
|
|
1191
|
+
need to know its directory on disk.
|
|
790
1192
|
|
|
791
1193
|
````
|
|
792
1194
|
```{include find, relative = FALSE}`r ''`
|
|
@@ -808,9 +1210,9 @@ the Yaml header to generate this blog in PDF format instead of HTML:
|
|
|
808
1210
|
|
|
809
1211
|
```
|
|
810
1212
|
---
|
|
811
|
-
title: "gKnit - Ruby and R Knitting with Galaaz
|
|
1213
|
+
title: "gKnit - Ruby and R Knitting with Galaaz"
|
|
812
1214
|
author: "Rodrigo Botafogo"
|
|
813
|
-
tags: [Galaaz, Ruby, R,
|
|
1215
|
+
tags: [Galaaz, Ruby, R, JRuby, knitr, gknit]
|
|
814
1216
|
date: "29 October 2018"
|
|
815
1217
|
output:
|
|
816
1218
|
pdf\_document:
|
|
@@ -822,7 +1224,7 @@ output:
|
|
|
822
1224
|
|
|
823
1225
|
## Template based documents generation
|
|
824
1226
|
|
|
825
|
-
When a document is converted to PDF it follows a certain
|
|
1227
|
+
When a document is converted to PDF it follows a certain conversion template. We've seen above
|
|
826
1228
|
the use of 'galaaz.sty' as a basic template to generate a PDF document. Using the
|
|
827
1229
|
'gknit-draft' app that comes with Galaaz, the same .Rmd file can be compiled to different
|
|
828
1230
|
looking PDF documents. Galaaz automatically loads the 'rticles' R package that comes with
|
|
@@ -872,14 +1274,14 @@ gknit-draft --filename my_r_article --template rjournal_article --package rticle
|
|
|
872
1274
|
|
|
873
1275
|
# Accessing R variables
|
|
874
1276
|
|
|
875
|
-
Galaaz allows Ruby to access variables created in R. For example, the
|
|
876
|
-
available in R and can be accessed from Ruby by using the
|
|
877
|
-
symbol for the variable, in this case
|
|
878
|
-
used to output the
|
|
879
|
-
|
|
1277
|
+
Galaaz allows Ruby to access variables created in R. For example, the `mtcars` data set is
|
|
1278
|
+
available in R and can be accessed from Ruby by using the tilde operator followed by the
|
|
1279
|
+
symbol for the variable, in this case `:mtcars`. In the code below, method `outputs` is
|
|
1280
|
+
used to output the `mtcars` data set nicely formatted in HTML by use of the `kable` and
|
|
1281
|
+
`kable_styling` functions. Method `outputs` is only available when used with gKnit.
|
|
880
1282
|
|
|
881
1283
|
```{ruby view_kable}
|
|
882
|
-
outputs (
|
|
1284
|
+
outputs (~R[:mtcars]).kable.kable_styling
|
|
883
1285
|
```
|
|
884
1286
|
|
|
885
1287
|
# Basic Data Types
|
|
@@ -897,7 +1299,7 @@ table.
|
|
|
897
1299
|
| logical | logical | logical |
|
|
898
1300
|
| integer | numeric | integer |
|
|
899
1301
|
| double | numeric | double |
|
|
900
|
-
| complex | complex |
|
|
1302
|
+
| complex | complex | complex |
|
|
901
1303
|
| character | character | character |
|
|
902
1304
|
| raw | raw | raw |
|
|
903
1305
|
|
|
@@ -974,7 +1376,7 @@ In this next example, method 'c' is chainned after 'vec1'. This also looks like
|
|
|
974
1376
|
method of the vector, but in reallity, this is actually closer to the pipe operator. When
|
|
975
1377
|
Galaaz identifies that 'c' is not a method of 'vec' it actually tries to call 'R.c' with
|
|
976
1378
|
'vec1' as the first argument concatenated with all the other available arguments. The code
|
|
977
|
-
|
|
1379
|
+
below is automatically converted to the code above.
|
|
978
1380
|
|
|
979
1381
|
```{ruby chainning_methods}
|
|
980
1382
|
vec = vec1.c(vec2)
|
|
@@ -1008,7 +1410,7 @@ Vectors can be indexed by using the '[]' operator:
|
|
|
1008
1410
|
puts vec4[3]
|
|
1009
1411
|
```
|
|
1010
1412
|
|
|
1011
|
-
We can also index a vector with another vector. For example, in the code
|
|
1413
|
+
We can also index a vector with another vector. For example, in the code below, we take elements
|
|
1012
1414
|
1, 3, 5, and 7 from vec3:
|
|
1013
1415
|
|
|
1014
1416
|
```{ruby index_by_vector}
|
|
@@ -1179,7 +1581,7 @@ operator) and then the vector was indexed by its first element, extracting the n
|
|
|
1179
1581
|
|
|
1180
1582
|
A data frame is a table like structure in which each column has the same number of
|
|
1181
1583
|
rows. Data frames are the basic structure for storing data for data analysis. We have already
|
|
1182
|
-
seen a data frame previously when we accessed variable '
|
|
1584
|
+
seen a data frame previously when we accessed variable '~R[:mtcars]'. In order to create a
|
|
1183
1585
|
data frame, function 'data__frame' is used:
|
|
1184
1586
|
|
|
1185
1587
|
```{ruby dataframe}
|
|
@@ -1196,45 +1598,45 @@ A data frame can be indexed the same way as a matrix, by using '[row, column]',
|
|
|
1196
1598
|
column can either be a numeric or the name of the row or column
|
|
1197
1599
|
|
|
1198
1600
|
```{ruby dataframe_index}
|
|
1199
|
-
puts (
|
|
1200
|
-
puts (
|
|
1201
|
-
puts (
|
|
1601
|
+
puts (~R[:mtcars]).head
|
|
1602
|
+
puts (~R[:mtcars])[1, 2]
|
|
1603
|
+
puts (~R[:mtcars])['Datsun 710', 'mpg']
|
|
1202
1604
|
```
|
|
1203
1605
|
|
|
1204
1606
|
Extracting a column from a data frame as a vector can be done by using the double square bracket
|
|
1205
1607
|
operator:
|
|
1206
1608
|
|
|
1207
1609
|
```{ruby dataframe_column}
|
|
1208
|
-
puts (
|
|
1610
|
+
puts (~R[:mtcars])[['mpg']]
|
|
1209
1611
|
```
|
|
1210
1612
|
|
|
1211
1613
|
A data frame column can also be accessed as if it were an instance variable of the data frame:
|
|
1212
1614
|
|
|
1213
1615
|
```{ruby dataframe_instance_variable}
|
|
1214
|
-
puts (
|
|
1616
|
+
puts (~R[:mtcars]).mpg
|
|
1215
1617
|
```
|
|
1216
1618
|
|
|
1217
1619
|
Slicing a data frame can be done by indexing it with a vector (we use 'head' to reduce the
|
|
1218
1620
|
output):
|
|
1219
1621
|
|
|
1220
1622
|
```{ruby dataframe_column_slice}
|
|
1221
|
-
puts (
|
|
1623
|
+
puts (~R[:mtcars])[R.c('mpg', 'hp')].head
|
|
1222
1624
|
```
|
|
1223
1625
|
|
|
1224
1626
|
A row slice can be obtained by indexing by row and using the ':all' keyword for the column:
|
|
1225
1627
|
|
|
1226
1628
|
```{ruby dataframe_row_slice}
|
|
1227
|
-
puts (
|
|
1629
|
+
puts (~R[:mtcars])[R.c('Datsun 710', 'Camaro Z28'), :all]
|
|
1228
1630
|
```
|
|
1229
1631
|
|
|
1230
1632
|
Finally, a data frame can also be indexed with a logical vector. In this next example, the
|
|
1231
1633
|
'am' column of :mtcars is compared with 0 (with method 'eq'). When 'am' is equal to 0 the
|
|
1232
|
-
car is automatic. So, by doing '(
|
|
1634
|
+
car is automatic. So, by doing '(~R[:mtcars]).am.eq 0' a logical vector is created with
|
|
1233
1635
|
'true' whenever 'am' is 0 and 'false' otherwise.
|
|
1234
1636
|
|
|
1235
1637
|
```{ruby logical_vector_filter}
|
|
1236
1638
|
# obtain a vector with 'true' for cars with automatic transmission
|
|
1237
|
-
automatic = (
|
|
1639
|
+
automatic = (~R[:mtcars]).am.eq 0
|
|
1238
1640
|
puts automatic
|
|
1239
1641
|
```
|
|
1240
1642
|
|
|
@@ -1243,7 +1645,7 @@ which all cars have automatic transmission.
|
|
|
1243
1645
|
|
|
1244
1646
|
```{ruby dataframe_logical}
|
|
1245
1647
|
# slice the data frame by using this vector
|
|
1246
|
-
puts (
|
|
1648
|
+
puts (~R[:mtcars])[automatic, :all]
|
|
1247
1649
|
```
|
|
1248
1650
|
|
|
1249
1651
|
# Writing Expressions in Galaaz
|
|
@@ -1253,24 +1655,24 @@ Galaaz extends Ruby to work with complex expressions, similar to R's expressions
|
|
|
1253
1655
|
|
|
1254
1656
|
## Expressions from operators
|
|
1255
1657
|
|
|
1256
|
-
The code
|
|
1658
|
+
The code below
|
|
1257
1659
|
creates an expression summing two symbols
|
|
1258
1660
|
|
|
1259
1661
|
```{ruby expressions}
|
|
1260
|
-
exp1 = :a + :b
|
|
1662
|
+
exp1 = R[:a] + R[:b]
|
|
1261
1663
|
puts exp1
|
|
1262
1664
|
```
|
|
1263
1665
|
We can build any complex mathematical expression
|
|
1264
1666
|
|
|
1265
1667
|
```{ruby expr2}
|
|
1266
|
-
exp2 = (:a + :b) * 2.0 + :c ** 2 / :z
|
|
1668
|
+
exp2 = (R[:a] + R[:b]) * 2.0 + R[:c] ** 2 / R[:z]
|
|
1267
1669
|
puts exp2
|
|
1268
1670
|
```
|
|
1269
1671
|
|
|
1270
1672
|
It is also possible to use inequality operators in building expressions
|
|
1271
1673
|
|
|
1272
1674
|
```{ruby expr3}
|
|
1273
|
-
exp3 = (:a + :b) >= :z
|
|
1675
|
+
exp3 = (R[:a] + R[:b]) >= :z
|
|
1274
1676
|
puts exp3
|
|
1275
1677
|
```
|
|
1276
1678
|
|
|
@@ -1279,7 +1681,7 @@ notation for those operators such as (.gt, .ge, etc.). So the same expression w
|
|
|
1279
1681
|
above can also be written as
|
|
1280
1682
|
|
|
1281
1683
|
```{ruby expr4}
|
|
1282
|
-
exp4 = (:a + :b).ge :z
|
|
1684
|
+
exp4 = (R[:a] + R[:b]).ge :z
|
|
1283
1685
|
puts exp4
|
|
1284
1686
|
```
|
|
1285
1687
|
|
|
@@ -1288,23 +1690,23 @@ those are expressions involving '==', and '='. In order to write an expression
|
|
|
1288
1690
|
need to use the method '.eq' and for '=' we need the function '.assign'
|
|
1289
1691
|
|
|
1290
1692
|
```{ruby expr5}
|
|
1291
|
-
exp5 = (:a + :b).eq :z
|
|
1693
|
+
exp5 = (R[:a] + R[:b]).eq :z
|
|
1292
1694
|
puts exp5
|
|
1293
1695
|
```
|
|
1294
1696
|
|
|
1295
1697
|
```{ruby expr6}
|
|
1296
|
-
exp6 = :y.assign :a + :b
|
|
1698
|
+
exp6 = R[:y].assign R[:a] + R[:b]
|
|
1297
1699
|
puts exp6
|
|
1298
1700
|
```
|
|
1299
1701
|
In general we think that using the functional notation is preferable to using the
|
|
1300
1702
|
symbolic notation as otherwise, we end up writing invalid expressions such as
|
|
1301
1703
|
|
|
1302
1704
|
```{ruby exp_wrong, warning=FALSE, eval=FALSE}
|
|
1303
|
-
exp_wrong = (:a + :b) == :z
|
|
1705
|
+
exp_wrong = (R[:a] + R[:b]) == :z
|
|
1304
1706
|
puts exp_wrong
|
|
1305
1707
|
```
|
|
1306
1708
|
and it might be difficult to understand what is going on here. The problem lies with the fact that
|
|
1307
|
-
when using '==' we are comparing expression (:a + :b) to expression :z with '=='. When the
|
|
1709
|
+
when using '==' we are comparing expression (R[:a] + R[:b]) to expression :z with '=='. When the
|
|
1308
1710
|
comparison is executed, the system tries to evaluate :a, :b and :z, and those symbols at
|
|
1309
1711
|
this time are not bound to anything and we get a "object 'a' not found" message.
|
|
1310
1712
|
If we only use functional notation, this type of error will not occur.
|
|
@@ -1319,21 +1721,21 @@ When we want the function to be part of the expression, we call the function pre
|
|
|
1319
1721
|
by the letter E, such as 'E.sin(x)'
|
|
1320
1722
|
|
|
1321
1723
|
```{ruby method_expression}
|
|
1322
|
-
exp7 = :y.assign E.sin(:x)
|
|
1724
|
+
exp7 = R[:y].assign E.sin(R[:x])
|
|
1323
1725
|
puts exp7
|
|
1324
1726
|
```
|
|
1325
1727
|
|
|
1326
1728
|
Expressions can also be written using '.' notation:
|
|
1327
1729
|
|
|
1328
1730
|
```{ruby expression_with_dot}
|
|
1329
|
-
exp8 = :y.assign :x.sin
|
|
1731
|
+
exp8 = R[:y].assign R[:x].sin
|
|
1330
1732
|
puts exp8
|
|
1331
1733
|
```
|
|
1332
1734
|
|
|
1333
1735
|
When a function has multiple arguments, the first one can be used before the '.':
|
|
1334
1736
|
|
|
1335
1737
|
```{ruby expression_multiple_args}
|
|
1336
|
-
exp9 = :x.c(:y)
|
|
1738
|
+
exp9 = R[:x].c(R[:y])
|
|
1337
1739
|
puts exp9
|
|
1338
1740
|
```
|
|
1339
1741
|
|
|
@@ -1343,7 +1745,7 @@ Expressions can be evaluated by calling function 'eval' with a binding. A bindin
|
|
|
1343
1745
|
with a list:
|
|
1344
1746
|
|
|
1345
1747
|
```{ruby eval_expression_list}
|
|
1346
|
-
exp = (:a + :b) * 2.0 + :c ** 2 / :z
|
|
1748
|
+
exp = (R[:a] + R[:b]) * 2.0 + R[:c] ** 2 / R[:z]
|
|
1347
1749
|
puts exp.eval(R.list(a: 10, b: 20, c: 30, z: 40))
|
|
1348
1750
|
```
|
|
1349
1751
|
|
|
@@ -1362,7 +1764,7 @@ puts exp.eval(df)
|
|
|
1362
1764
|
# Manipulating Data
|
|
1363
1765
|
|
|
1364
1766
|
One of the major benefits of Galaaz is to bring strong data manipulation to Ruby. The following
|
|
1365
|
-
examples were extracted from
|
|
1767
|
+
examples were extracted from Hadley's "R for Data Science" (https://r4ds.had.co.nz/). This
|
|
1366
1768
|
is a highly recommended book for those not already familiar with the 'tidyverse' style of
|
|
1367
1769
|
programming in R. In the sections to follow, we will limit ourselves to convert the R code to
|
|
1368
1770
|
Galaaz.
|
|
@@ -1374,9 +1776,9 @@ locally, and if not, installs it. This data frame contains all 336,776 flights t
|
|
|
1374
1776
|
departed from New York City in 2013. The data comes from the US Bureau of
|
|
1375
1777
|
Transportation Statistics.
|
|
1376
1778
|
|
|
1377
|
-
Dplyr uses
|
|
1378
|
-
|
|
1379
|
-
|
|
1779
|
+
Dplyr often uses **tibbles** in place of classic data frames. In Galaaz, printing may differ from
|
|
1780
|
+
the R console; if you need a classic tabular printout, convert with **`as__data__frame`** (or use
|
|
1781
|
+
`head` / `str` in R via `R` calls).
|
|
1380
1782
|
|
|
1381
1783
|
```{ruby nycflights13}
|
|
1382
1784
|
R.install_and_loads('nycflights13')
|
|
@@ -1384,17 +1786,17 @@ R.library('dplyr')
|
|
|
1384
1786
|
```
|
|
1385
1787
|
|
|
1386
1788
|
```{ruby flights}
|
|
1387
|
-
flights =
|
|
1789
|
+
flights = ~R[:flights]
|
|
1388
1790
|
puts flights.head
|
|
1389
1791
|
```
|
|
1390
1792
|
|
|
1391
1793
|
## Filtering rows with Filter
|
|
1392
1794
|
|
|
1393
1795
|
In this example we filter the flights data set by giving to the filter function two expressions:
|
|
1394
|
-
the first :month.eq 1
|
|
1796
|
+
the first R[:month].eq 1
|
|
1395
1797
|
|
|
1396
1798
|
```{ruby filter_rows}
|
|
1397
|
-
puts flights.filter((:month.eq 1), (:day.eq 1)).head
|
|
1799
|
+
puts flights.filter((R[:month].eq 1), (R[:day].eq 1)).head
|
|
1398
1800
|
```
|
|
1399
1801
|
|
|
1400
1802
|
## Logical Operators
|
|
@@ -1402,7 +1804,7 @@ puts flights.filter((:month.eq 1), (:day.eq 1)).head
|
|
|
1402
1804
|
All flights that departed in November of December
|
|
1403
1805
|
|
|
1404
1806
|
```{ruby nov_dec}
|
|
1405
|
-
puts flights.filter((:month.eq 11) | (:month.eq 12)).head
|
|
1807
|
+
puts flights.filter((R[:month].eq 11) | (R[:month].eq 12)).head
|
|
1406
1808
|
```
|
|
1407
1809
|
|
|
1408
1810
|
The same as above, but using the 'in' operator. In R, it is possible to define many operators
|
|
@@ -1411,7 +1813,7 @@ operators from Galaaz the '._' method is used, where the first argument is the o
|
|
|
1411
1813
|
symbol, in this case ':in' and the second argument is the vector:
|
|
1412
1814
|
|
|
1413
1815
|
```{ruby in_op}
|
|
1414
|
-
puts flights.filter(:month._ :in, R.c(11, 12)).head
|
|
1816
|
+
puts flights.filter(R[:month]._ :in, R.c(11, 12)).head
|
|
1415
1817
|
```
|
|
1416
1818
|
|
|
1417
1819
|
## Filtering with NA (Not Available)
|
|
@@ -1430,13 +1832,13 @@ Now filtering by :x > 1 shows all lines that satisfy this condition, where the r
|
|
|
1430
1832
|
not.
|
|
1431
1833
|
|
|
1432
1834
|
```{ruby filter_na}
|
|
1433
|
-
puts df.filter(:x > 1)
|
|
1835
|
+
puts df.filter(R[:x] > 1)
|
|
1434
1836
|
```
|
|
1435
1837
|
|
|
1436
1838
|
To match an NA use method 'is__na'
|
|
1437
1839
|
|
|
1438
1840
|
```{ruby with_na}
|
|
1439
|
-
puts df.filter((:x.is__na) | (:x > 1))
|
|
1841
|
+
puts df.filter((R[:x].is__na) | (R[:x] > 1))
|
|
1440
1842
|
```
|
|
1441
1843
|
|
|
1442
1844
|
## Arrange Rows with arrange
|
|
@@ -1450,7 +1852,7 @@ puts flights.arrange(:year, :month, :day).head
|
|
|
1450
1852
|
To arrange in descending order, use function 'desc'
|
|
1451
1853
|
|
|
1452
1854
|
```{ruby desc_arrange}
|
|
1453
|
-
puts flights.arrange(:dep_delay.desc).head
|
|
1855
|
+
puts flights.arrange(R[:dep_delay].desc).head
|
|
1454
1856
|
```
|
|
1455
1857
|
|
|
1456
1858
|
## Selecting columns
|
|
@@ -1464,7 +1866,7 @@ puts flights.select(:year, :month, :day).head
|
|
|
1464
1866
|
It is also possible to select column in a given range
|
|
1465
1867
|
|
|
1466
1868
|
```{ruby select_range}
|
|
1467
|
-
puts flights.select(:year.up_to
|
|
1869
|
+
puts flights.select(R[:year].up_to(R[:day])).head
|
|
1468
1870
|
```
|
|
1469
1871
|
|
|
1470
1872
|
Select all columns that start with a given name sequence
|
|
@@ -1494,7 +1896,7 @@ puts flights.select(:year, :month, :day, E.everything).head
|
|
|
1494
1896
|
|
|
1495
1897
|
```{ruby small_flights}
|
|
1496
1898
|
flights_sm = flights.
|
|
1497
|
-
select((:year.up_to
|
|
1899
|
+
select((R[:year].up_to(R[:day])),
|
|
1498
1900
|
E.ends_with('delay'),
|
|
1499
1901
|
:distance,
|
|
1500
1902
|
:air_time)
|
|
@@ -1504,8 +1906,8 @@ puts flights_sm.head
|
|
|
1504
1906
|
|
|
1505
1907
|
```{ruby mutate}
|
|
1506
1908
|
flights_sm = flights_sm.
|
|
1507
|
-
mutate(gain: :dep_delay - :arr_delay,
|
|
1508
|
-
speed: :distance / :air_time * 60)
|
|
1909
|
+
mutate(gain: R[:dep_delay] - R[:arr_delay],
|
|
1910
|
+
speed: R[:distance] / R[:air_time] * 60)
|
|
1509
1911
|
puts flights_sm.head
|
|
1510
1912
|
```
|
|
1511
1913
|
|
|
@@ -1522,7 +1924,7 @@ When a data frame is grouped with 'group_by' summaries apply to the given group:
|
|
|
1522
1924
|
|
|
1523
1925
|
```{ruby summarise_group_by}
|
|
1524
1926
|
by_day = flights.group_by(:year, :month, :day)
|
|
1525
|
-
puts by_day.summarise(delay: :dep_delay.mean(na__rm: true)).head
|
|
1927
|
+
puts by_day.summarise(delay: R[:dep_delay].mean(na__rm: true)).head
|
|
1526
1928
|
```
|
|
1527
1929
|
|
|
1528
1930
|
Next we put many operations together by pipping them one after the other:
|
|
@@ -1532,23 +1934,25 @@ delays = flights.
|
|
|
1532
1934
|
group_by(:dest).
|
|
1533
1935
|
summarise(
|
|
1534
1936
|
count: E.n,
|
|
1535
|
-
dist: :distance.mean(na__rm: true),
|
|
1536
|
-
delay: :arr_delay.mean(na__rm: true)).
|
|
1537
|
-
filter(:count > 20, :dest != "NHL")
|
|
1937
|
+
dist: R[:distance].mean(na__rm: true),
|
|
1938
|
+
delay: R[:arr_delay].mean(na__rm: true)).
|
|
1939
|
+
filter(R[:count] > 20, R[:dest] != "NHL")
|
|
1538
1940
|
|
|
1539
1941
|
puts delays.head
|
|
1540
1942
|
```
|
|
1541
1943
|
|
|
1542
1944
|
# Using Data Table
|
|
1543
1945
|
|
|
1946
|
+
The next chunk converts the **nycflights13** `flights` tibble already loaded above into a
|
|
1947
|
+
**`data.table`**. That keeps the manual offline and avoids downloading a remote CSV during gknit
|
|
1948
|
+
(network stalls look like bridge hangs when the transfer runs inside a single R eval).
|
|
1949
|
+
|
|
1544
1950
|
```{ruby fread}
|
|
1545
1951
|
R.library('data.table')
|
|
1546
|
-
R.install_and_loads('curl')
|
|
1547
1952
|
|
|
1548
|
-
|
|
1549
|
-
flights = R.fread(input)
|
|
1550
|
-
puts flights
|
|
1953
|
+
flights = R.as__data__table(~R[:flights])
|
|
1551
1954
|
puts flights.dim
|
|
1955
|
+
puts R.head(flights, 12)
|
|
1552
1956
|
```
|
|
1553
1957
|
|
|
1554
1958
|
```{ruby data_table}
|
|
@@ -1566,7 +1970,7 @@ puts data_table.ID
|
|
|
1566
1970
|
|
|
1567
1971
|
```{ruby subset_i}
|
|
1568
1972
|
# subset rows in i
|
|
1569
|
-
ans = flights[(:origin.eq "JFK") & (:month.eq 6)]
|
|
1973
|
+
ans = flights[(R[:origin].eq "JFK") & (R[:month].eq 6)]
|
|
1570
1974
|
puts ans.head
|
|
1571
1975
|
|
|
1572
1976
|
# Get the first two rows from flights.
|
|
@@ -1574,8 +1978,7 @@ puts ans.head
|
|
|
1574
1978
|
ans = flights[(1..2)]
|
|
1575
1979
|
puts ans
|
|
1576
1980
|
|
|
1577
|
-
# Sort
|
|
1578
|
-
|
|
1981
|
+
# Sort by origin asc, then dest desc (example kept commented):
|
|
1579
1982
|
# ans = flights[E.order(:origin, -(:dest))]
|
|
1580
1983
|
# puts ans.head
|
|
1581
1984
|
|
|
@@ -1588,66 +1991,274 @@ puts ans
|
|
|
1588
1991
|
ans = flights[:all, :arr_delay]
|
|
1589
1992
|
puts ans.head
|
|
1590
1993
|
|
|
1591
|
-
#
|
|
1994
|
+
# arr_delay as data.table (not plain vector).
|
|
1592
1995
|
|
|
1593
|
-
ans = flights[:all, :arr_delay.list]
|
|
1996
|
+
ans = flights[:all, R[:arr_delay].list]
|
|
1594
1997
|
puts ans.head
|
|
1595
1998
|
|
|
1596
|
-
ans = flights[:all, E.list(:arr_delay, :dep_delay)]
|
|
1999
|
+
ans = flights[:all, E.list(R[:arr_delay], R[:dep_delay])]
|
|
2000
|
+
```
|
|
2001
|
+
|
|
2002
|
+
# Apache Arrow
|
|
2003
|
+
|
|
2004
|
+
[Apache Arrow](https://arrow.apache.org/) is a **columnar** in-memory format used heavily in R
|
|
2005
|
+
and Python for analytics. In Galaaz, **Ruby does not hold an Arrow C++ table itself**; instead you
|
|
2006
|
+
build ordinary Ruby structures (arrays of row hashes), and **`R::Arrow.from_ruby_batches`** creates
|
|
2007
|
+
a real **Arrow `Table` inside GNU R**. From there you use R’s **`arrow`** and **`dplyr`** packages
|
|
2008
|
+
as usual: **`group_by`** on the Arrow table, **`summarise`** for aggregates, then **`collect()`** to
|
|
2009
|
+
materialize a tibble when you need in-memory R rows.
|
|
2010
|
+
|
|
2011
|
+
That pattern matches production use: **JRuby threads** (or sequential code) assemble many rows in
|
|
2012
|
+
Ruby; you pay **one** bridge-heavy handoff to R; **dplyr** runs vectorised work on the Arrow table
|
|
2013
|
+
in R.
|
|
2014
|
+
|
|
2015
|
+
**Prerequisites:** install R packages **`arrow`** and **`dplyr`**. Run scripts with
|
|
2016
|
+
**`bin/galaaz-jruby`** (or the same JVM flags as in **`docs/testing.md`**) so the Arrow JNI stack is
|
|
2017
|
+
available.
|
|
2018
|
+
|
|
2019
|
+
## Other `R::Arrow` helpers
|
|
2020
|
+
|
|
2021
|
+
The Ruby module **`R::Arrow`** (see `lib/R_interface/r_arrow.rb`) also includes:
|
|
2022
|
+
|
|
2023
|
+
* **`R::Arrow.table_from(df)`** — wrap an R `data.frame` / tibble as an Arrow table.
|
|
2024
|
+
* **`R::Arrow.read_feather` / `write_feather`**, **`read_parquet`**, **`dataset(path)`** — file and
|
|
2025
|
+
dataset IO on paths visible to R.
|
|
2026
|
+
|
|
2027
|
+
## Example: many Ruby rows → Arrow in R → grouped statistics
|
|
2028
|
+
|
|
2029
|
+
The repository test **`slow-specs/arrow_large_pipeline_spec.rb`** builds **200k rows** in parallel
|
|
2030
|
+
(eight threads × 25,000 rows), pushes them through **`R::Arrow.from_ruby_batches`**, then checks that
|
|
2031
|
+
**dplyr** group summaries match a Ruby reference calculation. The same logic appears below at a
|
|
2032
|
+
**smaller scale** so this manual can knit quickly; increase `thread_count` and `rows_per_thread`
|
|
2033
|
+
when experimenting locally.
|
|
2034
|
+
|
|
2035
|
+
```{ruby arrow_pipeline_example, message=FALSE, warning=FALSE}
|
|
2036
|
+
# Scaled-down version of slow-specs/arrow_large_pipeline_spec.rb.
|
|
2037
|
+
unless R::Support.eval("requireNamespace('arrow', quietly=TRUE) && requireNamespace('dplyr', quietly=TRUE)") == true
|
|
2038
|
+
puts '(Skip: need arrow + dplyr in R; use bin/galaaz-jruby outside gKnit.)'
|
|
2039
|
+
else
|
|
2040
|
+
thread_count = 4
|
|
2041
|
+
rows_per_thread = 500
|
|
2042
|
+
group_count = 5
|
|
2043
|
+
|
|
2044
|
+
batches = []
|
|
2045
|
+
mutex = Mutex.new
|
|
2046
|
+
threads = []
|
|
2047
|
+
|
|
2048
|
+
thread_count.times do |tid|
|
|
2049
|
+
threads << Thread.new do
|
|
2050
|
+
start = tid * rows_per_thread
|
|
2051
|
+
local = (start...(start + rows_per_thread)).map do |i|
|
|
2052
|
+
{
|
|
2053
|
+
id: i,
|
|
2054
|
+
grp: "g#{i % group_count}",
|
|
2055
|
+
value: (i % 17) + 1,
|
|
2056
|
+
weight: ((i % 5) + 1) * 0.5
|
|
2057
|
+
}
|
|
2058
|
+
end
|
|
2059
|
+
mutex.synchronize { batches << local }
|
|
2060
|
+
end
|
|
2061
|
+
end
|
|
2062
|
+
threads.each(&:join)
|
|
2063
|
+
|
|
2064
|
+
tbl = R::Arrow.from_ruby_batches(batches)
|
|
2065
|
+
puts "R class after from_ruby_batches: #{tbl.rclass}"
|
|
2066
|
+
|
|
2067
|
+
grouped = R.dplyr___group_by(tbl, :grp)
|
|
2068
|
+
summarised = R.dplyr___summarise(
|
|
2069
|
+
grouped,
|
|
2070
|
+
n: E.n(),
|
|
2071
|
+
total: E.sum(:value),
|
|
2072
|
+
wsum: E.sum(R[:value] * R[:weight])
|
|
2073
|
+
)
|
|
2074
|
+
out = R.dplyr___collect(summarised)
|
|
2075
|
+
|
|
2076
|
+
puts 'Per-group summary (first rows):'
|
|
2077
|
+
puts R.as__data__frame(out).head(10)
|
|
2078
|
+
|
|
2079
|
+
total_n = 0
|
|
2080
|
+
(1..(out.nrow >> 0)).each { |i| total_n += (out[['n']][i] >> 0) }
|
|
2081
|
+
puts "Sum of group counts n (should equal #{thread_count * rows_per_thread}): #{total_n}"
|
|
2082
|
+
end
|
|
2083
|
+
```
|
|
2084
|
+
|
|
2085
|
+
**What to notice:** (1) Ruby only sees **`Hash`** rows and Ruby **`Thread`** objects; (2) a single
|
|
2086
|
+
**`from_ruby_batches`** call creates the Arrow table in R; (3) **`dplyr___group_by`** /
|
|
2087
|
+
**`dplyr___summarise`** / **`dplyr___collect`** mirror **`dplyr::group_by`** /
|
|
2088
|
+
**`dplyr::summarise`** / **`dplyr::collect`** on an Arrow-backed table. For a lighter test, see
|
|
2089
|
+
**`specs/arrow_from_ruby_batches_spec.rb`**; for the full-size benchmark, run
|
|
2090
|
+
**`bin/run_slow_rspec slow-specs/arrow_large_pipeline_spec.rb`**.
|
|
2091
|
+
|
|
2092
|
+
# Bioconductor and DESeq2
|
|
2093
|
+
|
|
2094
|
+
**Bioconductor** packages are ordinary R packages installed from the Bioconductor repositories.
|
|
2095
|
+
Galaaz does not treat them specially: once installed in **GNU R**, you load them with
|
|
2096
|
+
**`R.library`** like any CRAN package.
|
|
2097
|
+
|
|
2098
|
+
## Installing Bioconductor packages
|
|
2099
|
+
|
|
2100
|
+
From an R session (or `R -e '...'`), use **BiocManager** (see
|
|
2101
|
+
[bioconductor.org](https://bioconductor.org/install/)):
|
|
2102
|
+
|
|
2103
|
+
```r
|
|
2104
|
+
if (!requireNamespace("BiocManager", quietly = TRUE))
|
|
2105
|
+
install.packages("BiocManager")
|
|
2106
|
+
BiocManager::install(c("DESeq2", "airway"))
|
|
2107
|
+
```
|
|
2108
|
+
|
|
2109
|
+
The **`airway`** package ships the example **`SummarizedExperiment`** used below. **DESeq2**
|
|
2110
|
+
pulls in several dependencies; the first install can take several minutes.
|
|
2111
|
+
|
|
2112
|
+
## Example: DESeq2 on the airway dataset
|
|
2113
|
+
|
|
2114
|
+
The script **`examples/bioconductor_deseq2_airway/deseq2_airway_galaaz.rb`** is the canonical
|
|
2115
|
+
version in the repository. Run it from the **Galaaz repository root** with JRuby, for example:
|
|
2116
|
+
|
|
2117
|
+
```text
|
|
2118
|
+
bin/galaaz-jruby examples/bioconductor_deseq2_airway/deseq2_airway_galaaz.rb
|
|
2119
|
+
```
|
|
2120
|
+
|
|
2121
|
+
The workflow in Ruby mirrors a standard DESeq2 vignette:
|
|
2122
|
+
|
|
2123
|
+
1. **`R.library('DESeq2')`** and **`R.library('airway')`**, then **`R.data('airway')`** so the
|
|
2124
|
+
object exists in R’s global environment.
|
|
2125
|
+
2. **`airway = ~R[:airway]`** pulls the experiment into a Galaaz wrapper so you can pass it to R
|
|
2126
|
+
functions as a Ruby value.
|
|
2127
|
+
3. **`R.DESeqDataSet(..., design: (R[:all].til R[:cell] + R[:dex]))`** builds the **`DESeqDataSet`**. The
|
|
2128
|
+
**`(R[:all].til R[:cell] + R[:dex])`** form is Galaaz’s way of passing the one-sided formula
|
|
2129
|
+
**`~ cell + dex`** (adjust for the design you need).
|
|
2130
|
+
4. Prefilter rows with almost no counts: **`keep = R.rowSums(R.counts(dds)) >= 10`** and
|
|
2131
|
+
**`dds = dds[keep, :all]`**.
|
|
2132
|
+
5. **`dds = R.DESeq(dds)`** fits the model; **`res = R.results(dds, contrast: R.c('dex', 'trt', 'untrt'))`**
|
|
2133
|
+
extracts the treatment contrast (adjust **`contrast`** for your experiment).
|
|
2134
|
+
6. Summaries use normal Ruby string interpolation on **`R.nrow`**, **`R.ncol`**, **`R.colnames`**, etc.
|
|
2135
|
+
7. **`R.pdf(...); R.plotMA(res, ...); R.dev__off`** writes DESeq2’s MA plot (path is relative to the
|
|
2136
|
+
process working directory—use the repo root when running the bundled script).
|
|
2137
|
+
|
|
2138
|
+
Related benchmarks and warm-run notes live under **`docs/deseq2_airway_benchmark.md`** and
|
|
2139
|
+
**`examples/bioconductor_deseq2_airway/bench_*.rb`**.
|
|
2140
|
+
|
|
2141
|
+
Below is the full listing (same as the file in the repository). It is **not** executed while this
|
|
2142
|
+
manual is knitted, because **DESeq2** is heavy and may be absent on the build machine.
|
|
2143
|
+
|
|
2144
|
+
```{ruby deseq2_airway_full_listing, eval=FALSE}
|
|
2145
|
+
# Canonical script: examples/bioconductor_deseq2_airway/deseq2_airway_galaaz.rb
|
|
2146
|
+
# Run: bin/galaaz-jruby examples/.../deseq2_airway_galaaz.rb (repo root).
|
|
2147
|
+
|
|
2148
|
+
require 'galaaz'
|
|
2149
|
+
|
|
2150
|
+
R.library('DESeq2')
|
|
2151
|
+
R.library('airway')
|
|
2152
|
+
R.data('airway')
|
|
2153
|
+
|
|
2154
|
+
airway = ~R[:airway]
|
|
2155
|
+
|
|
2156
|
+
# Build DESeq2 dataset with one-sided formula: ~ cell + dex.
|
|
2157
|
+
dds = R.DESeqDataSet(airway, design: (R[:all].til R[:cell] + R[:dex]))
|
|
2158
|
+
|
|
2159
|
+
# Prefilter genes with almost no counts.
|
|
2160
|
+
keep = R.rowSums(R.counts(dds)) >= 10
|
|
2161
|
+
dds = dds[keep, :all]
|
|
2162
|
+
|
|
2163
|
+
# Fit DE model and extract treatment effect.
|
|
2164
|
+
dds = R.DESeq(dds)
|
|
2165
|
+
res = R.results(dds, contrast: R.c('dex', 'trt', 'untrt'))
|
|
2166
|
+
|
|
2167
|
+
# Compact sanity outputs for quick verification.
|
|
2168
|
+
puts "Samples: #{R.ncol(dds)}"
|
|
2169
|
+
puts "Genes after prefilter: #{R.nrow(dds)}"
|
|
2170
|
+
puts "Result rows: #{R.nrow(res)}"
|
|
2171
|
+
puts "Result columns: #{R.colnames(res)}"
|
|
2172
|
+
puts "Significant genes (padj < 0.05): #{R.sum(res.padj < 0.05, na__rm: true)}"
|
|
2173
|
+
|
|
2174
|
+
res_ordered = res[R.order(res.padj), :all]
|
|
2175
|
+
puts R.head(R.as__data__frame(res_ordered), 10)
|
|
2176
|
+
|
|
2177
|
+
# Standard DESeq2 plot call written to file.
|
|
2178
|
+
R.pdf('examples/bioconductor_deseq2_airway/plotMA_galaaz.pdf')
|
|
2179
|
+
R.plotMA(res, ylim: R.c(-5, 5))
|
|
2180
|
+
R.dev__off
|
|
1597
2181
|
```
|
|
1598
2182
|
|
|
2183
|
+
If **DESeq2** and **airway** are installed, the next chunk loads the data and prints a short
|
|
2184
|
+
preview (it does **not** run **`DESeq`** so the manual knits quickly).
|
|
2185
|
+
|
|
2186
|
+
```{ruby deseq2_airway_smoke, message=FALSE, warning=FALSE}
|
|
2187
|
+
unless R::Support.eval("requireNamespace('DESeq2', quietly=TRUE) && requireNamespace('airway', quietly=TRUE)")
|
|
2188
|
+
puts '(Skip: install DESeq2 and airway via BiocManager in R to run the full example.)'
|
|
2189
|
+
else
|
|
2190
|
+
R.library('DESeq2')
|
|
2191
|
+
R.library('airway')
|
|
2192
|
+
R.data('airway')
|
|
2193
|
+
airway = ~R[:airway]
|
|
2194
|
+
puts 'airway object (head of assay / dims via R):'
|
|
2195
|
+
puts "ncol(samples): #{R.ncol(airway)}"
|
|
2196
|
+
puts R.head(R.assay(airway), 3)
|
|
2197
|
+
end
|
|
2198
|
+
```
|
|
2199
|
+
|
|
2200
|
+
# Performance
|
|
2201
|
+
|
|
2202
|
+
For realistic analyses, **most wall-clock time is spent inside GNU R** (model fitting, I/O inside
|
|
2203
|
+
R, graphics). The Galaaz **bridge** adds overhead mainly from **starting a session**, **serializing
|
|
2204
|
+
requests**, and **wrapping results** in Ruby objects—not from reimplementing R’s numerical work.
|
|
2205
|
+
|
|
2206
|
+
Practical tips:
|
|
2207
|
+
|
|
2208
|
+
* Keep **hot loops** in R or vectorized code when possible; use Ruby for orchestration, I/O, and
|
|
2209
|
+
glue.
|
|
2210
|
+
* **Reuse one process**: running many short scripts cold-starts Ruby, the JVM, and R each time;
|
|
2211
|
+
a long-lived process or repeated calls in one run amortize setup (see benchmarks below).
|
|
2212
|
+
* **Batch data**: merge shards in Ruby, then call **`R::Arrow.from_ruby_batches`** (or build one
|
|
2213
|
+
data frame) instead of millions of tiny R calls.
|
|
2214
|
+
|
|
2215
|
+
For measured discussion (including DESeq2-style workloads and warm comparisons), see
|
|
2216
|
+
**`docs/performance.md`** and **`docs/deseq2_airway_benchmark.md`** in the Galaaz repository.
|
|
2217
|
+
|
|
1599
2218
|
# Graphics in Galaaz
|
|
1600
2219
|
|
|
1601
2220
|
Creating graphics in Galaaz is quite easy, as it can use all the power of ggplot2. There are
|
|
1602
|
-
many resources
|
|
2221
|
+
many resources on the web that teach ggplot, so here we give a quick example of ggplot
|
|
1603
2222
|
integration with Ruby. We continue to use the :mtcars dataset and we will plot a diverging
|
|
1604
|
-
bar plot, showing cars that have 'above' or 'below' gas
|
|
2223
|
+
bar plot, showing cars that have 'above' or 'below' gas consumption. Let's first prepare
|
|
1605
2224
|
the data frame with the necessary data:
|
|
1606
2225
|
|
|
1607
2226
|
```{ruby diverging_plot_pre}
|
|
1608
|
-
#
|
|
1609
|
-
mtcars =
|
|
1610
|
-
|
|
1611
|
-
#
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
mtcars.
|
|
1615
|
-
|
|
1616
|
-
# compute normalized mpg and add it to a new column called mpg_z
|
|
1617
|
-
# Note that the mean value for mpg can be obtained by calling the 'mean'
|
|
1618
|
-
# function on the vector 'mtcars.mpg'. The same with the standard
|
|
1619
|
-
# deviation 'sd'. The vector is then rounded to two digits with 'round 2'
|
|
2227
|
+
# :mtcars -> Ruby handle
|
|
2228
|
+
mtcars = ~R[:mtcars]
|
|
2229
|
+
|
|
2230
|
+
# Row labels are not a plot column; copy them to car_name.
|
|
2231
|
+
mtcars.car_name = R.rownames(R[:mtcars])
|
|
2232
|
+
|
|
2233
|
+
# Z-score mpg (mean/sd on mtcars.mpg); round to 2 decimals.
|
|
1620
2234
|
mtcars.mpg_z = ((mtcars.mpg - mtcars.mpg.mean)/mtcars.mpg.sd).round 2
|
|
1621
2235
|
|
|
1622
|
-
#
|
|
1623
|
-
# that looks at every element of the mpg_z vector and if the value is below
|
|
1624
|
-
# 0, returns 'below', otherwise returns 'above'
|
|
2236
|
+
# ifelse is vectorized: below / above average mpg_z.
|
|
1625
2237
|
mtcars.mpg_type = (mtcars.mpg_z < 0).ifelse("below", "above")
|
|
1626
2238
|
|
|
1627
|
-
#
|
|
2239
|
+
# Sort rows by mpg_z.
|
|
1628
2240
|
mtcars = mtcars[mtcars.mpg_z.order, :all]
|
|
1629
2241
|
|
|
1630
|
-
#
|
|
2242
|
+
# Factor car_name so plot order follows sort.
|
|
1631
2243
|
mtcars.car_name = mtcars.car_name.factor levels: mtcars.car_name
|
|
1632
2244
|
|
|
1633
|
-
# let's look at the final data frame
|
|
1634
2245
|
puts mtcars.head
|
|
1635
2246
|
```
|
|
1636
|
-
Now,
|
|
1637
|
-
|
|
2247
|
+
Now, let's plot the diverging bar plot. When using gKnit, you normally do **not** need to open a
|
|
2248
|
+
graphics device manually; gKnit arranges the figure device for chunk output. Galaaz
|
|
1638
2249
|
provides integration with ggplot. The interested reader should check online for more
|
|
1639
2250
|
information on ggplot, since it is outside the scope of this manual describing
|
|
1640
|
-
how ggplot works.
|
|
2251
|
+
how ggplot works. Here we give only a brief description of how this plot is generated.
|
|
1641
2252
|
|
|
1642
|
-
ggplot implements the 'grammar of graphics'. In this approach, plots are
|
|
2253
|
+
ggplot implements the 'grammar of graphics'. In this approach, plots are built by
|
|
1643
2254
|
adding layers to the plot. On the first layer we describe what we want on the 'x'
|
|
1644
2255
|
and 'y' axis of the plot. In this case, we have 'car_name' on the 'x' axis and
|
|
1645
2256
|
'mpg\_z' on the 'y' axis. Then the type of graph is specified by adding
|
|
1646
2257
|
'geom\_bar' (for a bar graph). We specify that our bars should be filled using
|
|
1647
|
-
'mpg\_type', which is either 'above' or '
|
|
2258
|
+
'mpg\_type', which is either 'above' or 'below' giving then two colours for
|
|
1648
2259
|
filling. On the next layer we specify the labels for the graph, then we add the
|
|
1649
2260
|
title and subtitle. Finally, in a bar chart usually bars go on the vertical direction,
|
|
1650
|
-
but in this graph we want the bars to be horizontally
|
|
2261
|
+
but in this graph we want the bars to be horizontally laid so we add 'coord\_flip'.
|
|
1651
2262
|
|
|
1652
2263
|
```{ruby diverging_bar, fig.width = 9.1, fig.height = 6.5}
|
|
1653
2264
|
require 'ggplot'
|
|
@@ -1665,7 +2276,7 @@ puts mtcars.ggplot(E.aes(x: :car_name, y: :mpg_z, label: :mpg_z)) +
|
|
|
1665
2276
|
# Coding with Tidyverse
|
|
1666
2277
|
|
|
1667
2278
|
In R, and when coding with 'tidyverse', arguments to a function are usually not
|
|
1668
|
-
*
|
|
2279
|
+
*referentially transparent*. That is, you can’t replace a value with a seemingly equivalent
|
|
1669
2280
|
object that you’ve defined elsewhere. To see the problem, let's first define a data frame:
|
|
1670
2281
|
|
|
1671
2282
|
```{ruby df}
|
|
@@ -1681,17 +2292,17 @@ filter(df, my_var == 1)
|
|
|
1681
2292
|
```
|
|
1682
2293
|
It generates the following error: "object 'x' not found.
|
|
1683
2294
|
|
|
1684
|
-
However, in Galaaz, arguments are
|
|
1685
|
-
code
|
|
2295
|
+
However, in Galaaz, arguments are referentially transparent as can be seen by the
|
|
2296
|
+
code below. Note initially that 'my_var = R[:x]' will not give the error "object 'x' not found"
|
|
1686
2297
|
since ':x' is treated as an expression and assigned to my\_var. Then when doing (my\_var.eq 1),
|
|
1687
|
-
my\_var is a variable that resolves to ':x' and it becomes equivalent to (:x.eq 1) which is
|
|
2298
|
+
my\_var is a variable that resolves to ':x' and it becomes equivalent to (R[:x].eq 1) which is
|
|
1688
2299
|
what we want.
|
|
1689
2300
|
|
|
1690
2301
|
```{ruby my_var}
|
|
1691
|
-
my_var = :x
|
|
2302
|
+
my_var = R[:x]
|
|
1692
2303
|
puts df.filter(my_var.eq 1)
|
|
1693
2304
|
```
|
|
1694
|
-
As stated by
|
|
2305
|
+
As stated by Hadley
|
|
1695
2306
|
|
|
1696
2307
|
> dplyr code is ambiguous. Depending on what variables are defined where,
|
|
1697
2308
|
> filter(df, x == y) could be equivalent to any of:
|
|
@@ -1703,9 +2314,9 @@ df[x == df$y, ]
|
|
|
1703
2314
|
df[x == y, ]
|
|
1704
2315
|
```
|
|
1705
2316
|
In galaaz this ambiguity does not exist, filter(df, x.eq y) is not a valid expression as
|
|
1706
|
-
expressions are build with symbols. In doing filter(df, :x.eq y) we are looking for elements
|
|
2317
|
+
expressions are build with symbols. In doing filter(df, R[:x].eq y) we are looking for elements
|
|
1707
2318
|
of the 'x' column that are equal to a previously defined y variable. Finally in
|
|
1708
|
-
filter(df, :x.eq :y) we are looking for elements in which the 'x' column value is equal to
|
|
2319
|
+
filter(df, R[:x].eq R[:y]) we are looking for elements in which the 'x' column value is equal to
|
|
1709
2320
|
the 'y' column value. This can be seen in the following two chunks of code:
|
|
1710
2321
|
|
|
1711
2322
|
```{ruby disamb1}
|
|
@@ -1713,13 +2324,13 @@ y = 1
|
|
|
1713
2324
|
x = 2
|
|
1714
2325
|
|
|
1715
2326
|
# looking for values where the 'x' column is equal to the 'y' column
|
|
1716
|
-
puts df.filter(:x.eq :y)
|
|
2327
|
+
puts df.filter(R[:x].eq R[:y])
|
|
1717
2328
|
```
|
|
1718
2329
|
|
|
1719
2330
|
```{ruby disamb2}
|
|
1720
2331
|
# looking for values where the 'x' column is equal to the 'y' variable
|
|
1721
2332
|
# in this case, the number 1
|
|
1722
|
-
puts df.filter(:x.eq y)
|
|
2333
|
+
puts df.filter(R[:x].eq y)
|
|
1723
2334
|
```
|
|
1724
2335
|
## Writing a function that applies to different data sets
|
|
1725
2336
|
|
|
@@ -1746,11 +2357,12 @@ Unfortunately, in R, this function can fail silently if one of the variables isn
|
|
|
1746
2357
|
in the data frame, but is present in the global environment. We will not go through here how
|
|
1747
2358
|
to solve this problem in R.
|
|
1748
2359
|
|
|
1749
|
-
In Galaaz the method mutate_y
|
|
2360
|
+
In Galaaz the method mutate_y below will work fine and will never fail silently.
|
|
1750
2361
|
|
|
1751
2362
|
```{ruby mutate_y, warning=FALSE}
|
|
1752
2363
|
def mutate_y(df)
|
|
1753
|
-
|
|
2364
|
+
# Mutate column names are Ruby kwargs (y: …). Use .assign only for R `<-` expressions.
|
|
2365
|
+
df.mutate(y: R[:a] + R[:x])
|
|
1754
2366
|
end
|
|
1755
2367
|
```
|
|
1756
2368
|
Here we create a data frame that has only one column named 'x':
|
|
@@ -1760,8 +2372,8 @@ df1 = R.data__frame(x: (1..3))
|
|
|
1760
2372
|
puts df1
|
|
1761
2373
|
```
|
|
1762
2374
|
|
|
1763
|
-
Note that method mutate_y will fail
|
|
1764
|
-
in the scope of the method. Variable 'a' has no relationship with the symbol
|
|
2375
|
+
Note that method mutate_y will fail independently from the fact that variable 'a' is defined and
|
|
2376
|
+
in the scope of the method. Variable 'a' has no relationship with the symbol `R[:a]` used in the
|
|
1765
2377
|
definition of 'mutate\_y' above:
|
|
1766
2378
|
|
|
1767
2379
|
```{ruby call_mutate_y, warning = FALSE}
|
|
@@ -1770,12 +2382,13 @@ mutate_y(df1)
|
|
|
1770
2382
|
```
|
|
1771
2383
|
## Different expressions
|
|
1772
2384
|
|
|
1773
|
-
Let's move to the next problem as presented by
|
|
2385
|
+
Let's move to the next problem as presented by Hadley where trying to write a function in R
|
|
1774
2386
|
that will receive two argumens, the first a variable and the second an expression is not trivial.
|
|
1775
|
-
|
|
2387
|
+
Below we create a data frame and we want to write a function that groups data by a variable and
|
|
1776
2388
|
summarises it by an expression:
|
|
1777
2389
|
|
|
1778
2390
|
```{r diff_expr}
|
|
2391
|
+
library(dplyr)
|
|
1779
2392
|
set.seed(123)
|
|
1780
2393
|
|
|
1781
2394
|
df <- data.frame(
|
|
@@ -1800,7 +2413,7 @@ d2 <- df %>%
|
|
|
1800
2413
|
as.data.frame(d2)
|
|
1801
2414
|
```
|
|
1802
2415
|
|
|
1803
|
-
As shown by
|
|
2416
|
+
As shown by Hadley, one might expect this function to do the trick:
|
|
1804
2417
|
|
|
1805
2418
|
```{r diff_exp_fnc}
|
|
1806
2419
|
my_summarise <- function(df, group_var) {
|
|
@@ -1815,32 +2428,32 @@ my_summarise <- function(df, group_var) {
|
|
|
1815
2428
|
|
|
1816
2429
|
In order to solve this problem, coding with dplyr requires the introduction of many new concepts
|
|
1817
2430
|
and functions such as 'quo', 'quos', 'enquo', 'enquos', '!!' (bang bang), '!!!' (triple bang).
|
|
1818
|
-
Again, we'll leave to
|
|
2431
|
+
Again, we'll leave to Hadley the explanation on how to use all those functions.
|
|
1819
2432
|
|
|
1820
2433
|
Now, let's try to implement the same function in galaaz. The next code block first prints the
|
|
1821
|
-
'df' data frame defined previously in R (to access an R variable from Galaaz, we use the
|
|
1822
|
-
operator
|
|
2434
|
+
'df' data frame defined previously in R (to access an R variable from Galaaz, we use the tilde
|
|
2435
|
+
operator `~` applied to the R variable name as a symbol, e.g. `:df`).
|
|
1823
2436
|
|
|
1824
2437
|
```{ruby r_dataframe}
|
|
1825
|
-
puts
|
|
2438
|
+
puts ~R[:df]
|
|
1826
2439
|
```
|
|
1827
2440
|
|
|
1828
2441
|
We then create the 'my_summarize' method and call it passing the R data frame and
|
|
1829
|
-
the group by variable ':g1':
|
|
2442
|
+
the group by variable 'R[:g1]':
|
|
1830
2443
|
|
|
1831
2444
|
```{ruby diff_exp_ruby_func}
|
|
1832
2445
|
def my_summarize(df, group_var)
|
|
1833
2446
|
df.group_by(group_var).
|
|
1834
|
-
summarize(a: :a.mean)
|
|
2447
|
+
summarize(a: R[:a].mean)
|
|
1835
2448
|
end
|
|
1836
2449
|
|
|
1837
|
-
puts my_summarize(:df, :g1)
|
|
2450
|
+
puts my_summarize(~R[:df], R[:g1])
|
|
1838
2451
|
```
|
|
1839
2452
|
|
|
1840
2453
|
It works!!! Well, let's make sure this was not just some coincidence
|
|
1841
2454
|
|
|
1842
2455
|
```{ruby group_g2}
|
|
1843
|
-
puts my_summarize(:df, :g2)
|
|
2456
|
+
puts my_summarize(~R[:df], R[:g2])
|
|
1844
2457
|
```
|
|
1845
2458
|
|
|
1846
2459
|
Great, everything is fine! No magic, no new functions, no complexities, just normal, standard Ruby
|
|
@@ -1852,7 +2465,7 @@ In the previous section we've managed to get rid of all NSE formulation for a si
|
|
|
1852
2465
|
does this remain true for more complex examples, or will the Galaaz way prove inpractical for
|
|
1853
2466
|
more complex code?
|
|
1854
2467
|
|
|
1855
|
-
In the next example
|
|
2468
|
+
In the next example Hadley proposes us to write a function that given an expression such as 'a'
|
|
1856
2469
|
or 'a * b', calculates three summaries. What we want a function that does the same as these R
|
|
1857
2470
|
statements:
|
|
1858
2471
|
|
|
@@ -1881,9 +2494,9 @@ def my_summarise2(df, expr)
|
|
|
1881
2494
|
)
|
|
1882
2495
|
end
|
|
1883
2496
|
|
|
1884
|
-
puts my_summarise2((
|
|
2497
|
+
puts my_summarise2((~R[:df]), :a)
|
|
1885
2498
|
puts "\n"
|
|
1886
|
-
puts my_summarise2((
|
|
2499
|
+
puts my_summarise2((~R[:df]), R[:a] * R[:b])
|
|
1887
2500
|
```
|
|
1888
2501
|
|
|
1889
2502
|
Once again, there is no need to use any special theory or functions. The only point to be
|
|
@@ -1891,7 +2504,7 @@ careful about is the use of 'E' to build expressions from functions 'mean', 'sum
|
|
|
1891
2504
|
|
|
1892
2505
|
## Different input and output variable
|
|
1893
2506
|
|
|
1894
|
-
Now the next challenge presented by
|
|
2507
|
+
Now the next challenge presented by Hadley is to vary the name of the output variables based on
|
|
1895
2508
|
the received expression. So, if the input expression is 'a', we want our data frame columns to
|
|
1896
2509
|
be named 'mean\_a' and 'sum\_a'. Now, if the input expression is 'b', columns
|
|
1897
2510
|
should be named 'mean\_b' and 'sum\_b'.
|
|
@@ -1917,7 +2530,7 @@ mutate(df, mean_b = mean(b), sum_b = sum(b))
|
|
|
1917
2530
|
#> 4 2 2 5 4 3 15
|
|
1918
2531
|
#> # … with 1 more row
|
|
1919
2532
|
```
|
|
1920
|
-
In order to solve this problem in R,
|
|
2533
|
+
In order to solve this problem in R, Hadley needs to introduce some more new functions and notations:
|
|
1921
2534
|
'quo_name' and the ':=' operator from package 'rlang'
|
|
1922
2535
|
|
|
1923
2536
|
Here is our Ruby code:
|
|
@@ -1931,9 +2544,9 @@ def my_mutate(df, expr)
|
|
|
1931
2544
|
sum_name => E.sum(expr))
|
|
1932
2545
|
end
|
|
1933
2546
|
|
|
1934
|
-
puts my_mutate((
|
|
2547
|
+
puts my_mutate((~R[:df]), :a)
|
|
1935
2548
|
puts "\n"
|
|
1936
|
-
puts my_mutate((
|
|
2549
|
+
puts my_mutate((~R[:df]), :b)
|
|
1937
2550
|
```
|
|
1938
2551
|
It really seems that "Non Standard Evaluation" is actually quite standard in Galaaz! But, you
|
|
1939
2552
|
might have noticed a small change in the way the arguments to the mutate method were called.
|
|
@@ -1945,7 +2558,7 @@ and variable mean\_name is not followed by ':' but by '=>'. This is standard Ru
|
|
|
1945
2558
|
|
|
1946
2559
|
## Capturing multiple variables
|
|
1947
2560
|
|
|
1948
|
-
Moving on with new complexities,
|
|
2561
|
+
Moving on with new complexities, Hadley proposes us to solve the problem in which the
|
|
1949
2562
|
summarise function will receive any number of grouping variables.
|
|
1950
2563
|
|
|
1951
2564
|
This again is quite standard Ruby. In order to receive an undefined number of paramenters
|
|
@@ -1957,7 +2570,7 @@ def my_summarise3(df, *group_vars)
|
|
|
1957
2570
|
summarise(a: E.mean(:a))
|
|
1958
2571
|
end
|
|
1959
2572
|
|
|
1960
|
-
puts my_summarise3((
|
|
2573
|
+
puts my_summarise3((~R[:df]), R[:g1], R[:g2])
|
|
1961
2574
|
```
|
|
1962
2575
|
|
|
1963
2576
|
## Why does R require NSE and Galaaz does not?
|
|
@@ -1975,7 +2588,7 @@ In Ruby, there is no lazy evaluation of parameters and 'a' is always a variable
|
|
|
1975
2588
|
Variables assume their value as soon as they are used, so 'x = a' is immediately evaluate and
|
|
1976
2589
|
variable 'x' will receive the value of variable 'a' as soon as the Ruby statement is executed.
|
|
1977
2590
|
Ruby also provides the notion of a symbol; ':a' is a symbol and does not evaluate to anything.
|
|
1978
|
-
Galaaz uses Ruby symbols to build expressions that are not bound to anything: ':a.eq :b' is
|
|
2591
|
+
Galaaz uses Ruby symbols to build expressions that are not bound to anything: 'R[:a].eq R[:b]' is
|
|
1979
2592
|
clearly an expression and has no relationship whatsoever with the statment 'a = b'. By using
|
|
1980
2593
|
symbols, variables and expressions all the possible ambiguities that are found in R are
|
|
1981
2594
|
eliminated in Galaaz.
|
|
@@ -1985,7 +2598,7 @@ of input they are expecting, they might be expecting regular variables or they m
|
|
|
1985
2598
|
expecting expressions and the R function will know how to deal with an input of the form
|
|
1986
2599
|
'a = b', now for the Ruby developer it might not be immediately clear if it should call the
|
|
1987
2600
|
function passing the value 'true' if variable 'a' is equal to variable 'b' or if it should
|
|
1988
|
-
call the function passing the expression ':a.eq :b'.
|
|
2601
|
+
call the function passing the expression 'R[:a].eq R[:b]'.
|
|
1989
2602
|
|
|
1990
2603
|
|
|
1991
2604
|
## Advanced dplyr features
|
|
@@ -2008,12 +2621,13 @@ In the following examples, we show the use of functions 'group\_by\_at', 'summar
|
|
|
2008
2621
|
features of characters in the Starwars movies:
|
|
2009
2622
|
|
|
2010
2623
|
```{ruby starwars}
|
|
2011
|
-
puts (
|
|
2624
|
+
puts (~R[:starwars]).head
|
|
2012
2625
|
```
|
|
2013
|
-
The grouped_mean function
|
|
2626
|
+
The grouped_mean function below will receive a grouping variable and calculate summaries for
|
|
2014
2627
|
the value\_variables given:
|
|
2015
2628
|
|
|
2016
2629
|
```{r grouped_mean}
|
|
2630
|
+
library(dplyr)
|
|
2017
2631
|
grouped_mean <- function(data, grouping_variables, value_variables) {
|
|
2018
2632
|
data %>%
|
|
2019
2633
|
group_by_at(grouping_variables) %>%
|
|
@@ -2035,24 +2649,26 @@ def grouped_mean(data, grouping_variables, value_variables)
|
|
|
2035
2649
|
data.
|
|
2036
2650
|
group_by_at(grouping_variables).
|
|
2037
2651
|
mutate(count: E.n).
|
|
2038
|
-
summarise_at(E.c(value_variables, "count"),
|
|
2652
|
+
summarise_at(E.c(value_variables, "count"), ~R[:mean], na__rm: true).
|
|
2039
2653
|
rename_at(value_variables, E.funs(E.paste0("mean_", value_variables)))
|
|
2040
2654
|
end
|
|
2041
2655
|
|
|
2042
|
-
puts grouped_mean((
|
|
2656
|
+
puts grouped_mean((~R[:starwars]), "eye_color", E.c("mass", "birth_year"))
|
|
2043
2657
|
```
|
|
2044
2658
|
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2659
|
+
The examples above cover programmatic dplyr with string column names and `_at` helpers. The same
|
|
2660
|
+
Galaaz patterns (symbols, `E.*` for expression-safe functions, and Ruby methods on R-backed objects)
|
|
2661
|
+
extend to other tidyverse workflows; consult R package documentation for function-specific
|
|
2662
|
+
arguments.
|
|
2048
2663
|
|
|
2049
2664
|
# Contributing
|
|
2050
2665
|
|
|
2051
2666
|
* Fork it
|
|
2052
|
-
* Create your feature branch (git checkout -b my-new-feature)
|
|
2053
|
-
* Write
|
|
2054
|
-
|
|
2055
|
-
*
|
|
2056
|
-
*
|
|
2667
|
+
* Create your feature branch (`git checkout -b my-new-feature`)
|
|
2668
|
+
* Write tests — use **`bin/run_rspec`** or **`bin/run_all_rspec`** with **JRuby** so JVM flags and
|
|
2669
|
+
the load path match **`docs/testing.md`**
|
|
2670
|
+
* Commit your changes (`git commit -am 'Add some feature'`)
|
|
2671
|
+
* Push to the branch (`git push origin my-new-feature`)
|
|
2672
|
+
* Open a pull request
|
|
2057
2673
|
|
|
2058
2674
|
# References
|