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

|
|
4347
|
+

|
|
3617
4348
|
|
|
3618
4349
|
# Coding with Tidyverse
|
|
3619
4350
|
|
|
3620
4351
|
In R, and when coding with 'tidyverse', arguments to a function are usually not
|
|
3621
|
-
*
|
|
4352
|
+
*referentially transparent*. That is, you can’t replace a value with a seemingly equivalent
|
|
3622
4353
|
object that you’ve defined elsewhere. To see the problem, let's first define a data frame:
|
|
3623
4354
|
|
|
3624
4355
|
|
|
3625
|
-
```ruby
|
|
4356
|
+
``` ruby
|
|
3626
4357
|
df = R.data__frame(x: (1..3), y: (3..1))
|
|
3627
4358
|
puts df
|
|
3628
4359
|
```
|
|
@@ -3637,21 +4368,21 @@ puts df
|
|
|
3637
4368
|
and now, let's look at this code:
|
|
3638
4369
|
|
|
3639
4370
|
|
|
3640
|
-
```r
|
|
4371
|
+
``` r
|
|
3641
4372
|
my_var <- x
|
|
3642
4373
|
filter(df, my_var == 1)
|
|
3643
4374
|
```
|
|
3644
4375
|
It generates the following error: "object 'x' not found.
|
|
3645
4376
|
|
|
3646
|
-
However, in Galaaz, arguments are
|
|
3647
|
-
code
|
|
4377
|
+
However, in Galaaz, arguments are referentially transparent as can be seen by the
|
|
4378
|
+
code below. Note initially that 'my_var = R[:x]' will not give the error "object 'x' not found"
|
|
3648
4379
|
since ':x' is treated as an expression and assigned to my\_var. Then when doing (my\_var.eq 1),
|
|
3649
|
-
my\_var is a variable that resolves to ':x' and it becomes equivalent to (:x.eq 1) which is
|
|
4380
|
+
my\_var is a variable that resolves to ':x' and it becomes equivalent to (R[:x].eq 1) which is
|
|
3650
4381
|
what we want.
|
|
3651
4382
|
|
|
3652
4383
|
|
|
3653
|
-
```ruby
|
|
3654
|
-
my_var = :x
|
|
4384
|
+
``` ruby
|
|
4385
|
+
my_var = R[:x]
|
|
3655
4386
|
puts df.filter(my_var.eq 1)
|
|
3656
4387
|
```
|
|
3657
4388
|
|
|
@@ -3659,7 +4390,7 @@ puts df.filter(my_var.eq 1)
|
|
|
3659
4390
|
## x y
|
|
3660
4391
|
## 1 1 3
|
|
3661
4392
|
```
|
|
3662
|
-
As stated by
|
|
4393
|
+
As stated by Hadley
|
|
3663
4394
|
|
|
3664
4395
|
> dplyr code is ambiguous. Depending on what variables are defined where,
|
|
3665
4396
|
> filter(df, x == y) could be equivalent to any of:
|
|
@@ -3671,18 +4402,18 @@ df[x == df$y, ]
|
|
|
3671
4402
|
df[x == y, ]
|
|
3672
4403
|
```
|
|
3673
4404
|
In galaaz this ambiguity does not exist, filter(df, x.eq y) is not a valid expression as
|
|
3674
|
-
expressions are build with symbols. In doing filter(df, :x.eq y) we are looking for elements
|
|
4405
|
+
expressions are build with symbols. In doing filter(df, R[:x].eq y) we are looking for elements
|
|
3675
4406
|
of the 'x' column that are equal to a previously defined y variable. Finally in
|
|
3676
|
-
filter(df, :x.eq :y) we are looking for elements in which the 'x' column value is equal to
|
|
4407
|
+
filter(df, R[:x].eq R[:y]) we are looking for elements in which the 'x' column value is equal to
|
|
3677
4408
|
the 'y' column value. This can be seen in the following two chunks of code:
|
|
3678
4409
|
|
|
3679
4410
|
|
|
3680
|
-
```ruby
|
|
4411
|
+
``` ruby
|
|
3681
4412
|
y = 1
|
|
3682
4413
|
x = 2
|
|
3683
4414
|
|
|
3684
4415
|
# looking for values where the 'x' column is equal to the 'y' column
|
|
3685
|
-
puts df.filter(:x.eq :y)
|
|
4416
|
+
puts df.filter(R[:x].eq R[:y])
|
|
3686
4417
|
```
|
|
3687
4418
|
|
|
3688
4419
|
```
|
|
@@ -3691,10 +4422,10 @@ puts df.filter(:x.eq :y)
|
|
|
3691
4422
|
```
|
|
3692
4423
|
|
|
3693
4424
|
|
|
3694
|
-
```ruby
|
|
4425
|
+
``` ruby
|
|
3695
4426
|
# looking for values where the 'x' column is equal to the 'y' variable
|
|
3696
4427
|
# in this case, the number 1
|
|
3697
|
-
puts df.filter(:x.eq y)
|
|
4428
|
+
puts df.filter(R[:x].eq y)
|
|
3698
4429
|
```
|
|
3699
4430
|
|
|
3700
4431
|
```
|
|
@@ -3726,18 +4457,19 @@ Unfortunately, in R, this function can fail silently if one of the variables isn
|
|
|
3726
4457
|
in the data frame, but is present in the global environment. We will not go through here how
|
|
3727
4458
|
to solve this problem in R.
|
|
3728
4459
|
|
|
3729
|
-
In Galaaz the method mutate_y
|
|
4460
|
+
In Galaaz the method mutate_y below will work fine and will never fail silently.
|
|
3730
4461
|
|
|
3731
4462
|
|
|
3732
|
-
```ruby
|
|
4463
|
+
``` ruby
|
|
3733
4464
|
def mutate_y(df)
|
|
3734
|
-
|
|
4465
|
+
# Mutate column names are Ruby kwargs (y: …). Use .assign only for R `<-` expressions.
|
|
4466
|
+
df.mutate(y: R[:a] + R[:x])
|
|
3735
4467
|
end
|
|
3736
4468
|
```
|
|
3737
4469
|
Here we create a data frame that has only one column named 'x':
|
|
3738
4470
|
|
|
3739
4471
|
|
|
3740
|
-
```ruby
|
|
4472
|
+
``` ruby
|
|
3741
4473
|
df1 = R.data__frame(x: (1..3))
|
|
3742
4474
|
puts df1
|
|
3743
4475
|
```
|
|
@@ -3749,34 +4481,31 @@ puts df1
|
|
|
3749
4481
|
## 3 3
|
|
3750
4482
|
```
|
|
3751
4483
|
|
|
3752
|
-
Note that method mutate_y will fail
|
|
3753
|
-
in the scope of the method. Variable 'a' has no relationship with the symbol
|
|
4484
|
+
Note that method mutate_y will fail independently from the fact that variable 'a' is defined and
|
|
4485
|
+
in the scope of the method. Variable 'a' has no relationship with the symbol `R[:a]` used in the
|
|
3754
4486
|
definition of 'mutate\_y' above:
|
|
3755
4487
|
|
|
3756
4488
|
|
|
3757
|
-
```ruby
|
|
4489
|
+
``` ruby
|
|
3758
4490
|
a = 10
|
|
3759
4491
|
mutate_y(df1)
|
|
3760
4492
|
```
|
|
3761
4493
|
|
|
3762
4494
|
```
|
|
3763
|
-
##
|
|
3764
|
-
##
|
|
3765
|
-
##
|
|
3766
|
-
## In addition: Warning message:
|
|
3767
|
-
## In mutate_impl(.data, dots) :
|
|
3768
|
-
## mismatched protect/unprotect (unprotect with empty protect stack) (RError)
|
|
3769
|
-
## Translated to internal error
|
|
4495
|
+
## Error: ℹ In argument: `y = a + x`.
|
|
4496
|
+
## Caused by error:
|
|
4497
|
+
## ! object 'a' not found
|
|
3770
4498
|
```
|
|
3771
4499
|
## Different expressions
|
|
3772
4500
|
|
|
3773
|
-
Let's move to the next problem as presented by
|
|
4501
|
+
Let's move to the next problem as presented by Hadley where trying to write a function in R
|
|
3774
4502
|
that will receive two argumens, the first a variable and the second an expression is not trivial.
|
|
3775
|
-
|
|
4503
|
+
Below we create a data frame and we want to write a function that groups data by a variable and
|
|
3776
4504
|
summarises it by an expression:
|
|
3777
4505
|
|
|
3778
4506
|
|
|
3779
|
-
```r
|
|
4507
|
+
``` r
|
|
4508
|
+
library(dplyr)
|
|
3780
4509
|
set.seed(123)
|
|
3781
4510
|
|
|
3782
4511
|
df <- data.frame(
|
|
@@ -3798,7 +4527,7 @@ as.data.frame(df)
|
|
|
3798
4527
|
## 5 2 1 1 4
|
|
3799
4528
|
```
|
|
3800
4529
|
|
|
3801
|
-
```r
|
|
4530
|
+
``` r
|
|
3802
4531
|
d2 <- df %>%
|
|
3803
4532
|
group_by(g1) %>%
|
|
3804
4533
|
summarise(a = mean(a))
|
|
@@ -3812,7 +4541,7 @@ as.data.frame(d2)
|
|
|
3812
4541
|
## 2 2 3.333333
|
|
3813
4542
|
```
|
|
3814
4543
|
|
|
3815
|
-
```r
|
|
4544
|
+
``` r
|
|
3816
4545
|
d2 <- df %>%
|
|
3817
4546
|
group_by(g2) %>%
|
|
3818
4547
|
summarise(a = mean(a))
|
|
@@ -3826,10 +4555,10 @@ as.data.frame(d2)
|
|
|
3826
4555
|
## 2 2 3
|
|
3827
4556
|
```
|
|
3828
4557
|
|
|
3829
|
-
As shown by
|
|
4558
|
+
As shown by Hadley, one might expect this function to do the trick:
|
|
3830
4559
|
|
|
3831
4560
|
|
|
3832
|
-
```r
|
|
4561
|
+
``` r
|
|
3833
4562
|
my_summarise <- function(df, group_var) {
|
|
3834
4563
|
df %>%
|
|
3835
4564
|
group_by(group_var) %>%
|
|
@@ -3842,15 +4571,15 @@ my_summarise <- function(df, group_var) {
|
|
|
3842
4571
|
|
|
3843
4572
|
In order to solve this problem, coding with dplyr requires the introduction of many new concepts
|
|
3844
4573
|
and functions such as 'quo', 'quos', 'enquo', 'enquos', '!!' (bang bang), '!!!' (triple bang).
|
|
3845
|
-
Again, we'll leave to
|
|
4574
|
+
Again, we'll leave to Hadley the explanation on how to use all those functions.
|
|
3846
4575
|
|
|
3847
4576
|
Now, let's try to implement the same function in galaaz. The next code block first prints the
|
|
3848
|
-
'df' data frame defined previously in R (to access an R variable from Galaaz, we use the
|
|
3849
|
-
operator
|
|
4577
|
+
'df' data frame defined previously in R (to access an R variable from Galaaz, we use the tilde
|
|
4578
|
+
operator `~` applied to the R variable name as a symbol, e.g. `:df`).
|
|
3850
4579
|
|
|
3851
4580
|
|
|
3852
|
-
```ruby
|
|
3853
|
-
puts
|
|
4581
|
+
``` ruby
|
|
4582
|
+
puts ~R[:df]
|
|
3854
4583
|
```
|
|
3855
4584
|
|
|
3856
4585
|
```
|
|
@@ -3863,20 +4592,20 @@ puts ~:df
|
|
|
3863
4592
|
```
|
|
3864
4593
|
|
|
3865
4594
|
We then create the 'my_summarize' method and call it passing the R data frame and
|
|
3866
|
-
the group by variable ':g1':
|
|
4595
|
+
the group by variable 'R[:g1]':
|
|
3867
4596
|
|
|
3868
4597
|
|
|
3869
|
-
```ruby
|
|
4598
|
+
``` ruby
|
|
3870
4599
|
def my_summarize(df, group_var)
|
|
3871
4600
|
df.group_by(group_var).
|
|
3872
|
-
summarize(a: :a.mean)
|
|
4601
|
+
summarize(a: R[:a].mean)
|
|
3873
4602
|
end
|
|
3874
4603
|
|
|
3875
|
-
puts my_summarize(:df, :g1)
|
|
4604
|
+
puts my_summarize(~R[:df], R[:g1])
|
|
3876
4605
|
```
|
|
3877
4606
|
|
|
3878
4607
|
```
|
|
3879
|
-
## # A tibble: 2
|
|
4608
|
+
## # A tibble: 2 × 2
|
|
3880
4609
|
## g1 a
|
|
3881
4610
|
## <dbl> <dbl>
|
|
3882
4611
|
## 1 1 2.5
|
|
@@ -3886,12 +4615,12 @@ puts my_summarize(:df, :g1)
|
|
|
3886
4615
|
It works!!! Well, let's make sure this was not just some coincidence
|
|
3887
4616
|
|
|
3888
4617
|
|
|
3889
|
-
```ruby
|
|
3890
|
-
puts my_summarize(:df, :g2)
|
|
4618
|
+
``` ruby
|
|
4619
|
+
puts my_summarize(~R[:df], R[:g2])
|
|
3891
4620
|
```
|
|
3892
4621
|
|
|
3893
4622
|
```
|
|
3894
|
-
## # A tibble: 2
|
|
4623
|
+
## # A tibble: 2 × 2
|
|
3895
4624
|
## g2 a
|
|
3896
4625
|
## <dbl> <dbl>
|
|
3897
4626
|
## 1 1 3
|
|
@@ -3907,7 +4636,7 @@ In the previous section we've managed to get rid of all NSE formulation for a si
|
|
|
3907
4636
|
does this remain true for more complex examples, or will the Galaaz way prove inpractical for
|
|
3908
4637
|
more complex code?
|
|
3909
4638
|
|
|
3910
|
-
In the next example
|
|
4639
|
+
In the next example Hadley proposes us to write a function that given an expression such as 'a'
|
|
3911
4640
|
or 'a * b', calculates three summaries. What we want a function that does the same as these R
|
|
3912
4641
|
statements:
|
|
3913
4642
|
|
|
@@ -3928,7 +4657,7 @@ summarise(df, mean = mean(a * b), sum = sum(a * b), n = n())
|
|
|
3928
4657
|
Let's try it in galaaz:
|
|
3929
4658
|
|
|
3930
4659
|
|
|
3931
|
-
```ruby
|
|
4660
|
+
``` ruby
|
|
3932
4661
|
def my_summarise2(df, expr)
|
|
3933
4662
|
df.summarize(
|
|
3934
4663
|
mean: E.mean(expr),
|
|
@@ -3937,9 +4666,10 @@ def my_summarise2(df, expr)
|
|
|
3937
4666
|
)
|
|
3938
4667
|
end
|
|
3939
4668
|
|
|
3940
|
-
puts my_summarise2((
|
|
3941
|
-
puts "
|
|
3942
|
-
|
|
4669
|
+
puts my_summarise2((~R[:df]), :a)
|
|
4670
|
+
puts "
|
|
4671
|
+
"
|
|
4672
|
+
puts my_summarise2((~R[:df]), R[:a] * R[:b])
|
|
3943
4673
|
```
|
|
3944
4674
|
|
|
3945
4675
|
```
|
|
@@ -3955,7 +4685,7 @@ careful about is the use of 'E' to build expressions from functions 'mean', 'sum
|
|
|
3955
4685
|
|
|
3956
4686
|
## Different input and output variable
|
|
3957
4687
|
|
|
3958
|
-
Now the next challenge presented by
|
|
4688
|
+
Now the next challenge presented by Hadley is to vary the name of the output variables based on
|
|
3959
4689
|
the received expression. So, if the input expression is 'a', we want our data frame columns to
|
|
3960
4690
|
be named 'mean\_a' and 'sum\_a'. Now, if the input expression is 'b', columns
|
|
3961
4691
|
should be named 'mean\_b' and 'sum\_b'.
|
|
@@ -3981,13 +4711,13 @@ mutate(df, mean_b = mean(b), sum_b = sum(b))
|
|
|
3981
4711
|
#> 4 2 2 5 4 3 15
|
|
3982
4712
|
#> # … with 1 more row
|
|
3983
4713
|
```
|
|
3984
|
-
In order to solve this problem in R,
|
|
4714
|
+
In order to solve this problem in R, Hadley needs to introduce some more new functions and notations:
|
|
3985
4715
|
'quo_name' and the ':=' operator from package 'rlang'
|
|
3986
4716
|
|
|
3987
4717
|
Here is our Ruby code:
|
|
3988
4718
|
|
|
3989
4719
|
|
|
3990
|
-
```ruby
|
|
4720
|
+
``` ruby
|
|
3991
4721
|
def my_mutate(df, expr)
|
|
3992
4722
|
mean_name = "mean_#{expr.to_s}"
|
|
3993
4723
|
sum_name = "sum_#{expr.to_s}"
|
|
@@ -3996,9 +4726,10 @@ def my_mutate(df, expr)
|
|
|
3996
4726
|
sum_name => E.sum(expr))
|
|
3997
4727
|
end
|
|
3998
4728
|
|
|
3999
|
-
puts my_mutate((
|
|
4000
|
-
puts "
|
|
4001
|
-
|
|
4729
|
+
puts my_mutate((~R[:df]), :a)
|
|
4730
|
+
puts "
|
|
4731
|
+
"
|
|
4732
|
+
puts my_mutate((~R[:df]), :b)
|
|
4002
4733
|
```
|
|
4003
4734
|
|
|
4004
4735
|
```
|
|
@@ -4026,25 +4757,25 @@ and variable mean\_name is not followed by ':' but by '=>'. This is standard Ru
|
|
|
4026
4757
|
|
|
4027
4758
|
## Capturing multiple variables
|
|
4028
4759
|
|
|
4029
|
-
Moving on with new complexities,
|
|
4760
|
+
Moving on with new complexities, Hadley proposes us to solve the problem in which the
|
|
4030
4761
|
summarise function will receive any number of grouping variables.
|
|
4031
4762
|
|
|
4032
4763
|
This again is quite standard Ruby. In order to receive an undefined number of paramenters
|
|
4033
4764
|
the paramenter is preceded by '*':
|
|
4034
4765
|
|
|
4035
4766
|
|
|
4036
|
-
```ruby
|
|
4767
|
+
``` ruby
|
|
4037
4768
|
def my_summarise3(df, *group_vars)
|
|
4038
4769
|
df.group_by(*group_vars).
|
|
4039
4770
|
summarise(a: E.mean(:a))
|
|
4040
4771
|
end
|
|
4041
4772
|
|
|
4042
|
-
puts my_summarise3((
|
|
4773
|
+
puts my_summarise3((~R[:df]), R[:g1], R[:g2])
|
|
4043
4774
|
```
|
|
4044
4775
|
|
|
4045
4776
|
```
|
|
4046
|
-
## # A tibble: 4
|
|
4047
|
-
## # Groups: g1 [
|
|
4777
|
+
## # A tibble: 4 × 3
|
|
4778
|
+
## # Groups: g1 [2]
|
|
4048
4779
|
## g1 g2 a
|
|
4049
4780
|
## <dbl> <dbl> <dbl>
|
|
4050
4781
|
## 1 1 1 3
|
|
@@ -4068,7 +4799,7 @@ In Ruby, there is no lazy evaluation of parameters and 'a' is always a variable
|
|
|
4068
4799
|
Variables assume their value as soon as they are used, so 'x = a' is immediately evaluate and
|
|
4069
4800
|
variable 'x' will receive the value of variable 'a' as soon as the Ruby statement is executed.
|
|
4070
4801
|
Ruby also provides the notion of a symbol; ':a' is a symbol and does not evaluate to anything.
|
|
4071
|
-
Galaaz uses Ruby symbols to build expressions that are not bound to anything: ':a.eq :b' is
|
|
4802
|
+
Galaaz uses Ruby symbols to build expressions that are not bound to anything: 'R[:a].eq R[:b]' is
|
|
4072
4803
|
clearly an expression and has no relationship whatsoever with the statment 'a = b'. By using
|
|
4073
4804
|
symbols, variables and expressions all the possible ambiguities that are found in R are
|
|
4074
4805
|
eliminated in Galaaz.
|
|
@@ -4078,7 +4809,7 @@ of input they are expecting, they might be expecting regular variables or they m
|
|
|
4078
4809
|
expecting expressions and the R function will know how to deal with an input of the form
|
|
4079
4810
|
'a = b', now for the Ruby developer it might not be immediately clear if it should call the
|
|
4080
4811
|
function passing the value 'true' if variable 'a' is equal to variable 'b' or if it should
|
|
4081
|
-
call the function passing the expression ':a.eq :b'.
|
|
4812
|
+
call the function passing the expression 'R[:a].eq R[:b]'.
|
|
4082
4813
|
|
|
4083
4814
|
|
|
4084
4815
|
## Advanced dplyr features
|
|
@@ -4101,28 +4832,29 @@ In the following examples, we show the use of functions 'group\_by\_at', 'summar
|
|
|
4101
4832
|
features of characters in the Starwars movies:
|
|
4102
4833
|
|
|
4103
4834
|
|
|
4104
|
-
```ruby
|
|
4105
|
-
puts (
|
|
4835
|
+
``` ruby
|
|
4836
|
+
puts (~R[:starwars]).head
|
|
4106
4837
|
```
|
|
4107
4838
|
|
|
4108
4839
|
```
|
|
4109
|
-
## # A tibble: 6
|
|
4110
|
-
## name
|
|
4111
|
-
## <chr>
|
|
4112
|
-
## 1 Luke… 172 77 blond fair blue 19 male
|
|
4113
|
-
## 2 C-3PO
|
|
4114
|
-
## 3 R2-D2
|
|
4115
|
-
## 4
|
|
4116
|
-
## 5 Leia… 150 49 brown light brown 19
|
|
4117
|
-
## 6 Owen
|
|
4118
|
-
## #
|
|
4840
|
+
## # A tibble: 6 × 14
|
|
4841
|
+
## name height mass hair_color skin_color eye_color birth_year sex gender
|
|
4842
|
+
## <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
|
|
4843
|
+
## 1 Luke Sky… 172 77 blond fair blue 19 male mascu…
|
|
4844
|
+
## 2 C-3PO 167 75 <NA> gold yellow 112 none mascu…
|
|
4845
|
+
## 3 R2-D2 96 32 <NA> white, bl… red 33 none mascu…
|
|
4846
|
+
## 4 Darth Va… 202 136 none white yellow 41.9 male mascu…
|
|
4847
|
+
## 5 Leia Org… 150 49 brown light brown 19 fema… femin…
|
|
4848
|
+
## 6 Owen Lars 178 120 brown, gr… light blue 52 male mascu…
|
|
4849
|
+
## # ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
|
|
4119
4850
|
## # vehicles <list>, starships <list>
|
|
4120
4851
|
```
|
|
4121
|
-
The grouped_mean function
|
|
4852
|
+
The grouped_mean function below will receive a grouping variable and calculate summaries for
|
|
4122
4853
|
the value\_variables given:
|
|
4123
4854
|
|
|
4124
4855
|
|
|
4125
|
-
```r
|
|
4856
|
+
``` r
|
|
4857
|
+
library(dplyr)
|
|
4126
4858
|
grouped_mean <- function(data, grouping_variables, value_variables) {
|
|
4127
4859
|
data %>%
|
|
4128
4860
|
group_by_at(grouping_variables) %>%
|
|
@@ -4133,7 +4865,22 @@ grouped_mean <- function(data, grouping_variables, value_variables) {
|
|
|
4133
4865
|
|
|
4134
4866
|
gm = starwars %>%
|
|
4135
4867
|
grouped_mean("eye_color", c("mass", "birth_year"))
|
|
4868
|
+
```
|
|
4136
4869
|
|
|
4870
|
+
```
|
|
4871
|
+
## Warning: `funs()` was deprecated in dplyr 0.8.0.
|
|
4872
|
+
## ℹ Please use a list of either functions or lambdas:
|
|
4873
|
+
##
|
|
4874
|
+
## # Simple named list: list(mean = mean, median = median)
|
|
4875
|
+
##
|
|
4876
|
+
## # Auto named with `tibble::lst()`: tibble::lst(mean, median)
|
|
4877
|
+
##
|
|
4878
|
+
## # Using lambdas list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))
|
|
4879
|
+
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
|
|
4880
|
+
## generated.
|
|
4881
|
+
```
|
|
4882
|
+
|
|
4883
|
+
``` r
|
|
4137
4884
|
as.data.frame(gm)
|
|
4138
4885
|
```
|
|
4139
4886
|
|
|
@@ -4159,20 +4906,20 @@ as.data.frame(gm)
|
|
|
4159
4906
|
The same code with Galaaz, becomes:
|
|
4160
4907
|
|
|
4161
4908
|
|
|
4162
|
-
```ruby
|
|
4909
|
+
``` ruby
|
|
4163
4910
|
def grouped_mean(data, grouping_variables, value_variables)
|
|
4164
4911
|
data.
|
|
4165
4912
|
group_by_at(grouping_variables).
|
|
4166
4913
|
mutate(count: E.n).
|
|
4167
|
-
summarise_at(E.c(value_variables, "count"),
|
|
4914
|
+
summarise_at(E.c(value_variables, "count"), ~R[:mean], na__rm: true).
|
|
4168
4915
|
rename_at(value_variables, E.funs(E.paste0("mean_", value_variables)))
|
|
4169
4916
|
end
|
|
4170
4917
|
|
|
4171
|
-
puts grouped_mean((
|
|
4918
|
+
puts grouped_mean((~R[:starwars]), "eye_color", E.c("mass", "birth_year"))
|
|
4172
4919
|
```
|
|
4173
4920
|
|
|
4174
4921
|
```
|
|
4175
|
-
## # A tibble: 15
|
|
4922
|
+
## # A tibble: 15 × 4
|
|
4176
4923
|
## eye_color mean_mass mean_birth_year count
|
|
4177
4924
|
## <chr> <dbl> <dbl> <dbl>
|
|
4178
4925
|
## 1 black 76.3 33 10
|
|
@@ -4192,17 +4939,19 @@ puts grouped_mean((~:starwars), "eye_color", E.c("mass", "birth_year"))
|
|
|
4192
4939
|
## 15 yellow 81.1 76.4 11
|
|
4193
4940
|
```
|
|
4194
4941
|
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4942
|
+
The examples above cover programmatic dplyr with string column names and `_at` helpers. The same
|
|
4943
|
+
Galaaz patterns (symbols, `E.*` for expression-safe functions, and Ruby methods on R-backed objects)
|
|
4944
|
+
extend to other tidyverse workflows; consult R package documentation for function-specific
|
|
4945
|
+
arguments.
|
|
4198
4946
|
|
|
4199
4947
|
# Contributing
|
|
4200
4948
|
|
|
4201
4949
|
* Fork it
|
|
4202
|
-
* Create your feature branch (git checkout -b my-new-feature)
|
|
4203
|
-
* Write
|
|
4204
|
-
|
|
4205
|
-
*
|
|
4206
|
-
*
|
|
4950
|
+
* Create your feature branch (`git checkout -b my-new-feature`)
|
|
4951
|
+
* Write tests — use **`bin/run_rspec`** or **`bin/run_all_rspec`** (JRuby or CRuby via
|
|
4952
|
+
**`GALAAZ_RUBY`**) so JVM flags and the load path match **`docs/testing.md`**
|
|
4953
|
+
* Commit your changes (`git commit -am 'Add some feature'`)
|
|
4954
|
+
* Push to the branch (`git push origin my-new-feature`)
|
|
4955
|
+
* Open a pull request
|
|
4207
4956
|
|
|
4208
4957
|
# References
|