galaaz 0.5.0 → 2.0.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 +26 -0
- data/LICENSE +0 -0
- data/README.md +1360 -636
- data/Rakefile +61 -41
- data/bin/galaaz-bootstrap +137 -0
- data/bin/galaaz-jruby +14 -0
- data/bin/galaaz_jruby_env.inc.sh +6 -0
- data/bin/gbookdown +64 -0
- data/bin/gknit +84 -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 +43 -0
- data/bin/run_example +14 -0
- data/bin/run_old_rspec +19 -0
- data/bin/run_rspec +23 -0
- data/bin/run_rspec_subset +38 -0
- data/bin/run_slow_rspec +19 -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-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-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 +852 -239
- data/blogs/manual/manual.log +1786 -0
- data/blogs/manual/manual.md +1360 -636
- 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-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/examples/50Plots_MasterList/Images/midwest-scatterplot.PNG +0 -0
- data/examples/50Plots_MasterList/ScatterPlot.rb +0 -0
- data/examples/50Plots_MasterList/scatter_plot.rb +0 -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 +53 -0
- data/examples/bioconductor_deseq2_airway/bench_r_three_same_process.R +34 -0
- data/examples/bioconductor_deseq2_airway/deseq2_airway_galaaz.rb +33 -0
- data/examples/bioconductor_deseq2_airway/deseq2_airway_galaaz_optimized.rb +34 -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 +13 -0
- data/examples/islr/ch2.spec.rb +37 -7
- data/examples/islr/ch3.spec.rb +11 -2
- data/examples/islr/ch3_boston.rb +27 -0
- data/examples/islr/ch3_multiple_regression.rb +0 -0
- data/examples/islr/ch6.spec.rb +24 -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 +3 -2
- data/examples/misc/moneyball.rb +0 -0
- data/examples/misc/subsetting.rb +0 -0
- data/examples/multithread_shards_to_r/shards_to_r.rb +67 -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 +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/density_gg.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_area.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_density.rb +2 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_dotplot.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_freqpoly.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_histogram.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/histogram_density.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/stat.rb +0 -0
- data/examples/sthda_ggplot/one_variable_discrete/bar.rb +0 -0
- data/examples/sthda_ggplot/qplots/box_violin_dot.rb +0 -0
- data/examples/sthda_ggplot/qplots/scatter_plots.rb +0 -0
- data/examples/sthda_ggplot/scatter_gg.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_bin2d.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_density2d.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_hex.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_cont/geom_point.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_cont/geom_smooth.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_cont/misc.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_function/geom_area.rb +4 -3
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_bar.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_boxplot.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_dotplot.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_jitter.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_line.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_violin.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_disc/geom_jitter.rb +0 -0
- data/examples/sthda_ggplot/two_variables_error/geom_crossbar.rb +0 -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 +147 -31
- data/lib/galaaz.rb +0 -0
- data/lib/galaaz_jruby.rb +22 -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 +194 -64
- 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: "Coupling Ruby (JRuby) and GNU R for data science"
|
|
4
4
|
author: "Rodrigo Botafogo"
|
|
5
|
-
tags: [Galaaz, Ruby, R,
|
|
6
|
-
date: "
|
|
7
|
-
bibliography: "/
|
|
5
|
+
tags: [Galaaz, Ruby, 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
|
|
|
@@ -29,8 +32,9 @@ Galaaz is a system for tightly coupling Ruby and R. Ruby is a powerful language,
|
|
|
29
32
|
community, a very large set of libraries and great for web development. However, it lacks
|
|
30
33
|
libraries for data science, statistics, scientific plotting and machine learning. On the
|
|
31
34
|
other hand, R is considered one of the most powerful languages for solving all of the above
|
|
32
|
-
problems.
|
|
33
|
-
|
|
35
|
+
problems. **Python** is a strong competitor: NumPy, pandas, SciPy, and scikit-learn are
|
|
36
|
+
widely used building blocks, and **PyPI** hosts many thousands of other packages for
|
|
37
|
+
numerical work, machine learning, and beyond.
|
|
34
38
|
|
|
35
39
|
With Galaaz we do not intend to re-implement any of the scientific libraries in R, we allow
|
|
36
40
|
for very tight coupling between the two languages to the point that the Ruby developer does
|
|
@@ -41,59 +45,39 @@ general-purpose programming language. It was designed and developed in the mid-1
|
|
|
41
45
|
"Matz" Matsumoto in Japan." It reached high popularity with the development of Ruby on Rails
|
|
42
46
|
(RoR) by David Heinemeier Hansson. RoR is a web application framework first released
|
|
43
47
|
around 2005. It makes extensive use of Ruby's metaprogramming features. With RoR,
|
|
44
|
-
Ruby became very popular. According to [Ruby
|
|
45
|
-
it
|
|
46
|
-
|
|
47
|
-
most popular language.
|
|
48
|
+
Ruby became very popular. According to [Ruby’s place in the TIOBE index](https://www.tiobe.com/tiobe-index/ruby/)
|
|
49
|
+
it peaked in popularity around 2008, then declined until 2015 when it started picking up again.
|
|
50
|
+
Ruby remains a significant language in web development and general-purpose scripting.
|
|
48
51
|
|
|
49
52
|
Python, a language similar to Ruby, ranks 4th in the index. Java, C and C++ take the
|
|
50
53
|
first three positions. Ruby is often criticized for its focus on web applications.
|
|
51
54
|
But Ruby can do [much more](https://github.com/markets/awesome-ruby) than just web applications.
|
|
52
|
-
Yet, for scientific computing, Ruby lags
|
|
53
|
-
|
|
55
|
+
Yet, for scientific computing, Ruby lags behind Python and R. Python offers Django and
|
|
56
|
+
similar frameworks for the web, plus NumPy, pandas, and a deep catalog of science and ML libraries.
|
|
54
57
|
R is a free software environment for statistical computing and graphics with thousands
|
|
55
58
|
of libraries for data analysis.
|
|
56
59
|
|
|
57
60
|
Until recently, there was no real perspective for Ruby to bridge this gap.
|
|
58
61
|
Implementing a complete scientific computing infrastructure would take too long.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
> * That library is not available in my language. I need to rewrite it.
|
|
79
|
-
> * That language would be the perfect fit for my problem, but we cannot
|
|
80
|
-
> run it in our environment.
|
|
81
|
-
> * That problem is already solved in my language, but the language is
|
|
82
|
-
> too slow.
|
|
83
|
-
>
|
|
84
|
-
> With GraalVM we aim to allow developers to freely choose the right language for
|
|
85
|
-
> the task at hand without making compromises.
|
|
86
|
-
|
|
87
|
-
As stated above, GraalVM is a _universal_ virtual machine that allows Ruby and R (and other
|
|
88
|
-
languages) to run on the same environment. GraalVM allows polyglot applications to
|
|
89
|
-
_seamlessly_ interact with one another and pass values from one language to the other.
|
|
90
|
-
Although a great idea, GraalVM still requires application writers to know several languages.
|
|
91
|
-
To eliminate that requirement, we built Galaaz, a gem for Ruby, to tightly couple
|
|
92
|
-
Ruby and R and allow those languages to interact in a way that the user will be unaware
|
|
93
|
-
of such interaction. In other words, a Ruby programmer will be able to use all
|
|
94
|
-
the capabilities of R without knowing the R syntax.
|
|
95
|
-
|
|
96
|
-
Library wrapping is a usual way of bringing features from one language into another.
|
|
62
|
+
|
|
63
|
+
**Galaaz 2.0** couples **JRuby** (Ruby on the JVM) with **GNU R**—the same R you use for
|
|
64
|
+
CRAN and Bioconductor. Ruby and R run in **separate processes**; the **Galaaz bridge**
|
|
65
|
+
sends requests to R and returns results to Ruby. From your point of view you still write
|
|
66
|
+
Ruby: `R.c(...)`, `R.library('ggplot2')`, `~R[:mtcars]`, and dplyr-style chains on R objects.
|
|
67
|
+
You do not need to learn R syntax to get a lot done, though reading R documentation for
|
|
68
|
+
individual packages remains useful.
|
|
69
|
+
|
|
70
|
+
Earlier experiments with Galaaz used Oracle’s **GraalVM** with TruffleRuby and FastR so that
|
|
71
|
+
Ruby and R could share one runtime. That path is no longer the focus: **standard GNU R**
|
|
72
|
+
gives full compatibility with the R package ecosystem (including compiled extensions and
|
|
73
|
+
Bioconductor) while JRuby gives a mature Ruby with **real multithreading** for application
|
|
74
|
+
and I/O code.
|
|
75
|
+
|
|
76
|
+
The bridge handles **communication and typing** between the two worlds; large tables can
|
|
77
|
+
also flow through **Apache Arrow** on the R side when you use the optional helpers described
|
|
78
|
+
later in this manual.
|
|
79
|
+
|
|
80
|
+
Library wrapping is a common way to bring features from one language into another.
|
|
97
81
|
To improve performance, Python often wraps more efficient C libraries. For the
|
|
98
82
|
Python developer, the existence of such C libraries is hidden. The problem with
|
|
99
83
|
library wrapping is that for any new library, there is the need to handcraft a new
|
|
@@ -118,27 +102,265 @@ 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 + GNU R)** means the tool is wired to **`jruby`** and
|
|
112
|
+
**`bin/galaaz_jruby_env.inc.sh`** (or equivalent logic in Ruby via `lib/galaaz_jruby.rb`). **Legacy**
|
|
113
|
+
means the script still targets **GraalVM** polyglot Ruby / FastR-era invocation and is **not**
|
|
114
|
+
expected to work on a typical JRuby-only setup.
|
|
115
|
+
|
|
116
|
+
**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.
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
<table class="table table-striped table-condensed" style="margin-left: auto; margin-right: auto;">
|
|
121
|
+
<thead>
|
|
122
|
+
<tr>
|
|
123
|
+
<th style="text-align:left;"> Script </th>
|
|
124
|
+
<th style="text-align:left;"> Role </th>
|
|
125
|
+
<th style="text-align:left;"> 2.0? </th>
|
|
126
|
+
</tr>
|
|
127
|
+
</thead>
|
|
128
|
+
<tbody>
|
|
129
|
+
<tr>
|
|
130
|
+
<td style="text-align:left;"> galaaz-bootstrap </td>
|
|
131
|
+
<td style="text-align:left;"> WSL2 helper: Docker checks; optional TinyTeX or poppler for gKnit PDF. </td>
|
|
132
|
+
<td style="text-align:left;"> Yes* </td>
|
|
133
|
+
</tr>
|
|
134
|
+
<tr>
|
|
135
|
+
<td style="text-align:left;"> galaaz-jruby </td>
|
|
136
|
+
<td style="text-align:left;"> JRuby with repo lib/ on LOAD_PATH and required JVM flags (e.g. Arrow). </td>
|
|
137
|
+
<td style="text-align:left;"> Yes </td>
|
|
138
|
+
</tr>
|
|
139
|
+
<tr>
|
|
140
|
+
<td style="text-align:left;"> galaaz_jruby_env.inc.sh </td>
|
|
141
|
+
<td style="text-align:left;"> Sourced by bash wrappers; sets GALAAZ_REQUIRED_JRUBY_J_ARGS. </td>
|
|
142
|
+
<td style="text-align:left;"> Yes† </td>
|
|
143
|
+
</tr>
|
|
144
|
+
<tr>
|
|
145
|
+
<td style="text-align:left;"> install-tinytex </td>
|
|
146
|
+
<td style="text-align:left;"> Install TinyTeX for PDF output. </td>
|
|
147
|
+
<td style="text-align:left;"> Yes </td>
|
|
148
|
+
</tr>
|
|
149
|
+
</tbody>
|
|
150
|
+
</table>
|
|
151
|
+
|
|
152
|
+
\* Where WSL/Docker apply. **`galaaz-bootstrap` flags:** `--check`, `--apply`, `--runtime` (`docker` \| `local` \| `auto`), `--[no-]prompt-doc-tools`.
|
|
153
|
+
|
|
154
|
+
† Not run directly.
|
|
155
|
+
|
|
156
|
+
**`galaaz-jruby` examples** (from repo root):
|
|
157
|
+
|
|
158
|
+
```text
|
|
159
|
+
bin/galaaz-jruby my_script.rb
|
|
160
|
+
bin/galaaz-jruby -S rspec
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Interactive use, examples, and Rake
|
|
164
|
+
|
|
165
|
+
<table class="table table-striped table-condensed" style="margin-left: auto; margin-right: auto;">
|
|
166
|
+
<thead>
|
|
167
|
+
<tr>
|
|
168
|
+
<th style="text-align:left;"> Script </th>
|
|
169
|
+
<th style="text-align:left;"> Role </th>
|
|
170
|
+
<th style="text-align:left;"> 2.0? </th>
|
|
171
|
+
</tr>
|
|
172
|
+
</thead>
|
|
173
|
+
<tbody>
|
|
174
|
+
<tr>
|
|
175
|
+
<td style="text-align:left;"> gstudio </td>
|
|
176
|
+
<td style="text-align:left;"> IRB or Pry with Galaaz preloaded (JRuby + JVM flags). </td>
|
|
177
|
+
<td style="text-align:left;"> Yes </td>
|
|
178
|
+
</tr>
|
|
179
|
+
<tr>
|
|
180
|
+
<td style="text-align:left;"> run_example </td>
|
|
181
|
+
<td style="text-align:left;"> Run one Ruby file using the same JRuby/JVM setup as tests. </td>
|
|
182
|
+
<td style="text-align:left;"> Yes </td>
|
|
183
|
+
</tr>
|
|
184
|
+
<tr>
|
|
185
|
+
<td style="text-align:left;"> galaaz </td>
|
|
186
|
+
<td style="text-align:left;"> Forward arguments to rake (needs rake; usually JRuby). </td>
|
|
187
|
+
<td style="text-align:left;"> Yes </td>
|
|
188
|
+
</tr>
|
|
189
|
+
</tbody>
|
|
190
|
+
</table>
|
|
191
|
+
|
|
192
|
+
## gKnit and document drafts
|
|
193
|
+
|
|
194
|
+
<table class="table table-striped table-condensed" style="margin-left: auto; margin-right: auto;">
|
|
195
|
+
<thead>
|
|
196
|
+
<tr>
|
|
197
|
+
<th style="text-align:left;"> Script </th>
|
|
198
|
+
<th style="text-align:left;"> Role </th>
|
|
199
|
+
<th style="text-align:left;"> 2.0? </th>
|
|
200
|
+
</tr>
|
|
201
|
+
</thead>
|
|
202
|
+
<tbody>
|
|
203
|
+
<tr>
|
|
204
|
+
<td style="text-align:left;"> gknit </td>
|
|
205
|
+
<td style="text-align:left;"> Knit .Rmd via JRuby and R Markdown render. </td>
|
|
206
|
+
<td style="text-align:left;"> Yes </td>
|
|
207
|
+
</tr>
|
|
208
|
+
<tr>
|
|
209
|
+
<td style="text-align:left;"> gknit-draft </td>
|
|
210
|
+
<td style="text-align:left;"> Drafts from rticles-style templates; wrapper still uses legacy polyglot ruby. </td>
|
|
211
|
+
<td style="text-align:left;"> Legacy </td>
|
|
212
|
+
</tr>
|
|
213
|
+
<tr>
|
|
214
|
+
<td style="text-align:left;"> gknit-draft.rb </td>
|
|
215
|
+
<td style="text-align:left;"> Ruby entry: GKnit.draft (use with JRuby + LOAD_PATH). </td>
|
|
216
|
+
<td style="text-align:left;"> JRuby </td>
|
|
217
|
+
</tr>
|
|
218
|
+
<tr>
|
|
219
|
+
<td style="text-align:left;"> gknit_Rscript </td>
|
|
220
|
+
<td style="text-align:left;"> Polyglot Rscript launcher; hard-coded LOAD_PATH sample. </td>
|
|
221
|
+
<td style="text-align:left;"> No </td>
|
|
222
|
+
</tr>
|
|
223
|
+
</tbody>
|
|
224
|
+
</table>
|
|
225
|
+
|
|
226
|
+
**`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.
|
|
227
|
+
|
|
228
|
+
Prefer **`galaaz-jruby`** for **`gknit-draft`** workflows until that wrapper matches the **`gknit`** stack.
|
|
229
|
+
|
|
230
|
+
## Tests
|
|
231
|
+
|
|
232
|
+
<table class="table table-striped table-condensed" style="margin-left: auto; margin-right: auto;">
|
|
233
|
+
<thead>
|
|
234
|
+
<tr>
|
|
235
|
+
<th style="text-align:left;"> Script </th>
|
|
236
|
+
<th style="text-align:left;"> Role </th>
|
|
237
|
+
<th style="text-align:left;"> 2.0? </th>
|
|
238
|
+
</tr>
|
|
239
|
+
</thead>
|
|
240
|
+
<tbody>
|
|
241
|
+
<tr>
|
|
242
|
+
<td style="text-align:left;"> run_rspec </td>
|
|
243
|
+
<td style="text-align:left;"> Top-level specs/*_spec.rb with spec_helper (see docs/testing.md). </td>
|
|
244
|
+
<td style="text-align:left;"> Yes </td>
|
|
245
|
+
</tr>
|
|
246
|
+
<tr>
|
|
247
|
+
<td style="text-align:left;"> run_all_rspec </td>
|
|
248
|
+
<td style="text-align:left;"> Compile ext/new_bridge; run specs/ and new_bridge_specs/ together. </td>
|
|
249
|
+
<td style="text-align:left;"> Yes </td>
|
|
250
|
+
</tr>
|
|
251
|
+
<tr>
|
|
252
|
+
<td style="text-align:left;"> run_slow_rspec </td>
|
|
253
|
+
<td style="text-align:left;"> Suites under slow-specs/ (read script header for spec_helper). </td>
|
|
254
|
+
<td style="text-align:left;"> Yes </td>
|
|
255
|
+
</tr>
|
|
256
|
+
<tr>
|
|
257
|
+
<td style="text-align:left;"> run_old_rspec </td>
|
|
258
|
+
<td style="text-align:left;"> Legacy suites under old_specs/. </td>
|
|
259
|
+
<td style="text-align:left;"> Yes </td>
|
|
260
|
+
</tr>
|
|
261
|
+
<tr>
|
|
262
|
+
<td style="text-align:left;"> run_rspec_subset </td>
|
|
263
|
+
<td style="text-align:left;"> Numbered subset 1–18 (Documentation/Spec_Subsets.md). </td>
|
|
264
|
+
<td style="text-align:left;"> Yes </td>
|
|
265
|
+
</tr>
|
|
266
|
+
</tbody>
|
|
267
|
+
</table>
|
|
268
|
+
|
|
269
|
+
## Other
|
|
270
|
+
|
|
271
|
+
<table class="table table-striped table-condensed" style="margin-left: auto; margin-right: auto;">
|
|
272
|
+
<thead>
|
|
273
|
+
<tr>
|
|
274
|
+
<th style="text-align:left;"> Script </th>
|
|
275
|
+
<th style="text-align:left;"> Role </th>
|
|
276
|
+
<th style="text-align:left;"> 2.0? </th>
|
|
277
|
+
</tr>
|
|
278
|
+
</thead>
|
|
279
|
+
<tbody>
|
|
280
|
+
<tr>
|
|
281
|
+
<td style="text-align:left;"> grun </td>
|
|
282
|
+
<td style="text-align:left;"> Graal-era launcher: polyglot ruby with --jvm. Use galaaz-jruby -S instead. </td>
|
|
283
|
+
<td style="text-align:left;"> No </td>
|
|
284
|
+
</tr>
|
|
285
|
+
<tr>
|
|
286
|
+
<td style="text-align:left;"> gstudio_irb.rb / gstudio_pry.rb </td>
|
|
287
|
+
<td style="text-align:left;"> Loaded by gstudio; not meant to be run standalone. </td>
|
|
288
|
+
<td style="text-align:left;"> Yes </td>
|
|
289
|
+
</tr>
|
|
290
|
+
</tbody>
|
|
291
|
+
</table>
|
|
292
|
+
|
|
293
|
+
For day-to-day **2.0** use, rely on **`bin/galaaz-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 JRuby path as **`gknit`**.
|
|
294
|
+
|
|
121
295
|
# System Compatibility
|
|
122
296
|
|
|
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)
|
|
297
|
+
Typical development and CI targets:
|
|
129
298
|
|
|
130
|
-
|
|
299
|
+
* **Linux** — recent Ubuntu LTS or comparable distributions (x86_64).
|
|
300
|
+
* **macOS** — recent releases with JRuby and GNU R available.
|
|
301
|
+
* **Windows** — use **WSL2** (same Linux stack as above); native Windows is not the primary target.
|
|
302
|
+
|
|
303
|
+
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
304
|
|
|
132
|
-
|
|
133
|
-
* FastR
|
|
305
|
+
# Dependencies
|
|
134
306
|
|
|
307
|
+
* **JRuby** — Galaaz 2.0 requires JRuby (tested with **10.1.1.0**) and a matching **JDK** (tested with **Java 21**). MRI Ruby is not supported.
|
|
308
|
+
* **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.
|
|
309
|
+
* **galaaz gem** — runtime dependency `msgpack` is pulled in by `gem install`.
|
|
310
|
+
* Optional: **Docker** — if you run R in a container (common on WSL2); see bootstrap below.
|
|
311
|
+
* 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
312
|
|
|
136
313
|
# Installation
|
|
137
314
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
315
|
+
The supported install is **`gem install` + compile the gatekeeper**. You do not need a git clone.
|
|
316
|
+
|
|
317
|
+
1. Install **JRuby**, a compatible **JDK**, and **GNU R** (with `Rscript` and a C++ compiler).
|
|
318
|
+
2. In R, install **Rcpp**: `install.packages("Rcpp")`.
|
|
319
|
+
3. Install the gem: `jruby -S gem install galaaz`
|
|
320
|
+
4. Compile the native gatekeeper from the installed gem:
|
|
321
|
+
|
|
322
|
+
```
|
|
323
|
+
gem_dir="$(jruby -e "puts Gem::Specification.find_by_name('galaaz').full_gem_path")"
|
|
324
|
+
make -C "${gem_dir}/ext/new_bridge" all
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
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).
|
|
328
|
+
|
|
329
|
+
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.
|
|
330
|
+
|
|
331
|
+
A **table of all `bin/` scripts** (bootstrap, JRuby wrapper, gstudio, gknit, test runners, and which ones are legacy) is in the section **Command-line tools (`bin/`)** earlier in this manual.
|
|
332
|
+
|
|
333
|
+
### From a repository checkout (contributors)
|
|
334
|
+
|
|
335
|
+
1. Install **bundler** if needed, then run **`jruby -S bundle install`** in the repository root.
|
|
336
|
+
2. Build the bridge native code: **`make -C ext/new_bridge all`** (or **`rake compile_gatekeeper`**).
|
|
337
|
+
3. Run scripts with **`bin/galaaz-jruby`** (sources **`bin/galaaz_jruby_env.inc.sh`** and adds **`-I lib`**).
|
|
338
|
+
|
|
339
|
+
Maintainers can prove a built `.gem` on a throwaway Ubuntu machine (no repo inside the container) with **`./docker/cold-install/run.sh`**.
|
|
340
|
+
|
|
341
|
+
## Windows + WSL2 (optional: Docker / R in a container)
|
|
342
|
+
|
|
343
|
+
If you run Galaaz on Windows through WSL2 and want containerized R instances,
|
|
344
|
+
Docker Desktop is the supported setup.
|
|
345
|
+
|
|
346
|
+
1. Install Docker Desktop on Windows:
|
|
347
|
+
- https://www.docker.com/products/docker-desktop/
|
|
348
|
+
2. Open Docker Desktop and enable WSL integration:
|
|
349
|
+
- Settings > Resources > WSL Integration
|
|
350
|
+
- Enable integration for your target distro
|
|
351
|
+
- Apply & Restart Docker Desktop
|
|
352
|
+
3. In WSL, run Galaaz bootstrap:
|
|
353
|
+
|
|
354
|
+
> ruby bin/galaaz-bootstrap --apply
|
|
355
|
+
> ruby bin/galaaz-bootstrap --check
|
|
356
|
+
|
|
357
|
+
Expected result:
|
|
358
|
+
- docker CLI available
|
|
359
|
+
- docker compose available
|
|
360
|
+
- docker daemon reachable (`docker info` works)
|
|
361
|
+
|
|
362
|
+
If bootstrap reports daemon is unreachable, check Docker Desktop is running and
|
|
363
|
+
WSL integration is enabled for the distro where Galaaz is installed.
|
|
142
364
|
|
|
143
365
|
# Usage
|
|
144
366
|
|
|
@@ -147,7 +369,7 @@ Galaaz is the Portuguese name for "Galahad". From Wikipedia:
|
|
|
147
369
|
> gstudio
|
|
148
370
|
|
|
149
371
|
|
|
150
|
-
```ruby
|
|
372
|
+
``` ruby
|
|
151
373
|
vec = R.c(1, 2, 3, 4)
|
|
152
374
|
puts vec
|
|
153
375
|
```
|
|
@@ -172,7 +394,7 @@ Galaaz is the Portuguese name for "Galahad". From Wikipedia:
|
|
|
172
394
|
|
|
173
395
|
> galaaz -T
|
|
174
396
|
|
|
175
|
-
Shows a list with all available
|
|
397
|
+
Shows a list with all available executable tasks. To execute a task, substitute the
|
|
176
398
|
'rake' word in the list with 'galaaz'. For instance, the following line shows up
|
|
177
399
|
after 'galaaz -T'
|
|
178
400
|
|
|
@@ -182,17 +404,209 @@ Galaaz is the Portuguese name for "Galahad". From Wikipedia:
|
|
|
182
404
|
|
|
183
405
|
> galaaz master_list:scatter_plot
|
|
184
406
|
|
|
407
|
+
# JRuby, multithreading, and the R bridge
|
|
185
408
|
|
|
186
|
-
|
|
409
|
+
Galaaz 2.0 runs Ruby on **JRuby**, so your application can use **real parallel threads** for
|
|
410
|
+
I/O-bound work (HTTP clients, database connections, message consumers, and so on). R itself is
|
|
411
|
+
still executed in a **single GNU R process** behind the Galaaz bridge.
|
|
412
|
+
|
|
413
|
+
When several Ruby threads call into R at the same time, the bridge **serializes** those calls:
|
|
414
|
+
each request is matched to a reply using an internal per-call **queue**, so you do not need to
|
|
415
|
+
add your own mutex around every `R.foo` from application threads. (You should still use normal
|
|
416
|
+
Ruby synchronization when **Ruby** data structures are shared between threads—for example, when
|
|
417
|
+
appending rows from each thread into a shared array before sending them to R.)
|
|
418
|
+
|
|
419
|
+
A practical pattern is:
|
|
420
|
+
|
|
421
|
+
1. Use threads (or a connection pool) to read from **multiple databases or shards** in parallel.
|
|
422
|
+
2. Merge the rows in Ruby under a `Mutex` if you collect into one structure.
|
|
423
|
+
3. Hand the merged table to R **once** (for example with `R::Arrow.from_ruby_batches` and dplyr,
|
|
424
|
+
or by building a data frame) so heavy statistics run in R with fewer bridge round-trips.
|
|
425
|
+
|
|
426
|
+
A runnable sketch lives in
|
|
427
|
+
`examples/multithread_shards_to_r/shards_to_r.rb` (simulated shard queries; swap in your DB
|
|
428
|
+
driver). For concurrency tests on the bridge itself, see `specs/bridge_concurrent_spec.rb` and
|
|
429
|
+
`specs/arrow_from_ruby_batches_spec.rb`.
|
|
430
|
+
|
|
431
|
+
## Long-running R calls and a completion block
|
|
432
|
+
|
|
433
|
+
For R work that can take a long time, the bridge can avoid a Ruby-side **wait timeout** by
|
|
434
|
+
scheduling the call and resuming in a **block** when the `RET` arrives.
|
|
435
|
+
|
|
436
|
+
- **`R.eval_r_async(code, timeout: nil) { |result| ... }`** — string eval; on success, `result.value`
|
|
437
|
+
is the same formatted string as **`R.eval_r`** (use `timeout: nil` for no Ruby-side limit).
|
|
438
|
+
- **`R::Async.<rname>(...) { |result| ... }`** — same dispatch as **`R.<rname>(...)`**, but async;
|
|
439
|
+
on success, `result.value` is an **`R::Object`** (or unboxed Ruby value / Symbol), like synchronous
|
|
440
|
+
**`R.<rname>`**. Optional keyword **`timeout:`** applies a Ruby-side wait limit (completion receives
|
|
441
|
+
**`NewBridge::SessionClient::TimeoutError`** if R is too slow).
|
|
442
|
+
|
|
443
|
+
**Important:** **`R.foo(...) { |x| }`** is already used for dplyr-style scopes (`R::Support.new_scope`),
|
|
444
|
+
so async R calls must use **`R::Async`** or **`R.eval_r_async`**, not a bare **`R.foo` with a block.**
|
|
445
|
+
|
|
446
|
+
`NewBridge::EvalResult` exposes **`#ok?`**, **`#value`**, and **`#error`**. The completion block runs on a
|
|
447
|
+
**background thread** (not the bridge reader thread).
|
|
448
|
+
|
|
449
|
+
The example below is **plain Ruby** (no Rails). The R snippet sleeps (standing in for heavy work) and then
|
|
450
|
+
returns an integer so the success branch shows a **non-nil** value. (`Sys.sleep` alone returns **NULL** in R;
|
|
451
|
+
on success **`result.value`** is then **`nil`** in Ruby—that is expected, not a bridge error.)
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
``` ruby
|
|
455
|
+
require 'thread'
|
|
456
|
+
|
|
457
|
+
completion = Queue.new
|
|
458
|
+
|
|
459
|
+
R.eval_r_async('({ Sys.sleep(0.3); 42L })', timeout: nil) do |result|
|
|
460
|
+
if result.ok?
|
|
461
|
+
puts "[completion] R finished; eval_r-style value: #{result.value.inspect}"
|
|
462
|
+
else
|
|
463
|
+
puts "[completion] R/bridge error: #{result.error.class}: #{result.error.message}"
|
|
464
|
+
end
|
|
465
|
+
completion.push(:done)
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
3.times do |i|
|
|
469
|
+
puts "[main] other Ruby work step #{i + 1}"
|
|
470
|
+
sleep 0.05
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
completion.pop
|
|
474
|
+
puts "[main] R completion has run; exiting."
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
```
|
|
478
|
+
## [main] other Ruby work step 1
|
|
479
|
+
## [main] other Ruby work step 2
|
|
480
|
+
## [main] other Ruby work step 3
|
|
481
|
+
## [completion] R finished; eval_r-style value: "[1] 42"
|
|
482
|
+
## [main] R completion has run; exiting.
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
In a **web application**, the HTTP response usually ends before R finishes, so you would not
|
|
486
|
+
`Queue#pop` in the controller; you would persist an identifier, let the completion block write
|
|
487
|
+
the outcome to storage, and notify the client (poll, WebSocket, Turbo Stream, etc.). The plain
|
|
488
|
+
Ruby pattern above is only to show **when** the result exists (inside the block, or after data
|
|
489
|
+
written there is observed elsewhere). Runnable specs live in **`new_bridge_specs/eval_r_async_spec.rb`**.
|
|
490
|
+
|
|
491
|
+
## Galaaz + Rails (JRuby) integration baseline
|
|
492
|
+
|
|
493
|
+
This section documents the baseline we used to create a working Rails app with Galaaz in WSL.
|
|
494
|
+
The goals were:
|
|
495
|
+
|
|
496
|
+
1. Rails boots under **JRuby**.
|
|
497
|
+
2. Galaaz is loaded from a local checkout (before publishing to RubyGems).
|
|
498
|
+
3. A request path can execute **`R.eval(...)`** and return a result.
|
|
499
|
+
|
|
500
|
+
### 1) Create the app with JRuby-friendly options
|
|
501
|
+
|
|
502
|
+
Rails defaults can pull gems that are not ideal on JRuby-first setups (for example sqlite native
|
|
503
|
+
extension paths and deployment extras). A minimal app avoids early friction:
|
|
504
|
+
|
|
505
|
+
```bash
|
|
506
|
+
cd /home/rbotafogo/desenv_linux
|
|
507
|
+
jruby -S rails new hedi --skip-git --minimal --skip-kamal --skip-solid --skip-active-record
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
Then install gems:
|
|
511
|
+
|
|
512
|
+
```bash
|
|
513
|
+
cd /home/rbotafogo/desenv_linux/hedi
|
|
514
|
+
jruby -S bundle install
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### 2) Use Galaaz as a local path gem
|
|
518
|
+
|
|
519
|
+
For local development we keep a stable path:
|
|
187
520
|
|
|
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.
|
|
521
|
+
- `~/gems/galaaz` -> symlink to your Galaaz checkout
|
|
522
|
+
- optional built gem archive in `~/gems/pkg/`
|
|
192
523
|
|
|
524
|
+
In Rails `Gemfile`:
|
|
193
525
|
|
|
194
526
|
```ruby
|
|
195
|
-
|
|
527
|
+
gem "galaaz", path: "/home/rbotafogo/gems/galaaz", require: false
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
And load after Rails boot in `config/application.rb`:
|
|
531
|
+
|
|
532
|
+
```ruby
|
|
533
|
+
config.after_initialize { require "galaaz" }
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
Why `require: false` + `after_initialize`? In this integration, loading Galaaz too early via
|
|
537
|
+
`Bundler.require` triggered Rails/JRuby initialization failures.
|
|
538
|
+
|
|
539
|
+
### 3) Simple request-path smoke test
|
|
540
|
+
|
|
541
|
+
A direct smoke test from Rails runner:
|
|
542
|
+
|
|
543
|
+
```bash
|
|
544
|
+
cd /home/rbotafogo/desenv_linux/hedi
|
|
545
|
+
jruby -S bundle exec rails runner "puts R.eval('sum(c(1,2,3,4,5))').inspect"
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
Expected output:
|
|
549
|
+
|
|
550
|
+
```text
|
|
551
|
+
15.0
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### 4) HTTP endpoint pattern
|
|
555
|
+
|
|
556
|
+
For this baseline, a small Rack endpoint was the most stable first step to prove request-time R
|
|
557
|
+
evaluation. (A full ActionController stack can be enabled later as the app evolves.)
|
|
558
|
+
|
|
559
|
+
Minimal pattern:
|
|
560
|
+
|
|
561
|
+
1. Define a Rack app class under `lib/` that runs `R.eval(...)` and returns HTML/JSON.
|
|
562
|
+
2. Point a route to that Rack app (`root to: MyRackApp`).
|
|
563
|
+
3. Verify with browser/curl.
|
|
564
|
+
|
|
565
|
+
### 5) Running from WSL and opening from Windows
|
|
566
|
+
|
|
567
|
+
Recommended bind:
|
|
568
|
+
|
|
569
|
+
```bash
|
|
570
|
+
jruby -S bundle exec rails server -b 0.0.0.0 -p 3000
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
Then open from Windows:
|
|
574
|
+
|
|
575
|
+
- `http://localhost:3000` (usually works with WSL localhost forwarding), or
|
|
576
|
+
- `http://<wsl-ip>:3000` if needed.
|
|
577
|
+
|
|
578
|
+
In development, if Host Authorization blocks requests with unexpected Host headers, use:
|
|
579
|
+
|
|
580
|
+
```ruby
|
|
581
|
+
# config/environments/development.rb
|
|
582
|
+
config.hosts.clear
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
### 6) Troubleshooting checklist
|
|
586
|
+
|
|
587
|
+
- `Could not find ... in locally installed gems`:
|
|
588
|
+
run `jruby -S bundle install` in the Rails app directory.
|
|
589
|
+
- Stale PID after crash:
|
|
590
|
+
remove `tmp/pids/server.pid`.
|
|
591
|
+
- Local Galaaz path changed:
|
|
592
|
+
verify `Gemfile` path target exists and rerun bundler.
|
|
593
|
+
- R runtime issues:
|
|
594
|
+
confirm GNU R is installed and on `PATH` in the same shell where Rails runs.
|
|
595
|
+
|
|
596
|
+
As new Rails features are added (controllers, jobs, websockets, background rendering, plot
|
|
597
|
+
generation), extend this section with concrete, runnable snippets and the associated operational
|
|
598
|
+
checks.
|
|
599
|
+
|
|
600
|
+
# Accessing R from Ruby
|
|
601
|
+
|
|
602
|
+
One of the nice aspects of Galaaz is that variables and functions defined in R can
|
|
603
|
+
be easily accessed from Ruby. For instance, to access the `mtcars` data frame from R
|
|
604
|
+
in Ruby, we use the symbol `:mtcars` preceded by the `~` operator: `~R[:mtcars]` retrieves the
|
|
605
|
+
value of the `mtcars` object in R.
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
``` ruby
|
|
609
|
+
puts ~R[:mtcars]
|
|
196
610
|
```
|
|
197
611
|
|
|
198
612
|
```
|
|
@@ -231,11 +645,36 @@ puts ~:mtcars
|
|
|
231
645
|
## Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
|
|
232
646
|
```
|
|
233
647
|
|
|
234
|
-
|
|
235
|
-
|
|
648
|
+
## Scoped symbols and lexical scoping
|
|
649
|
+
|
|
650
|
+
Galaaz 2.0 uses **scoped symbols** by default. The canonical style is `R[:name]`:
|
|
651
|
+
|
|
652
|
+
- `~R[:mtcars]` fetches an R object by name.
|
|
653
|
+
- `R[:a] + R[:b]` builds an expression.
|
|
654
|
+
- `R[:year].up_to(R[:day])` builds range expressions.
|
|
236
655
|
|
|
656
|
+
If you prefer the terse `:x` syntax, you can opt in with lexical scoping using a Ruby refinement:
|
|
237
657
|
|
|
238
658
|
```ruby
|
|
659
|
+
module MyScript
|
|
660
|
+
using Galaaz::SymbolDSL
|
|
661
|
+
|
|
662
|
+
def self.run
|
|
663
|
+
expr = :a + :b
|
|
664
|
+
puts expr
|
|
665
|
+
puts ~:mtcars
|
|
666
|
+
end
|
|
667
|
+
end
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
`using Galaaz::SymbolDSL` is **lexically scoped**: only code in that module/file scope gets `:x` DSL behavior.
|
|
671
|
+
Outside that scope, plain Ruby `Symbol` behavior is unchanged.
|
|
672
|
+
|
|
673
|
+
To access an R function from Ruby, the R function needs to be preceded by `R.` scoping.
|
|
674
|
+
Below we see an example of creating a R::Vector by calling the 'c' R function
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
``` ruby
|
|
239
678
|
puts vec = R.c(1.0, 2.0, 3.0, 4.0)
|
|
240
679
|
```
|
|
241
680
|
|
|
@@ -245,7 +684,7 @@ puts vec = R.c(1.0, 2.0, 3.0, 4.0)
|
|
|
245
684
|
Note that 'vec' is an object of type R::Vector:
|
|
246
685
|
|
|
247
686
|
|
|
248
|
-
```ruby
|
|
687
|
+
``` ruby
|
|
249
688
|
puts vec.class
|
|
250
689
|
```
|
|
251
690
|
|
|
@@ -257,12 +696,12 @@ R::Object. In R, there is also a function 'class'. In order to access that funct
|
|
|
257
696
|
can call method 'rclass' in the R::Object:
|
|
258
697
|
|
|
259
698
|
|
|
260
|
-
```ruby
|
|
699
|
+
``` ruby
|
|
261
700
|
puts vec.rclass
|
|
262
701
|
```
|
|
263
702
|
|
|
264
703
|
```
|
|
265
|
-
##
|
|
704
|
+
## numeric
|
|
266
705
|
```
|
|
267
706
|
When working with R::Object(s), it is possible to use the '.' operator to pipe operations.
|
|
268
707
|
When using '.', the object to which the '.' is applied becomes the first argument of the
|
|
@@ -271,7 +710,7 @@ two vectors or more vectors (in R, there are no scalar values, scalars are conve
|
|
|
271
710
|
vectors of size 1. Within Galaaz, scalar parameter is converted to a size one vector):
|
|
272
711
|
|
|
273
712
|
|
|
274
|
-
```ruby
|
|
713
|
+
``` ruby
|
|
275
714
|
puts R.c(vec, 10, 20, 30)
|
|
276
715
|
```
|
|
277
716
|
|
|
@@ -281,18 +720,18 @@ puts R.c(vec, 10, 20, 30)
|
|
|
281
720
|
The call above to the 'c' function can also be done using '.' notation:
|
|
282
721
|
|
|
283
722
|
|
|
284
|
-
```ruby
|
|
723
|
+
``` ruby
|
|
285
724
|
puts vec.c(10, 20, 30)
|
|
286
725
|
```
|
|
287
726
|
|
|
288
727
|
```
|
|
289
728
|
## [1] 1 2 3 4 10 20 30
|
|
290
729
|
```
|
|
291
|
-
We will talk about vector indexing in a
|
|
730
|
+
We will talk about vector indexing in a later section. But notice here that indexing
|
|
292
731
|
an R::Vector will return another R::Vector:
|
|
293
732
|
|
|
294
733
|
|
|
295
|
-
```ruby
|
|
734
|
+
``` ruby
|
|
296
735
|
puts vec[1]
|
|
297
736
|
```
|
|
298
737
|
|
|
@@ -304,7 +743,7 @@ in an R::Object, but the native Ruby object. For this, we can index the R object
|
|
|
304
743
|
the '>>' operator:
|
|
305
744
|
|
|
306
745
|
|
|
307
|
-
```ruby
|
|
746
|
+
``` ruby
|
|
308
747
|
puts vec >> 0
|
|
309
748
|
puts vec >> 2
|
|
310
749
|
```
|
|
@@ -319,7 +758,7 @@ in Galaaz with named parameters. For instance, here is an example of creating a
|
|
|
319
758
|
with named elements:
|
|
320
759
|
|
|
321
760
|
|
|
322
|
-
```ruby
|
|
761
|
+
``` ruby
|
|
323
762
|
puts R.list(first_name: "Rodrigo", last_name: "Botafogo")
|
|
324
763
|
```
|
|
325
764
|
|
|
@@ -337,25 +776,22 @@ Method or Lambda in place of the expected R function. In this next example, we w
|
|
|
337
776
|
add 2 to every element of our previously created vector:
|
|
338
777
|
|
|
339
778
|
|
|
340
|
-
```ruby
|
|
779
|
+
``` ruby
|
|
341
780
|
puts vec.map { |x| x + 2 }
|
|
342
781
|
```
|
|
343
782
|
|
|
344
783
|
```
|
|
345
|
-
## [1] 3
|
|
346
|
-
## [1] 4
|
|
347
|
-
## [1] 5
|
|
348
|
-
## [1] 6
|
|
784
|
+
## [1] 3 4 5 6
|
|
349
785
|
```
|
|
350
786
|
|
|
351
787
|
# gKnitting a Document
|
|
352
788
|
|
|
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
|
|
789
|
+
This manual has been formatted using gKnit. gKnit uses knitr and R Markdown to knit
|
|
790
|
+
a document in Ruby or R and output it in any of the available formats for R Markdown.
|
|
791
|
+
gKnit runs with **JRuby**, **GNU R**, and Galaaz. In gKnit, Ruby variables are persisted between
|
|
356
792
|
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
|
-
|
|
793
|
+
on Galaaz, Ruby chunks can have access to R variables and combining Ruby with R in one
|
|
794
|
+
document is natural.
|
|
359
795
|
|
|
360
796
|
The idea of "literate programming" was first introduced by Donald Knuth in the
|
|
361
797
|
1980's [@Knuth:literate_programming].
|
|
@@ -374,7 +810,7 @@ single document or set of documents that when distributed to peers could be reru
|
|
|
374
810
|
the same output and reports.
|
|
375
811
|
|
|
376
812
|
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
|
|
813
|
+
introduced and it allowed mixing R code with LaTeX, generating high-quality PDF documents. A
|
|
378
814
|
Sweave document could include code, the results of executing the code, graphics and text
|
|
379
815
|
such that it contained the whole narrative to reproduce the research. In
|
|
380
816
|
2012, Knitr, developed by Yihui Xie from RStudio was released to replace Sweave and to
|
|
@@ -383,7 +819,7 @@ were necessary for Sweave.
|
|
|
383
819
|
|
|
384
820
|
With Knitr, __R markdown__ was also developed, an extension to the
|
|
385
821
|
Markdown format. With __R markdown__ and Knitr it is possible to generate reports in a multitude
|
|
386
|
-
of formats such as HTML,
|
|
822
|
+
of formats such as HTML, Markdown, LaTeX, PDF, DVI, etc. __R markdown__ also allows the use of
|
|
387
823
|
multiple programming languages such as R, Ruby, Python, etc. in the same document.
|
|
388
824
|
|
|
389
825
|
In __R markdown__, text is interspersed with
|
|
@@ -404,14 +840,14 @@ create a list, then we would need to save it to file, but to save it, we need so
|
|
|
404
840
|
data into a binary format:
|
|
405
841
|
|
|
406
842
|
|
|
407
|
-
```ruby
|
|
843
|
+
``` ruby
|
|
408
844
|
lst = R.list(a: 1, b: 2, c: 3)
|
|
409
845
|
lst.saveRDS("lst.rds")
|
|
410
846
|
```
|
|
411
847
|
then, on the next chunk, where variable 'lst' is used, we need to read back it's value
|
|
412
848
|
|
|
413
849
|
|
|
414
|
-
```ruby
|
|
850
|
+
``` ruby
|
|
415
851
|
lst = R.readRDS("lst.rds")
|
|
416
852
|
puts lst
|
|
417
853
|
```
|
|
@@ -431,16 +867,16 @@ Now, any single code has dozens of variables that we might want to use and reuse
|
|
|
431
867
|
Clearly, such an approach becomes quickly unmanageable. Probably, because of
|
|
432
868
|
this problem, it is very rare to see any __R markdown__ document in the Ruby community.
|
|
433
869
|
|
|
434
|
-
When variables can be used
|
|
870
|
+
When variables can be used across chunks, then no overhead is needed:
|
|
435
871
|
|
|
436
872
|
|
|
437
|
-
```ruby
|
|
873
|
+
``` ruby
|
|
438
874
|
lst = R.list(a: 1, b: 2, c: 3)
|
|
439
875
|
# any other code can be added here
|
|
440
876
|
```
|
|
441
877
|
|
|
442
878
|
|
|
443
|
-
```ruby
|
|
879
|
+
``` ruby
|
|
444
880
|
puts lst
|
|
445
881
|
```
|
|
446
882
|
|
|
@@ -456,8 +892,8 @@ puts lst
|
|
|
456
892
|
```
|
|
457
893
|
|
|
458
894
|
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
|
|
895
|
+
started around the first decade of the 2000s. In 2006 IPython 0.7.2 was released. In 2014,
|
|
896
|
+
Fernando Pérez spun off the Jupyter project from IPython, creating a web-based interactive
|
|
461
897
|
computation environment. Jupyter can now be used with many languages, including Ruby with the
|
|
462
898
|
iruby gem (https://github.com/SciRuby/iruby). In order to have multiple languages in a Jupyter
|
|
463
899
|
notebook the SoS kernel was developed (https://vatlab.github.io/sos-docs/).
|
|
@@ -471,8 +907,8 @@ have in a single document, text and code.
|
|
|
471
907
|
|
|
472
908
|
In gKnit, Ruby variables are persisted between
|
|
473
909
|
chunks, making it an ideal solution for literate programming in this language. Also,
|
|
474
|
-
since it is based on
|
|
475
|
-
|
|
910
|
+
since it is based on Galaaz, Ruby chunks can access R variables (`~R[:name]`, `R.*`) through the
|
|
911
|
+
**Galaaz bridge** while knitr drives **GNU R**—no GraalVM polyglot runtime is required.
|
|
476
912
|
|
|
477
913
|
This is not a blog post on __R markdown__, and the interested user is directed to the following links
|
|
478
914
|
for detailed information on its capabilities and use.
|
|
@@ -486,7 +922,7 @@ gKnitting Ruby and R documents quickly.
|
|
|
486
922
|
## The Yaml header
|
|
487
923
|
|
|
488
924
|
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
|
|
925
|
+
'.Rmd' extension. This document has the following header for gKnitting an HTML document.
|
|
490
926
|
|
|
491
927
|
```
|
|
492
928
|
---
|
|
@@ -494,7 +930,7 @@ title: "How to do reproducible research in Ruby with gKnit"
|
|
|
494
930
|
author:
|
|
495
931
|
- "Rodrigo Botafogo"
|
|
496
932
|
- "Daniel Mossé - University of Pittsburgh"
|
|
497
|
-
tags: [Tech, Data Science, Ruby, R,
|
|
933
|
+
tags: [Tech, Data Science, Ruby, R, JRuby, Galaaz]
|
|
498
934
|
date: "20/02/2019"
|
|
499
935
|
output:
|
|
500
936
|
html_document:
|
|
@@ -509,6 +945,35 @@ output:
|
|
|
509
945
|
|
|
510
946
|
For more information on the options in the Yaml header, [check here](https://bookdown.org/yihui/rmarkdown/html-document.html).
|
|
511
947
|
|
|
948
|
+
## Choosing the output format when calling gknit
|
|
949
|
+
|
|
950
|
+
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`**.
|
|
951
|
+
|
|
952
|
+
* **`--output_format FORMAT`** — name of the format, as in the YAML `output:` block. Examples:
|
|
953
|
+
* **`html_document`** — HTML (often the default you list first under `output:`).
|
|
954
|
+
* **`pdf_document`** — PDF (you need a working LaTeX setup, e.g. TinyTeX; see **`bin/install-tinytex`**).
|
|
955
|
+
* **`md_document`**, **`github_document`**, or any other format defined in your YAML.
|
|
956
|
+
* **`all`** — render **every** format declared under `output:` in the document (same idea as in R Markdown).
|
|
957
|
+
|
|
958
|
+
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).
|
|
959
|
+
|
|
960
|
+
Other useful flags:
|
|
961
|
+
|
|
962
|
+
* **`--output_file NAME`** — output file name (optional path; see also **`--output_dir`**).
|
|
963
|
+
* **`--output_dir DIR`** — directory for the rendered file (created if missing).
|
|
964
|
+
* **`--bridge_timeout_sec`** / **`--callback_timeout_ms`** — longer R or install steps (see elsewhere in this manual).
|
|
965
|
+
|
|
966
|
+
Examples (run from the directory where paths make sense, or use absolute paths):
|
|
967
|
+
|
|
968
|
+
```text
|
|
969
|
+
bin/gknit blogs/manual/manual.Rmd
|
|
970
|
+
bin/gknit --output_format html_document blogs/manual/manual.Rmd
|
|
971
|
+
bin/gknit --output_format pdf_document blogs/manual/manual.Rmd
|
|
972
|
+
bin/gknit --output_format all blogs/manual/manual.Rmd
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
Use **`gknit -h`** for the full option list.
|
|
976
|
+
|
|
512
977
|
## __R Markdown__ formatting
|
|
513
978
|
|
|
514
979
|
Document formatting can be done with simple markups such as:
|
|
@@ -553,7 +1018,7 @@ Running and executing Ruby and R code is actually what really interests us is th
|
|
|
553
1018
|
Inserting a code chunk is done by adding code in a block delimited by three back ticks
|
|
554
1019
|
followed by an open
|
|
555
1020
|
curly brace ('{') followed with the engine name (r, ruby, rb, include, ...), an
|
|
556
|
-
any optional chunk_label and options, as shown
|
|
1021
|
+
any optional chunk_label and options, as shown below:
|
|
557
1022
|
|
|
558
1023
|
````
|
|
559
1024
|
```{engine_name [chunk_label], [chunk_options]}
|
|
@@ -573,7 +1038,7 @@ print(vec)
|
|
|
573
1038
|
If this block is added to an __R markdown__ document and gKnitted the result will be:
|
|
574
1039
|
|
|
575
1040
|
|
|
576
|
-
```r
|
|
1041
|
+
``` r
|
|
577
1042
|
vec <- c(1, 2, 3)
|
|
578
1043
|
print(vec)
|
|
579
1044
|
```
|
|
@@ -621,7 +1086,7 @@ presentation we will not show chunk definitions any longer.
|
|
|
621
1086
|
|
|
622
1087
|
|
|
623
1088
|
|
|
624
|
-
```r
|
|
1089
|
+
``` r
|
|
625
1090
|
r_vec <- c(1, 2, 3, 4, 5)
|
|
626
1091
|
|
|
627
1092
|
reduce_sum <- function(...) {
|
|
@@ -632,7 +1097,7 @@ reduce_sum <- function(...) {
|
|
|
632
1097
|
We can, possibly in another chunk, access the vector and call the function as follows:
|
|
633
1098
|
|
|
634
1099
|
|
|
635
|
-
```r
|
|
1100
|
+
``` r
|
|
636
1101
|
print(r_vec)
|
|
637
1102
|
```
|
|
638
1103
|
|
|
@@ -640,7 +1105,7 @@ print(r_vec)
|
|
|
640
1105
|
## [1] 1 2 3 4 5
|
|
641
1106
|
```
|
|
642
1107
|
|
|
643
|
-
```r
|
|
1108
|
+
``` r
|
|
644
1109
|
print(reduce_sum(r_vec))
|
|
645
1110
|
```
|
|
646
1111
|
|
|
@@ -659,7 +1124,7 @@ grammar of graphics" [@Wilkinson:grammar_of_graphics]. The idea of the grammar o
|
|
|
659
1124
|
is to build a graphics by adding layers to the plot. More information can be found in
|
|
660
1125
|
https://towardsdatascience.com/a-comprehensive-guide-to-the-grammar-of-graphics-for-effective-visualization-of-multi-dimensional-1f92b4ed4149.
|
|
661
1126
|
|
|
662
|
-
In the plot
|
|
1127
|
+
In the plot below the 'mpg' dataset from base R is used. "The data concerns city-cycle fuel
|
|
663
1128
|
consumption in miles per gallon, to be predicted in terms of 3 multivalued discrete and 5
|
|
664
1129
|
continuous attributes." (Quinlan, 1993)
|
|
665
1130
|
|
|
@@ -673,21 +1138,9 @@ car point equal its high way consumption (size = hwy). Finally, a last layer is
|
|
|
673
1138
|
a linear regression line (method = "lm") for every manufacturer.
|
|
674
1139
|
|
|
675
1140
|
|
|
676
|
-
```r
|
|
1141
|
+
``` r
|
|
677
1142
|
# load package and data
|
|
678
1143
|
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
1144
|
data(mpg, package="ggplot2")
|
|
692
1145
|
|
|
693
1146
|
mpg_select <- mpg[mpg$manufacturer %in% c("audi", "ford", "honda", "hyundai"), ]
|
|
@@ -702,6 +1155,10 @@ g + geom_jitter(aes(col=manufacturer, size=hwy)) +
|
|
|
702
1155
|
geom_smooth(aes(col=manufacturer), method="lm", se=F)
|
|
703
1156
|
```
|
|
704
1157
|
|
|
1158
|
+
```
|
|
1159
|
+
## `geom_smooth()` using formula = 'y ~ x'
|
|
1160
|
+
```
|
|
1161
|
+
|
|
705
1162
|
<!-- -->
|
|
706
1163
|
|
|
707
1164
|
## Ruby chunks
|
|
@@ -733,7 +1190,7 @@ functions. gKnit will knit standard Ruby code, or even general text without
|
|
|
733
1190
|
any code.
|
|
734
1191
|
|
|
735
1192
|
|
|
736
|
-
```ruby
|
|
1193
|
+
``` ruby
|
|
737
1194
|
a = [1, 2, 3]
|
|
738
1195
|
b = "US$ 250.000"
|
|
739
1196
|
c = "The 'outputs' function"
|
|
@@ -745,7 +1202,7 @@ vec2 = R.c(10, 20, 30)
|
|
|
745
1202
|
In the next block, variables 'a', 'vec' and 'vec2' are used and printed.
|
|
746
1203
|
|
|
747
1204
|
|
|
748
|
-
```ruby
|
|
1205
|
+
``` ruby
|
|
749
1206
|
puts a
|
|
750
1207
|
puts vec * vec2
|
|
751
1208
|
```
|
|
@@ -869,24 +1326,24 @@ Here, for instance, is a table definition in HTML and its output in the document
|
|
|
869
1326
|
</div>
|
|
870
1327
|
|
|
871
1328
|
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
|
|
1329
|
+
if we intend the document to be rendered in other formats, for example, as LaTeX.
|
|
873
1330
|
Also, The above
|
|
874
1331
|
table looks ugly. The 'kableExtra' library is a great library for
|
|
875
1332
|
creating beautiful tables. Take a look at https://cran.r-project.org/web/packages/kableExtra/vignettes/awesome_table_in_html.html
|
|
876
1333
|
|
|
877
1334
|
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 '
|
|
1335
|
+
table. Note that we retrieve the mtcars dataframe by using '~R[:mtcars]'.
|
|
879
1336
|
|
|
880
1337
|
|
|
881
|
-
```ruby
|
|
1338
|
+
``` ruby
|
|
882
1339
|
R.install_and_loads('kableExtra')
|
|
883
|
-
outputs (
|
|
1340
|
+
outputs (~R[:mtcars]).kable.kable_styling
|
|
884
1341
|
```
|
|
885
1342
|
|
|
886
1343
|
<table class="table" style="margin-left: auto; margin-right: auto;">
|
|
887
1344
|
<thead>
|
|
888
1345
|
<tr>
|
|
889
|
-
<th style="text-align:left;">
|
|
1346
|
+
<th style="text-align:left;"> </th>
|
|
890
1347
|
<th style="text-align:right;"> mpg </th>
|
|
891
1348
|
<th style="text-align:right;"> cyl </th>
|
|
892
1349
|
<th style="text-align:right;"> disp </th>
|
|
@@ -1377,7 +1834,7 @@ true, ruby's 'require\_relative' semantics is used to load the file, when false,
|
|
|
1377
1834
|
```
|
|
1378
1835
|
````
|
|
1379
1836
|
|
|
1380
|
-
|
|
1837
|
+
Below we include file 'model.rb', which is in the same directory of this blog.
|
|
1381
1838
|
This code uses R 'caret' package to split a dataset in a train and test sets.
|
|
1382
1839
|
The 'caret' package is a very important a useful package for doing Data Analysis,
|
|
1383
1840
|
it has hundreds of functions for all steps of the Data Analysis workflow. To
|
|
@@ -1395,7 +1852,7 @@ will install the package if it is not already installed and can take a while.
|
|
|
1395
1852
|
````
|
|
1396
1853
|
|
|
1397
1854
|
|
|
1398
|
-
```include
|
|
1855
|
+
``` include
|
|
1399
1856
|
require 'galaaz'
|
|
1400
1857
|
|
|
1401
1858
|
# Loads the R 'caret' package. If not present, installs it
|
|
@@ -1427,7 +1884,7 @@ class Model
|
|
|
1427
1884
|
def partition(field)
|
|
1428
1885
|
|
|
1429
1886
|
train_index =
|
|
1430
|
-
R.createDataPartition(@data.send(field), p: @
|
|
1887
|
+
R.createDataPartition(@data.send(field), p: @percent_train,
|
|
1431
1888
|
list: false, times: 1)
|
|
1432
1889
|
@train = @data[train_index, :all]
|
|
1433
1890
|
@test = @data[-train_index, :all]
|
|
@@ -1435,12 +1892,11 @@ class Model
|
|
|
1435
1892
|
end
|
|
1436
1893
|
|
|
1437
1894
|
end
|
|
1438
|
-
|
|
1439
1895
|
```
|
|
1440
1896
|
|
|
1441
1897
|
|
|
1442
|
-
```ruby
|
|
1443
|
-
mtcars =
|
|
1898
|
+
``` ruby
|
|
1899
|
+
mtcars = ~R[:mtcars]
|
|
1444
1900
|
model = Model.new(mtcars, percent_train: 0.8)
|
|
1445
1901
|
model.partition(:mpg)
|
|
1446
1902
|
puts model.train.head
|
|
@@ -1448,20 +1904,18 @@ puts model.test.head
|
|
|
1448
1904
|
```
|
|
1449
1905
|
|
|
1450
1906
|
```
|
|
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
1907
|
## mpg cyl disp hp drat wt qsec vs am gear carb
|
|
1908
|
+
## Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
|
|
1459
1909
|
## Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
|
|
1460
1910
|
## Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
|
|
1461
1911
|
## Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
|
|
1462
|
-
##
|
|
1912
|
+
## Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
|
|
1463
1913
|
## Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
|
|
1464
|
-
##
|
|
1914
|
+
## mpg cyl disp hp drat wt qsec vs am gear carb
|
|
1915
|
+
## Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
|
|
1916
|
+
## Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
|
|
1917
|
+
## Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
|
|
1918
|
+
## Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4
|
|
1465
1919
|
```
|
|
1466
1920
|
|
|
1467
1921
|
## Documenting Gems
|
|
@@ -1469,9 +1923,9 @@ puts model.test.head
|
|
|
1469
1923
|
gKnit also allows developers to document and load files that are not in the same directory
|
|
1470
1924
|
of the '.Rmd' file.
|
|
1471
1925
|
|
|
1472
|
-
Here is an example of loading
|
|
1473
|
-
is set to FALSE, so Ruby will look for the file in its
|
|
1474
|
-
need to
|
|
1926
|
+
Here is an example of loading Ruby’s standard library file `find.rb`. In this example, relative
|
|
1927
|
+
is set to FALSE, so Ruby will look for the file in its `$LOAD_PATH`, and the user does not
|
|
1928
|
+
need to know its directory on disk.
|
|
1475
1929
|
|
|
1476
1930
|
````
|
|
1477
1931
|
```{include find, relative = FALSE}
|
|
@@ -1479,7 +1933,7 @@ need to no it's directory.
|
|
|
1479
1933
|
````
|
|
1480
1934
|
|
|
1481
1935
|
|
|
1482
|
-
```include
|
|
1936
|
+
``` include
|
|
1483
1937
|
# frozen_string_literal: true
|
|
1484
1938
|
#
|
|
1485
1939
|
# find.rb: the Find module for processing all files under a given directory.
|
|
@@ -1497,7 +1951,7 @@ need to no it's directory.
|
|
|
1497
1951
|
#
|
|
1498
1952
|
# Find.find(ENV["HOME"]) do |path|
|
|
1499
1953
|
# if FileTest.directory?(path)
|
|
1500
|
-
# if File.basename(path)
|
|
1954
|
+
# if File.basename(path).start_with?('.')
|
|
1501
1955
|
# Find.prune # Don't look any further into this directory.
|
|
1502
1956
|
# else
|
|
1503
1957
|
# next
|
|
@@ -1509,6 +1963,8 @@ need to no it's directory.
|
|
|
1509
1963
|
#
|
|
1510
1964
|
module Find
|
|
1511
1965
|
|
|
1966
|
+
VERSION = "0.2.0"
|
|
1967
|
+
|
|
1512
1968
|
#
|
|
1513
1969
|
# Calls the associated block with the name of every file and directory listed
|
|
1514
1970
|
# as arguments, then recursively on their subdirectories, and so on.
|
|
@@ -1528,24 +1984,24 @@ module Find
|
|
|
1528
1984
|
ps = [path]
|
|
1529
1985
|
while file = ps.shift
|
|
1530
1986
|
catch(:prune) do
|
|
1531
|
-
yield file.dup
|
|
1987
|
+
yield file.dup
|
|
1532
1988
|
begin
|
|
1533
1989
|
s = File.lstat(file)
|
|
1534
|
-
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
|
|
1990
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG, Errno::EINVAL
|
|
1535
1991
|
raise unless ignore_error
|
|
1536
1992
|
next
|
|
1537
1993
|
end
|
|
1538
1994
|
if s.directory? then
|
|
1539
1995
|
begin
|
|
1540
1996
|
fs = Dir.children(file, encoding: enc)
|
|
1541
|
-
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
|
|
1997
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG, Errno::EINVAL
|
|
1542
1998
|
raise unless ignore_error
|
|
1543
1999
|
next
|
|
1544
2000
|
end
|
|
1545
2001
|
fs.sort!
|
|
1546
2002
|
fs.reverse_each {|f|
|
|
1547
2003
|
f = File.join(file, f)
|
|
1548
|
-
ps.unshift f
|
|
2004
|
+
ps.unshift f
|
|
1549
2005
|
}
|
|
1550
2006
|
end
|
|
1551
2007
|
end
|
|
@@ -1582,9 +2038,9 @@ the Yaml header to generate this blog in PDF format instead of HTML:
|
|
|
1582
2038
|
|
|
1583
2039
|
```
|
|
1584
2040
|
---
|
|
1585
|
-
title: "gKnit - Ruby and R Knitting with Galaaz
|
|
2041
|
+
title: "gKnit - Ruby and R Knitting with Galaaz"
|
|
1586
2042
|
author: "Rodrigo Botafogo"
|
|
1587
|
-
tags: [Galaaz, Ruby, R,
|
|
2043
|
+
tags: [Galaaz, Ruby, R, JRuby, knitr, gknit]
|
|
1588
2044
|
date: "29 October 2018"
|
|
1589
2045
|
output:
|
|
1590
2046
|
pdf\_document:
|
|
@@ -1596,7 +2052,7 @@ output:
|
|
|
1596
2052
|
|
|
1597
2053
|
## Template based documents generation
|
|
1598
2054
|
|
|
1599
|
-
When a document is converted to PDF it follows a certain
|
|
2055
|
+
When a document is converted to PDF it follows a certain conversion template. We've seen above
|
|
1600
2056
|
the use of 'galaaz.sty' as a basic template to generate a PDF document. Using the
|
|
1601
2057
|
'gknit-draft' app that comes with Galaaz, the same .Rmd file can be compiled to different
|
|
1602
2058
|
looking PDF documents. Galaaz automatically loads the 'rticles' R package that comes with
|
|
@@ -1646,21 +2102,21 @@ gknit-draft --filename my_r_article --template rjournal_article --package rticle
|
|
|
1646
2102
|
|
|
1647
2103
|
# Accessing R variables
|
|
1648
2104
|
|
|
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
|
-
|
|
2105
|
+
Galaaz allows Ruby to access variables created in R. For example, the `mtcars` data set is
|
|
2106
|
+
available in R and can be accessed from Ruby by using the tilde operator followed by the
|
|
2107
|
+
symbol for the variable, in this case `:mtcars`. In the code below, method `outputs` is
|
|
2108
|
+
used to output the `mtcars` data set nicely formatted in HTML by use of the `kable` and
|
|
2109
|
+
`kable_styling` functions. Method `outputs` is only available when used with gKnit.
|
|
1654
2110
|
|
|
1655
2111
|
|
|
1656
|
-
```ruby
|
|
1657
|
-
outputs (
|
|
2112
|
+
``` ruby
|
|
2113
|
+
outputs (~R[:mtcars]).kable.kable_styling
|
|
1658
2114
|
```
|
|
1659
2115
|
|
|
1660
2116
|
<table class="table" style="margin-left: auto; margin-right: auto;">
|
|
1661
2117
|
<thead>
|
|
1662
2118
|
<tr>
|
|
1663
|
-
<th style="text-align:left;">
|
|
2119
|
+
<th style="text-align:left;"> </th>
|
|
1664
2120
|
<th style="text-align:right;"> mpg </th>
|
|
1665
2121
|
<th style="text-align:right;"> cyl </th>
|
|
1666
2122
|
<th style="text-align:right;"> disp </th>
|
|
@@ -2141,7 +2597,7 @@ table.
|
|
|
2141
2597
|
| logical | logical | logical |
|
|
2142
2598
|
| integer | numeric | integer |
|
|
2143
2599
|
| double | numeric | double |
|
|
2144
|
-
| complex | complex |
|
|
2600
|
+
| complex | complex | complex |
|
|
2145
2601
|
| character | character | character |
|
|
2146
2602
|
| raw | raw | raw |
|
|
2147
2603
|
|
|
@@ -2153,7 +2609,7 @@ vector is often referred to as a character string.
|
|
|
2153
2609
|
To create a vector the 'c' (concatenate) method from the 'R' module should be used:
|
|
2154
2610
|
|
|
2155
2611
|
|
|
2156
|
-
```ruby
|
|
2612
|
+
``` ruby
|
|
2157
2613
|
vec = R.c(1, 2, 3)
|
|
2158
2614
|
puts vec
|
|
2159
2615
|
```
|
|
@@ -2173,7 +2629,7 @@ Data frames will later be more carefully described. In R, the method used to cr
|
|
|
2173
2629
|
data frame is 'data.frame', in Galaaz we use 'data\_\_frame'.
|
|
2174
2630
|
|
|
2175
2631
|
|
|
2176
|
-
```ruby
|
|
2632
|
+
``` ruby
|
|
2177
2633
|
df = R.data__frame(typeof: vec.typeof, mode: vec.mode, storage__mode: vec.storage__mode)
|
|
2178
2634
|
puts df
|
|
2179
2635
|
```
|
|
@@ -2189,7 +2645,7 @@ like '1' is converted to float and to have an integer the R developer will use '
|
|
|
2189
2645
|
follows normal Ruby rules and the number 1 is an integer and 1.0 is a float.
|
|
2190
2646
|
|
|
2191
2647
|
|
|
2192
|
-
```ruby
|
|
2648
|
+
``` ruby
|
|
2193
2649
|
vec = R.c(1.0, 2, 3)
|
|
2194
2650
|
puts vec
|
|
2195
2651
|
```
|
|
@@ -2199,7 +2655,7 @@ puts vec
|
|
|
2199
2655
|
```
|
|
2200
2656
|
|
|
2201
2657
|
|
|
2202
|
-
```ruby
|
|
2658
|
+
``` ruby
|
|
2203
2659
|
df = R.data__frame(typeof: vec.typeof, mode: vec.mode, storage__mode: vec.storage__mode)
|
|
2204
2660
|
outputs df.kable.kable_styling
|
|
2205
2661
|
```
|
|
@@ -2227,41 +2683,30 @@ the first with a message explaining what went wrong and the second with the full
|
|
|
2227
2683
|
of the error.
|
|
2228
2684
|
|
|
2229
2685
|
|
|
2230
|
-
```ruby
|
|
2686
|
+
``` ruby
|
|
2231
2687
|
vec = R.c(1, hello, 5)
|
|
2232
2688
|
```
|
|
2233
2689
|
|
|
2234
2690
|
```
|
|
2235
|
-
##
|
|
2236
|
-
## undefined local variable or method `hello' for #<RC:0x3d8 @out_list=nil>:RC
|
|
2691
|
+
## undefined local variable or method 'hello' for an instance of RC
|
|
2237
2692
|
```
|
|
2238
2693
|
|
|
2239
2694
|
```
|
|
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
|
|
2695
|
+
## /home/rbotafogo/desenv_linux/galaaz/lib/util/exec_ruby.rb:170:in 'exec_ruby'
|
|
2696
|
+
## org/jruby/RubyKernel.java:1268:in 'eval'
|
|
2697
|
+
## /home/rbotafogo/desenv_linux/galaaz/lib/util/exec_ruby.rb:169:in 'exec_ruby'
|
|
2698
|
+
## /home/rbotafogo/desenv_linux/galaaz/lib/gknit/knitr_engine.rb:777:in 'block in initialize'
|
|
2699
|
+
## org/jruby/RubyBasicObject.java:2695:in 'instance_eval'
|
|
2700
|
+
## org/jruby/RubyBasicObject.java:2723:in 'instance_eval'
|
|
2701
|
+
## /home/rbotafogo/desenv_linux/galaaz/lib/gknit/knitr_engine.rb:748:in 'block in initialize'
|
|
2702
|
+
## /home/rbotafogo/desenv_linux/galaaz/lib/R_interface/new_bridge_adapter.rb:358:in 'block in register_callback_proc_stub'
|
|
2703
|
+
## /home/rbotafogo/desenv_linux/galaaz/lib/new_bridge/session_client.rb:413:in 'block in handle_call'
|
|
2259
2704
|
```
|
|
2260
2705
|
|
|
2261
2706
|
Here is a vector with logical values
|
|
2262
2707
|
|
|
2263
2708
|
|
|
2264
|
-
```ruby
|
|
2709
|
+
``` ruby
|
|
2265
2710
|
vec = R.c(true, true, false, false, true)
|
|
2266
2711
|
puts vec
|
|
2267
2712
|
```
|
|
@@ -2275,7 +2720,7 @@ puts vec
|
|
|
2275
2720
|
The 'c' functions used to create vectors can also be used to combine two vectors:
|
|
2276
2721
|
|
|
2277
2722
|
|
|
2278
|
-
```ruby
|
|
2723
|
+
``` ruby
|
|
2279
2724
|
vec1 = R.c(10.0, 20.0, 30.0)
|
|
2280
2725
|
vec2 = R.c(4.0, 5.0, 6.0)
|
|
2281
2726
|
vec = R.c(vec1, vec2)
|
|
@@ -2290,10 +2735,10 @@ In this next example, method 'c' is chainned after 'vec1'. This also looks like
|
|
|
2290
2735
|
method of the vector, but in reallity, this is actually closer to the pipe operator. When
|
|
2291
2736
|
Galaaz identifies that 'c' is not a method of 'vec' it actually tries to call 'R.c' with
|
|
2292
2737
|
'vec1' as the first argument concatenated with all the other available arguments. The code
|
|
2293
|
-
|
|
2738
|
+
below is automatically converted to the code above.
|
|
2294
2739
|
|
|
2295
2740
|
|
|
2296
|
-
```ruby
|
|
2741
|
+
``` ruby
|
|
2297
2742
|
vec = vec1.c(vec2)
|
|
2298
2743
|
puts vec
|
|
2299
2744
|
```
|
|
@@ -2307,7 +2752,7 @@ puts vec
|
|
|
2307
2752
|
Arithmetic operations on vectors are performed element by element:
|
|
2308
2753
|
|
|
2309
2754
|
|
|
2310
|
-
```ruby
|
|
2755
|
+
``` ruby
|
|
2311
2756
|
puts vec1 + vec2
|
|
2312
2757
|
```
|
|
2313
2758
|
|
|
@@ -2316,7 +2761,7 @@ puts vec1 + vec2
|
|
|
2316
2761
|
```
|
|
2317
2762
|
|
|
2318
2763
|
|
|
2319
|
-
```ruby
|
|
2764
|
+
``` ruby
|
|
2320
2765
|
puts vec1 * 5
|
|
2321
2766
|
```
|
|
2322
2767
|
|
|
@@ -2327,7 +2772,7 @@ puts vec1 * 5
|
|
|
2327
2772
|
When vectors have different length, a recycling rule is applied to the shorter vector:
|
|
2328
2773
|
|
|
2329
2774
|
|
|
2330
|
-
```ruby
|
|
2775
|
+
``` ruby
|
|
2331
2776
|
vec3 = R.c(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0)
|
|
2332
2777
|
puts vec4 = vec1 + vec3
|
|
2333
2778
|
```
|
|
@@ -2341,7 +2786,7 @@ puts vec4 = vec1 + vec3
|
|
|
2341
2786
|
Vectors can be indexed by using the '[]' operator:
|
|
2342
2787
|
|
|
2343
2788
|
|
|
2344
|
-
```ruby
|
|
2789
|
+
``` ruby
|
|
2345
2790
|
puts vec4[3]
|
|
2346
2791
|
```
|
|
2347
2792
|
|
|
@@ -2349,11 +2794,11 @@ puts vec4[3]
|
|
|
2349
2794
|
## [1] 33
|
|
2350
2795
|
```
|
|
2351
2796
|
|
|
2352
|
-
We can also index a vector with another vector. For example, in the code
|
|
2797
|
+
We can also index a vector with another vector. For example, in the code below, we take elements
|
|
2353
2798
|
1, 3, 5, and 7 from vec3:
|
|
2354
2799
|
|
|
2355
2800
|
|
|
2356
|
-
```ruby
|
|
2801
|
+
``` ruby
|
|
2357
2802
|
puts vec4[R.c(1, 3, 5, 7)]
|
|
2358
2803
|
```
|
|
2359
2804
|
|
|
@@ -2364,7 +2809,7 @@ puts vec4[R.c(1, 3, 5, 7)]
|
|
|
2364
2809
|
Repeating an index and having indices out of order is valid code:
|
|
2365
2810
|
|
|
2366
2811
|
|
|
2367
|
-
```ruby
|
|
2812
|
+
``` ruby
|
|
2368
2813
|
puts vec4[R.c(1, 3, 3, 1)]
|
|
2369
2814
|
```
|
|
2370
2815
|
|
|
@@ -2376,7 +2821,7 @@ It is also possible to index a vector with a negative number or negative vector.
|
|
|
2376
2821
|
the indexed values are not returned:
|
|
2377
2822
|
|
|
2378
2823
|
|
|
2379
|
-
```ruby
|
|
2824
|
+
``` ruby
|
|
2380
2825
|
puts vec4[-3]
|
|
2381
2826
|
puts vec4[-R.c(1, 3, 5, 7)]
|
|
2382
2827
|
```
|
|
@@ -2389,7 +2834,7 @@ puts vec4[-R.c(1, 3, 5, 7)]
|
|
|
2389
2834
|
If an index is out of range, a missing value (NA) will be reported.
|
|
2390
2835
|
|
|
2391
2836
|
|
|
2392
|
-
```ruby
|
|
2837
|
+
``` ruby
|
|
2393
2838
|
puts vec4[30]
|
|
2394
2839
|
```
|
|
2395
2840
|
|
|
@@ -2400,7 +2845,7 @@ puts vec4[30]
|
|
|
2400
2845
|
It is also possible to index a vector by range:
|
|
2401
2846
|
|
|
2402
2847
|
|
|
2403
|
-
```ruby
|
|
2848
|
+
``` ruby
|
|
2404
2849
|
puts vec4[(2..5)]
|
|
2405
2850
|
```
|
|
2406
2851
|
|
|
@@ -2411,7 +2856,7 @@ puts vec4[(2..5)]
|
|
|
2411
2856
|
Elements in a vector can be named using the 'names' attribute of a vector:
|
|
2412
2857
|
|
|
2413
2858
|
|
|
2414
|
-
```ruby
|
|
2859
|
+
``` ruby
|
|
2415
2860
|
full_name = R.c("Rodrigo", "A", "Botafogo")
|
|
2416
2861
|
full_name.names = R.c("First", "Middle", "Last")
|
|
2417
2862
|
puts full_name
|
|
@@ -2425,7 +2870,7 @@ puts full_name
|
|
|
2425
2870
|
Or it can also be named by using the 'c' function with named paramenters:
|
|
2426
2871
|
|
|
2427
2872
|
|
|
2428
|
-
```ruby
|
|
2873
|
+
``` ruby
|
|
2429
2874
|
full_name = R.c(First: "Rodrigo", Middle: "A", Last: "Botafogo")
|
|
2430
2875
|
puts full_name
|
|
2431
2876
|
```
|
|
@@ -2443,7 +2888,7 @@ R::Vector with other ruby classes it might be necessary to extract the actual Ru
|
|
|
2443
2888
|
from the vector. In order to do this extraction the '>>' operator is used.
|
|
2444
2889
|
|
|
2445
2890
|
|
|
2446
|
-
```ruby
|
|
2891
|
+
``` ruby
|
|
2447
2892
|
puts vec4
|
|
2448
2893
|
puts vec4 >> 0
|
|
2449
2894
|
puts vec4 >> 4
|
|
@@ -2463,7 +2908,7 @@ A matrix is a collection of elements organized as a two dimensional table. A ma
|
|
|
2463
2908
|
created by the 'matrix' function:
|
|
2464
2909
|
|
|
2465
2910
|
|
|
2466
|
-
```ruby
|
|
2911
|
+
``` ruby
|
|
2467
2912
|
mat = R.matrix(R.c(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0),
|
|
2468
2913
|
nrow: 3,
|
|
2469
2914
|
ncol: 3)
|
|
@@ -2481,7 +2926,7 @@ Note that matrices data is organized by column first. It is possible to organize
|
|
|
2481
2926
|
memory by row first passing an extra argument to the 'matrix' function:
|
|
2482
2927
|
|
|
2483
2928
|
|
|
2484
|
-
```ruby
|
|
2929
|
+
``` ruby
|
|
2485
2930
|
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
2931
|
nrow: 3,
|
|
2487
2932
|
ncol: 3,
|
|
@@ -2502,7 +2947,7 @@ puts mat_row
|
|
|
2502
2947
|
A matrix can be indexed by [row, column]:
|
|
2503
2948
|
|
|
2504
2949
|
|
|
2505
|
-
```ruby
|
|
2950
|
+
``` ruby
|
|
2506
2951
|
puts mat_row[1, 1]
|
|
2507
2952
|
puts mat_row[2, 3]
|
|
2508
2953
|
```
|
|
@@ -2514,7 +2959,7 @@ puts mat_row[2, 3]
|
|
|
2514
2959
|
It is possible to index an entire row or column with the ':all' keyword
|
|
2515
2960
|
|
|
2516
2961
|
|
|
2517
|
-
```ruby
|
|
2962
|
+
``` ruby
|
|
2518
2963
|
puts mat_row[1, :all]
|
|
2519
2964
|
puts mat_row[:all, 2]
|
|
2520
2965
|
```
|
|
@@ -2528,7 +2973,7 @@ Indexing with a vector is also possible for matrices. In the following example w
|
|
|
2528
2973
|
rows 1 and 3 and columns 2 and 3 building a 2 x 2 matrix.
|
|
2529
2974
|
|
|
2530
2975
|
|
|
2531
|
-
```ruby
|
|
2976
|
+
``` ruby
|
|
2532
2977
|
puts mat_row[R.c(1, 3), R.c(2, 3)]
|
|
2533
2978
|
```
|
|
2534
2979
|
|
|
@@ -2541,7 +2986,7 @@ puts mat_row[R.c(1, 3), R.c(2, 3)]
|
|
|
2541
2986
|
Matrices can be combined with functions 'rbind':
|
|
2542
2987
|
|
|
2543
2988
|
|
|
2544
|
-
```ruby
|
|
2989
|
+
``` ruby
|
|
2545
2990
|
puts mat_row.rbind(mat)
|
|
2546
2991
|
```
|
|
2547
2992
|
|
|
@@ -2558,7 +3003,7 @@ puts mat_row.rbind(mat)
|
|
|
2558
3003
|
and 'cbind':
|
|
2559
3004
|
|
|
2560
3005
|
|
|
2561
|
-
```ruby
|
|
3006
|
+
``` ruby
|
|
2562
3007
|
puts mat_row.cbind(mat)
|
|
2563
3008
|
```
|
|
2564
3009
|
|
|
@@ -2575,7 +3020,7 @@ A list is a data structure that can contain sublists of different types, while v
|
|
|
2575
3020
|
can only hold one type of element.
|
|
2576
3021
|
|
|
2577
3022
|
|
|
2578
|
-
```ruby
|
|
3023
|
+
``` ruby
|
|
2579
3024
|
nums = R.c(1.0, 2.0, 3.0)
|
|
2580
3025
|
strs = R.c("a", "b", "c", "d")
|
|
2581
3026
|
bool = R.c(true, true, false)
|
|
@@ -2604,7 +3049,7 @@ first start with the '[]' operator. The list above has three sublist indexing wi
|
|
|
2604
3049
|
return one of the sublists.
|
|
2605
3050
|
|
|
2606
3051
|
|
|
2607
|
-
```ruby
|
|
3052
|
+
``` ruby
|
|
2608
3053
|
puts lst[1]
|
|
2609
3054
|
```
|
|
2610
3055
|
|
|
@@ -2619,7 +3064,7 @@ the original list
|
|
|
2619
3064
|
|
|
2620
3065
|
|
|
2621
3066
|
|
|
2622
|
-
```ruby
|
|
3067
|
+
``` ruby
|
|
2623
3068
|
puts lst[[1]]
|
|
2624
3069
|
```
|
|
2625
3070
|
|
|
@@ -2630,7 +3075,7 @@ puts lst[[1]]
|
|
|
2630
3075
|
When elements are named, as dones with lst, indexing can be done by name:
|
|
2631
3076
|
|
|
2632
3077
|
|
|
2633
|
-
```ruby
|
|
3078
|
+
``` ruby
|
|
2634
3079
|
puts lst[['bool']][[1]] >> 0
|
|
2635
3080
|
```
|
|
2636
3081
|
|
|
@@ -2647,11 +3092,11 @@ operator) and then the vector was indexed by its first element, extracting the n
|
|
|
2647
3092
|
|
|
2648
3093
|
A data frame is a table like structure in which each column has the same number of
|
|
2649
3094
|
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 '
|
|
3095
|
+
seen a data frame previously when we accessed variable '~R[:mtcars]'. In order to create a
|
|
2651
3096
|
data frame, function 'data__frame' is used:
|
|
2652
3097
|
|
|
2653
3098
|
|
|
2654
|
-
```ruby
|
|
3099
|
+
``` ruby
|
|
2655
3100
|
df = R.data__frame(
|
|
2656
3101
|
year: R.c(2010, 2011, 2012),
|
|
2657
3102
|
income: R.c(1000.0, 1500.0, 2000.0))
|
|
@@ -2672,10 +3117,10 @@ A data frame can be indexed the same way as a matrix, by using '[row, column]',
|
|
|
2672
3117
|
column can either be a numeric or the name of the row or column
|
|
2673
3118
|
|
|
2674
3119
|
|
|
2675
|
-
```ruby
|
|
2676
|
-
puts (
|
|
2677
|
-
puts (
|
|
2678
|
-
puts (
|
|
3120
|
+
``` ruby
|
|
3121
|
+
puts (~R[:mtcars]).head
|
|
3122
|
+
puts (~R[:mtcars])[1, 2]
|
|
3123
|
+
puts (~R[:mtcars])['Datsun 710', 'mpg']
|
|
2679
3124
|
```
|
|
2680
3125
|
|
|
2681
3126
|
```
|
|
@@ -2694,52 +3139,48 @@ Extracting a column from a data frame as a vector can be done by using the doubl
|
|
|
2694
3139
|
operator:
|
|
2695
3140
|
|
|
2696
3141
|
|
|
2697
|
-
```ruby
|
|
2698
|
-
puts (
|
|
3142
|
+
``` ruby
|
|
3143
|
+
puts (~R[:mtcars])[['mpg']]
|
|
2699
3144
|
```
|
|
2700
3145
|
|
|
2701
3146
|
```
|
|
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
|
-
## [
|
|
3147
|
+
## [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
|
|
3148
|
+
## [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
|
|
3149
|
+
## [31] 15.0 21.4
|
|
2705
3150
|
```
|
|
2706
3151
|
|
|
2707
3152
|
A data frame column can also be accessed as if it were an instance variable of the data frame:
|
|
2708
3153
|
|
|
2709
3154
|
|
|
2710
|
-
```ruby
|
|
2711
|
-
puts (
|
|
3155
|
+
``` ruby
|
|
3156
|
+
puts (~R[:mtcars]).mpg
|
|
2712
3157
|
```
|
|
2713
3158
|
|
|
2714
3159
|
```
|
|
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
|
-
## [
|
|
3160
|
+
## [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
|
|
3161
|
+
## [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
|
|
3162
|
+
## [31] 15.0 21.4
|
|
2718
3163
|
```
|
|
2719
3164
|
|
|
2720
3165
|
Slicing a data frame can be done by indexing it with a vector (we use 'head' to reduce the
|
|
2721
3166
|
output):
|
|
2722
3167
|
|
|
2723
3168
|
|
|
2724
|
-
```ruby
|
|
2725
|
-
puts (
|
|
3169
|
+
``` ruby
|
|
3170
|
+
puts (~R[:mtcars])[R.c('mpg', 'hp')].head
|
|
2726
3171
|
```
|
|
2727
3172
|
|
|
2728
3173
|
```
|
|
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
|
|
3174
|
+
## mpg cyl disp hp drat wt qsec vs am gear carb
|
|
3175
|
+
## NA NA NA NA NA NA NA NA NA NA NA NA
|
|
3176
|
+
## NA.1 NA NA NA NA NA NA NA NA NA NA NA
|
|
2736
3177
|
```
|
|
2737
3178
|
|
|
2738
3179
|
A row slice can be obtained by indexing by row and using the ':all' keyword for the column:
|
|
2739
3180
|
|
|
2740
3181
|
|
|
2741
|
-
```ruby
|
|
2742
|
-
puts (
|
|
3182
|
+
``` ruby
|
|
3183
|
+
puts (~R[:mtcars])[R.c('Datsun 710', 'Camaro Z28'), :all]
|
|
2743
3184
|
```
|
|
2744
3185
|
|
|
2745
3186
|
```
|
|
@@ -2750,29 +3191,29 @@ puts (~:mtcars)[R.c('Datsun 710', 'Camaro Z28'), :all]
|
|
|
2750
3191
|
|
|
2751
3192
|
Finally, a data frame can also be indexed with a logical vector. In this next example, the
|
|
2752
3193
|
'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 '(
|
|
3194
|
+
car is automatic. So, by doing '(~R[:mtcars]).am.eq 0' a logical vector is created with
|
|
2754
3195
|
'true' whenever 'am' is 0 and 'false' otherwise.
|
|
2755
3196
|
|
|
2756
3197
|
|
|
2757
|
-
```ruby
|
|
3198
|
+
``` ruby
|
|
2758
3199
|
# obtain a vector with 'true' for cars with automatic transmission
|
|
2759
|
-
automatic = (
|
|
3200
|
+
automatic = (~R[:mtcars]).am.eq 0
|
|
2760
3201
|
puts automatic
|
|
2761
3202
|
```
|
|
2762
3203
|
|
|
2763
3204
|
```
|
|
2764
|
-
## [1] FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
|
|
2765
|
-
## [
|
|
2766
|
-
## [
|
|
3205
|
+
## [1] FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
|
|
3206
|
+
## [13] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE TRUE TRUE TRUE TRUE
|
|
3207
|
+
## [25] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
|
|
2767
3208
|
```
|
|
2768
3209
|
|
|
2769
3210
|
Using this logical vector, the data frame is indexed, returning a new data frame in
|
|
2770
3211
|
which all cars have automatic transmission.
|
|
2771
3212
|
|
|
2772
3213
|
|
|
2773
|
-
```ruby
|
|
3214
|
+
``` ruby
|
|
2774
3215
|
# slice the data frame by using this vector
|
|
2775
|
-
puts (
|
|
3216
|
+
puts (~R[:mtcars])[automatic, :all]
|
|
2776
3217
|
```
|
|
2777
3218
|
|
|
2778
3219
|
```
|
|
@@ -2805,12 +3246,12 @@ Galaaz extends Ruby to work with complex expressions, similar to R's expressions
|
|
|
2805
3246
|
|
|
2806
3247
|
## Expressions from operators
|
|
2807
3248
|
|
|
2808
|
-
The code
|
|
3249
|
+
The code below
|
|
2809
3250
|
creates an expression summing two symbols
|
|
2810
3251
|
|
|
2811
3252
|
|
|
2812
|
-
```ruby
|
|
2813
|
-
exp1 = :a + :b
|
|
3253
|
+
``` ruby
|
|
3254
|
+
exp1 = R[:a] + R[:b]
|
|
2814
3255
|
puts exp1
|
|
2815
3256
|
```
|
|
2816
3257
|
|
|
@@ -2820,20 +3261,20 @@ puts exp1
|
|
|
2820
3261
|
We can build any complex mathematical expression
|
|
2821
3262
|
|
|
2822
3263
|
|
|
2823
|
-
```ruby
|
|
2824
|
-
exp2 = (:a + :b) * 2.0 + :c ** 2 / :z
|
|
3264
|
+
``` ruby
|
|
3265
|
+
exp2 = (R[:a] + R[:b]) * 2.0 + R[:c] ** 2 / R[:z]
|
|
2825
3266
|
puts exp2
|
|
2826
3267
|
```
|
|
2827
3268
|
|
|
2828
3269
|
```
|
|
2829
|
-
##
|
|
3270
|
+
## a + b * 2.0 + c ^ 2L / z
|
|
2830
3271
|
```
|
|
2831
3272
|
|
|
2832
3273
|
It is also possible to use inequality operators in building expressions
|
|
2833
3274
|
|
|
2834
3275
|
|
|
2835
|
-
```ruby
|
|
2836
|
-
exp3 = (:a + :b) >= :z
|
|
3276
|
+
``` ruby
|
|
3277
|
+
exp3 = (R[:a] + R[:b]) >= :z
|
|
2837
3278
|
puts exp3
|
|
2838
3279
|
```
|
|
2839
3280
|
|
|
@@ -2846,8 +3287,8 @@ notation for those operators such as (.gt, .ge, etc.). So the same expression w
|
|
|
2846
3287
|
above can also be written as
|
|
2847
3288
|
|
|
2848
3289
|
|
|
2849
|
-
```ruby
|
|
2850
|
-
exp4 = (:a + :b).ge :z
|
|
3290
|
+
``` ruby
|
|
3291
|
+
exp4 = (R[:a] + R[:b]).ge :z
|
|
2851
3292
|
puts exp4
|
|
2852
3293
|
```
|
|
2853
3294
|
|
|
@@ -2860,8 +3301,8 @@ those are expressions involving '==', and '='. In order to write an expression
|
|
|
2860
3301
|
need to use the method '.eq' and for '=' we need the function '.assign'
|
|
2861
3302
|
|
|
2862
3303
|
|
|
2863
|
-
```ruby
|
|
2864
|
-
exp5 = (:a + :b).eq :z
|
|
3304
|
+
``` ruby
|
|
3305
|
+
exp5 = (R[:a] + R[:b]).eq :z
|
|
2865
3306
|
puts exp5
|
|
2866
3307
|
```
|
|
2867
3308
|
|
|
@@ -2870,8 +3311,8 @@ puts exp5
|
|
|
2870
3311
|
```
|
|
2871
3312
|
|
|
2872
3313
|
|
|
2873
|
-
```ruby
|
|
2874
|
-
exp6 = :y.assign :a + :b
|
|
3314
|
+
``` ruby
|
|
3315
|
+
exp6 = R[:y].assign R[:a] + R[:b]
|
|
2875
3316
|
puts exp6
|
|
2876
3317
|
```
|
|
2877
3318
|
|
|
@@ -2882,12 +3323,12 @@ In general we think that using the functional notation is preferable to using th
|
|
|
2882
3323
|
symbolic notation as otherwise, we end up writing invalid expressions such as
|
|
2883
3324
|
|
|
2884
3325
|
|
|
2885
|
-
```ruby
|
|
2886
|
-
exp_wrong = (:a + :b) == :z
|
|
3326
|
+
``` ruby
|
|
3327
|
+
exp_wrong = (R[:a] + R[:b]) == :z
|
|
2887
3328
|
puts exp_wrong
|
|
2888
3329
|
```
|
|
2889
3330
|
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
|
|
3331
|
+
when using '==' we are comparing expression (R[:a] + R[:b]) to expression :z with '=='. When the
|
|
2891
3332
|
comparison is executed, the system tries to evaluate :a, :b and :z, and those symbols at
|
|
2892
3333
|
this time are not bound to anything and we get a "object 'a' not found" message.
|
|
2893
3334
|
If we only use functional notation, this type of error will not occur.
|
|
@@ -2902,8 +3343,8 @@ When we want the function to be part of the expression, we call the function pre
|
|
|
2902
3343
|
by the letter E, such as 'E.sin(x)'
|
|
2903
3344
|
|
|
2904
3345
|
|
|
2905
|
-
```ruby
|
|
2906
|
-
exp7 = :y.assign E.sin(:x)
|
|
3346
|
+
``` ruby
|
|
3347
|
+
exp7 = R[:y].assign E.sin(R[:x])
|
|
2907
3348
|
puts exp7
|
|
2908
3349
|
```
|
|
2909
3350
|
|
|
@@ -2914,8 +3355,8 @@ puts exp7
|
|
|
2914
3355
|
Expressions can also be written using '.' notation:
|
|
2915
3356
|
|
|
2916
3357
|
|
|
2917
|
-
```ruby
|
|
2918
|
-
exp8 = :y.assign :x.sin
|
|
3358
|
+
``` ruby
|
|
3359
|
+
exp8 = R[:y].assign R[:x].sin
|
|
2919
3360
|
puts exp8
|
|
2920
3361
|
```
|
|
2921
3362
|
|
|
@@ -2926,8 +3367,8 @@ puts exp8
|
|
|
2926
3367
|
When a function has multiple arguments, the first one can be used before the '.':
|
|
2927
3368
|
|
|
2928
3369
|
|
|
2929
|
-
```ruby
|
|
2930
|
-
exp9 = :x.c(:y)
|
|
3370
|
+
``` ruby
|
|
3371
|
+
exp9 = R[:x].c(R[:y])
|
|
2931
3372
|
puts exp9
|
|
2932
3373
|
```
|
|
2933
3374
|
|
|
@@ -2941,19 +3382,19 @@ Expressions can be evaluated by calling function 'eval' with a binding. A bindin
|
|
|
2941
3382
|
with a list:
|
|
2942
3383
|
|
|
2943
3384
|
|
|
2944
|
-
```ruby
|
|
2945
|
-
exp = (:a + :b) * 2.0 + :c ** 2 / :z
|
|
3385
|
+
``` ruby
|
|
3386
|
+
exp = (R[:a] + R[:b]) * 2.0 + R[:c] ** 2 / R[:z]
|
|
2946
3387
|
puts exp.eval(R.list(a: 10, b: 20, c: 30, z: 40))
|
|
2947
3388
|
```
|
|
2948
3389
|
|
|
2949
3390
|
```
|
|
2950
|
-
## [1]
|
|
3391
|
+
## [1] 72.5
|
|
2951
3392
|
```
|
|
2952
3393
|
|
|
2953
3394
|
... with a data frame:
|
|
2954
3395
|
|
|
2955
3396
|
|
|
2956
|
-
```ruby
|
|
3397
|
+
``` ruby
|
|
2957
3398
|
df = R.data__frame(
|
|
2958
3399
|
a: R.c(1, 2, 3),
|
|
2959
3400
|
b: R.c(10, 20, 30),
|
|
@@ -2964,13 +3405,13 @@ puts exp.eval(df)
|
|
|
2964
3405
|
```
|
|
2965
3406
|
|
|
2966
3407
|
```
|
|
2967
|
-
## [1]
|
|
3408
|
+
## [1] 31 62 93
|
|
2968
3409
|
```
|
|
2969
3410
|
|
|
2970
3411
|
# Manipulating Data
|
|
2971
3412
|
|
|
2972
3413
|
One of the major benefits of Galaaz is to bring strong data manipulation to Ruby. The following
|
|
2973
|
-
examples were extracted from
|
|
3414
|
+
examples were extracted from Hadley's "R for Data Science" (https://r4ds.had.co.nz/). This
|
|
2974
3415
|
is a highly recommended book for those not already familiar with the 'tidyverse' style of
|
|
2975
3416
|
programming in R. In the sections to follow, we will limit ourselves to convert the R code to
|
|
2976
3417
|
Galaaz.
|
|
@@ -2982,62 +3423,60 @@ locally, and if not, installs it. This data frame contains all 336,776 flights t
|
|
|
2982
3423
|
departed from New York City in 2013. The data comes from the US Bureau of
|
|
2983
3424
|
Transportation Statistics.
|
|
2984
3425
|
|
|
2985
|
-
Dplyr uses
|
|
2986
|
-
|
|
2987
|
-
|
|
3426
|
+
Dplyr often uses **tibbles** in place of classic data frames. In Galaaz, printing may differ from
|
|
3427
|
+
the R console; if you need a classic tabular printout, convert with **`as__data__frame`** (or use
|
|
3428
|
+
`head` / `str` in R via `R` calls).
|
|
2988
3429
|
|
|
2989
3430
|
|
|
2990
|
-
```ruby
|
|
3431
|
+
``` ruby
|
|
2991
3432
|
R.install_and_loads('nycflights13')
|
|
2992
3433
|
R.library('dplyr')
|
|
2993
3434
|
```
|
|
2994
3435
|
|
|
2995
3436
|
|
|
2996
|
-
```ruby
|
|
2997
|
-
flights =
|
|
3437
|
+
``` ruby
|
|
3438
|
+
flights = ~R[:flights]
|
|
2998
3439
|
puts flights.head
|
|
2999
3440
|
```
|
|
3000
3441
|
|
|
3001
3442
|
```
|
|
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>
|
|
3443
|
+
## # A tibble: 6 × 19
|
|
3444
|
+
## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
|
|
3445
|
+
## <int> <int> <int> <int> <int> <dbl> <int> <int>
|
|
3446
|
+
## 1 2013 1 1 517 515 2 830 819
|
|
3447
|
+
## 2 2013 1 1 533 529 4 850 830
|
|
3448
|
+
## 3 2013 1 1 542 540 2 923 850
|
|
3449
|
+
## 4 2013 1 1 544 545 -1 1004 1022
|
|
3450
|
+
## 5 2013 1 1 554 600 -6 812 837
|
|
3451
|
+
## 6 2013 1 1 554 558 -4 740 728
|
|
3452
|
+
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
|
|
3453
|
+
## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
|
|
3454
|
+
## # hour <dbl>, minute <dbl>, time_hour <dttm>
|
|
3015
3455
|
```
|
|
3016
3456
|
|
|
3017
3457
|
## Filtering rows with Filter
|
|
3018
3458
|
|
|
3019
3459
|
In this example we filter the flights data set by giving to the filter function two expressions:
|
|
3020
|
-
the first :month.eq 1
|
|
3460
|
+
the first R[:month].eq 1
|
|
3021
3461
|
|
|
3022
3462
|
|
|
3023
|
-
```ruby
|
|
3024
|
-
puts flights.filter((:month.eq 1), (:day.eq 1)).head
|
|
3463
|
+
``` ruby
|
|
3464
|
+
puts flights.filter((R[:month].eq 1), (R[:day].eq 1)).head
|
|
3025
3465
|
```
|
|
3026
3466
|
|
|
3027
3467
|
```
|
|
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>
|
|
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>
|
|
3041
3480
|
```
|
|
3042
3481
|
|
|
3043
3482
|
## Logical Operators
|
|
@@ -3045,24 +3484,23 @@ puts flights.filter((:month.eq 1), (:day.eq 1)).head
|
|
|
3045
3484
|
All flights that departed in November of December
|
|
3046
3485
|
|
|
3047
3486
|
|
|
3048
|
-
```ruby
|
|
3049
|
-
puts flights.filter((:month.eq 11) | (:month.eq 12)).head
|
|
3487
|
+
``` ruby
|
|
3488
|
+
puts flights.filter((R[:month].eq 11) | (R[:month].eq 12)).head
|
|
3050
3489
|
```
|
|
3051
3490
|
|
|
3052
3491
|
```
|
|
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>
|
|
3492
|
+
## # A tibble: 6 × 19
|
|
3493
|
+
## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
|
|
3494
|
+
## <int> <int> <int> <int> <int> <dbl> <int> <int>
|
|
3495
|
+
## 1 2013 11 1 5 2359 6 352 345
|
|
3496
|
+
## 2 2013 11 1 35 2250 105 123 2356
|
|
3497
|
+
## 3 2013 11 1 455 500 -5 641 651
|
|
3498
|
+
## 4 2013 11 1 539 545 -6 856 827
|
|
3499
|
+
## 5 2013 11 1 542 545 -3 831 855
|
|
3500
|
+
## 6 2013 11 1 549 600 -11 912 923
|
|
3501
|
+
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
|
|
3502
|
+
## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
|
|
3503
|
+
## # hour <dbl>, minute <dbl>, time_hour <dttm>
|
|
3066
3504
|
```
|
|
3067
3505
|
|
|
3068
3506
|
The same as above, but using the 'in' operator. In R, it is possible to define many operators
|
|
@@ -3071,24 +3509,23 @@ operators from Galaaz the '._' method is used, where the first argument is the o
|
|
|
3071
3509
|
symbol, in this case ':in' and the second argument is the vector:
|
|
3072
3510
|
|
|
3073
3511
|
|
|
3074
|
-
```ruby
|
|
3075
|
-
puts flights.filter(:month._ :in, R.c(11, 12)).head
|
|
3512
|
+
``` ruby
|
|
3513
|
+
puts flights.filter(R[:month]._ :in, R.c(11, 12)).head
|
|
3076
3514
|
```
|
|
3077
3515
|
|
|
3078
3516
|
```
|
|
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>
|
|
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>
|
|
3092
3529
|
```
|
|
3093
3530
|
|
|
3094
3531
|
## Filtering with NA (Not Available)
|
|
@@ -3099,17 +3536,17 @@ the values and the result of some subsetting operations that are more consistent
|
|
|
3099
3536
|
what is obtained from data frame.
|
|
3100
3537
|
|
|
3101
3538
|
|
|
3102
|
-
```ruby
|
|
3539
|
+
``` ruby
|
|
3103
3540
|
df = R.tibble(x: R.c(1, R::NA, 3))
|
|
3104
3541
|
puts df
|
|
3105
3542
|
```
|
|
3106
3543
|
|
|
3107
3544
|
```
|
|
3108
|
-
## # A tibble: 3
|
|
3545
|
+
## # A tibble: 3 × 1
|
|
3109
3546
|
## x
|
|
3110
3547
|
## <int>
|
|
3111
3548
|
## 1 1
|
|
3112
|
-
## 2
|
|
3549
|
+
## 2 NA
|
|
3113
3550
|
## 3 3
|
|
3114
3551
|
```
|
|
3115
3552
|
|
|
@@ -3117,12 +3554,12 @@ Now filtering by :x > 1 shows all lines that satisfy this condition, where the r
|
|
|
3117
3554
|
not.
|
|
3118
3555
|
|
|
3119
3556
|
|
|
3120
|
-
```ruby
|
|
3121
|
-
puts df.filter(:x > 1)
|
|
3557
|
+
``` ruby
|
|
3558
|
+
puts df.filter(R[:x] > 1)
|
|
3122
3559
|
```
|
|
3123
3560
|
|
|
3124
3561
|
```
|
|
3125
|
-
## # A tibble: 1
|
|
3562
|
+
## # A tibble: 1 × 1
|
|
3126
3563
|
## x
|
|
3127
3564
|
## <int>
|
|
3128
3565
|
## 1 3
|
|
@@ -3131,15 +3568,15 @@ puts df.filter(:x > 1)
|
|
|
3131
3568
|
To match an NA use method 'is__na'
|
|
3132
3569
|
|
|
3133
3570
|
|
|
3134
|
-
```ruby
|
|
3135
|
-
puts df.filter((:x.is__na) | (:x > 1))
|
|
3571
|
+
``` ruby
|
|
3572
|
+
puts df.filter((R[:x].is__na) | (R[:x] > 1))
|
|
3136
3573
|
```
|
|
3137
3574
|
|
|
3138
3575
|
```
|
|
3139
|
-
## # A tibble: 2
|
|
3576
|
+
## # A tibble: 2 × 1
|
|
3140
3577
|
## x
|
|
3141
3578
|
## <int>
|
|
3142
|
-
## 1
|
|
3579
|
+
## 1 NA
|
|
3143
3580
|
## 2 3
|
|
3144
3581
|
```
|
|
3145
3582
|
|
|
@@ -3148,47 +3585,45 @@ puts df.filter((:x.is__na) | (:x > 1))
|
|
|
3148
3585
|
Arrange reorders the rows of a data frame by the given arguments.
|
|
3149
3586
|
|
|
3150
3587
|
|
|
3151
|
-
```ruby
|
|
3588
|
+
``` ruby
|
|
3152
3589
|
puts flights.arrange(:year, :month, :day).head
|
|
3153
3590
|
```
|
|
3154
3591
|
|
|
3155
3592
|
```
|
|
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>
|
|
3593
|
+
## # A tibble: 6 × 19
|
|
3594
|
+
## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
|
|
3595
|
+
## <int> <int> <int> <int> <int> <dbl> <int> <int>
|
|
3596
|
+
## 1 2013 1 1 517 515 2 830 819
|
|
3597
|
+
## 2 2013 1 1 533 529 4 850 830
|
|
3598
|
+
## 3 2013 1 1 542 540 2 923 850
|
|
3599
|
+
## 4 2013 1 1 544 545 -1 1004 1022
|
|
3600
|
+
## 5 2013 1 1 554 600 -6 812 837
|
|
3601
|
+
## 6 2013 1 1 554 558 -4 740 728
|
|
3602
|
+
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
|
|
3603
|
+
## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
|
|
3604
|
+
## # hour <dbl>, minute <dbl>, time_hour <dttm>
|
|
3169
3605
|
```
|
|
3170
3606
|
|
|
3171
3607
|
To arrange in descending order, use function 'desc'
|
|
3172
3608
|
|
|
3173
3609
|
|
|
3174
|
-
```ruby
|
|
3175
|
-
puts flights.arrange(:dep_delay.desc).head
|
|
3610
|
+
``` ruby
|
|
3611
|
+
puts flights.arrange(R[:dep_delay].desc).head
|
|
3176
3612
|
```
|
|
3177
3613
|
|
|
3178
3614
|
```
|
|
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>
|
|
3615
|
+
## # A tibble: 6 × 19
|
|
3616
|
+
## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
|
|
3617
|
+
## <int> <int> <int> <int> <int> <dbl> <int> <int>
|
|
3618
|
+
## 1 2013 1 9 641 900 1301 1242 1530
|
|
3619
|
+
## 2 2013 6 15 1432 1935 1137 1607 2120
|
|
3620
|
+
## 3 2013 1 10 1121 1635 1126 1239 1810
|
|
3621
|
+
## 4 2013 9 20 1139 1845 1014 1457 2210
|
|
3622
|
+
## 5 2013 7 22 845 1600 1005 1044 1815
|
|
3623
|
+
## 6 2013 4 10 1100 1900 960 1342 2211
|
|
3624
|
+
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
|
|
3625
|
+
## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
|
|
3626
|
+
## # hour <dbl>, minute <dbl>, time_hour <dttm>
|
|
3192
3627
|
```
|
|
3193
3628
|
|
|
3194
3629
|
## Selecting columns
|
|
@@ -3196,12 +3631,12 @@ puts flights.arrange(:dep_delay.desc).head
|
|
|
3196
3631
|
To select specific columns from a dataset we use function 'select':
|
|
3197
3632
|
|
|
3198
3633
|
|
|
3199
|
-
```ruby
|
|
3634
|
+
``` ruby
|
|
3200
3635
|
puts flights.select(:year, :month, :day).head
|
|
3201
3636
|
```
|
|
3202
3637
|
|
|
3203
3638
|
```
|
|
3204
|
-
## # A tibble: 6
|
|
3639
|
+
## # A tibble: 6 × 3
|
|
3205
3640
|
## year month day
|
|
3206
3641
|
## <int> <int> <int>
|
|
3207
3642
|
## 1 2013 1 1
|
|
@@ -3215,12 +3650,12 @@ puts flights.select(:year, :month, :day).head
|
|
|
3215
3650
|
It is also possible to select column in a given range
|
|
3216
3651
|
|
|
3217
3652
|
|
|
3218
|
-
```ruby
|
|
3219
|
-
puts flights.select(:year.up_to
|
|
3653
|
+
``` ruby
|
|
3654
|
+
puts flights.select(R[:year].up_to(R[:day])).head
|
|
3220
3655
|
```
|
|
3221
3656
|
|
|
3222
3657
|
```
|
|
3223
|
-
## # A tibble: 6
|
|
3658
|
+
## # A tibble: 6 × 3
|
|
3224
3659
|
## year month day
|
|
3225
3660
|
## <int> <int> <int>
|
|
3226
3661
|
## 1 2013 1 1
|
|
@@ -3234,12 +3669,12 @@ puts flights.select(:year.up_to :day).head
|
|
|
3234
3669
|
Select all columns that start with a given name sequence
|
|
3235
3670
|
|
|
3236
3671
|
|
|
3237
|
-
```ruby
|
|
3672
|
+
``` ruby
|
|
3238
3673
|
puts flights.select(E.starts_with('arr')).head
|
|
3239
3674
|
```
|
|
3240
3675
|
|
|
3241
3676
|
```
|
|
3242
|
-
## # A tibble: 6
|
|
3677
|
+
## # A tibble: 6 × 2
|
|
3243
3678
|
## arr_time arr_delay
|
|
3244
3679
|
## <int> <dbl>
|
|
3245
3680
|
## 1 830 11
|
|
@@ -3264,32 +3699,31 @@ Other functions that can be used:
|
|
|
3264
3699
|
A helper function that comes in handy when we just want to rearrange column order is 'Everything':
|
|
3265
3700
|
|
|
3266
3701
|
|
|
3267
|
-
```ruby
|
|
3702
|
+
``` ruby
|
|
3268
3703
|
puts flights.select(:year, :month, :day, E.everything).head
|
|
3269
3704
|
```
|
|
3270
3705
|
|
|
3271
3706
|
```
|
|
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>
|
|
3707
|
+
## # A tibble: 6 × 19
|
|
3708
|
+
## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
|
|
3709
|
+
## <int> <int> <int> <int> <int> <dbl> <int> <int>
|
|
3710
|
+
## 1 2013 1 1 517 515 2 830 819
|
|
3711
|
+
## 2 2013 1 1 533 529 4 850 830
|
|
3712
|
+
## 3 2013 1 1 542 540 2 923 850
|
|
3713
|
+
## 4 2013 1 1 544 545 -1 1004 1022
|
|
3714
|
+
## 5 2013 1 1 554 600 -6 812 837
|
|
3715
|
+
## 6 2013 1 1 554 558 -4 740 728
|
|
3716
|
+
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
|
|
3717
|
+
## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
|
|
3718
|
+
## # hour <dbl>, minute <dbl>, time_hour <dttm>
|
|
3285
3719
|
```
|
|
3286
3720
|
|
|
3287
3721
|
## Add variables to a dataframe with 'mutate'
|
|
3288
3722
|
|
|
3289
3723
|
|
|
3290
|
-
```ruby
|
|
3724
|
+
``` ruby
|
|
3291
3725
|
flights_sm = flights.
|
|
3292
|
-
select((:year.up_to
|
|
3726
|
+
select((R[:year].up_to(R[:day])),
|
|
3293
3727
|
E.ends_with('delay'),
|
|
3294
3728
|
:distance,
|
|
3295
3729
|
:air_time)
|
|
@@ -3298,7 +3732,7 @@ puts flights_sm.head
|
|
|
3298
3732
|
```
|
|
3299
3733
|
|
|
3300
3734
|
```
|
|
3301
|
-
## # A tibble: 6
|
|
3735
|
+
## # A tibble: 6 × 7
|
|
3302
3736
|
## year month day dep_delay arr_delay distance air_time
|
|
3303
3737
|
## <int> <int> <int> <dbl> <dbl> <dbl> <dbl>
|
|
3304
3738
|
## 1 2013 1 1 2 11 1400 227
|
|
@@ -3310,15 +3744,15 @@ puts flights_sm.head
|
|
|
3310
3744
|
```
|
|
3311
3745
|
|
|
3312
3746
|
|
|
3313
|
-
```ruby
|
|
3747
|
+
``` ruby
|
|
3314
3748
|
flights_sm = flights_sm.
|
|
3315
|
-
mutate(gain: :dep_delay - :arr_delay,
|
|
3316
|
-
speed: :distance / :air_time * 60)
|
|
3749
|
+
mutate(gain: R[:dep_delay] - R[:arr_delay],
|
|
3750
|
+
speed: R[:distance] / R[:air_time] * 60)
|
|
3317
3751
|
puts flights_sm.head
|
|
3318
3752
|
```
|
|
3319
3753
|
|
|
3320
3754
|
```
|
|
3321
|
-
## # A tibble: 6
|
|
3755
|
+
## # A tibble: 6 × 9
|
|
3322
3756
|
## year month day dep_delay arr_delay distance air_time gain speed
|
|
3323
3757
|
## <int> <int> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
|
|
3324
3758
|
## 1 2013 1 1 2 11 1400 227 -9 370.
|
|
@@ -3335,30 +3769,30 @@ Function 'summarise' calculates summaries for the data frame. When no 'group_by'
|
|
|
3335
3769
|
a single value is obtained from the data frame:
|
|
3336
3770
|
|
|
3337
3771
|
|
|
3338
|
-
```ruby
|
|
3772
|
+
``` ruby
|
|
3339
3773
|
puts flights.summarise(delay: E.mean(:dep_delay, na__rm: true))
|
|
3340
3774
|
```
|
|
3341
3775
|
|
|
3342
3776
|
```
|
|
3343
|
-
## # A tibble: 1
|
|
3777
|
+
## # A tibble: 1 × 1
|
|
3344
3778
|
## delay
|
|
3345
3779
|
## <dbl>
|
|
3346
3780
|
## 1 12.6
|
|
3347
3781
|
```
|
|
3348
3782
|
|
|
3349
|
-
When a data frame is
|
|
3783
|
+
When a data frame is grouped with 'group_by' summaries apply to the given group:
|
|
3350
3784
|
|
|
3351
3785
|
|
|
3352
|
-
```ruby
|
|
3786
|
+
``` ruby
|
|
3353
3787
|
by_day = flights.group_by(:year, :month, :day)
|
|
3354
|
-
puts by_day.summarise(delay: :dep_delay.mean(na__rm: true)).head
|
|
3788
|
+
puts by_day.summarise(delay: R[:dep_delay].mean(na__rm: true)).head
|
|
3355
3789
|
```
|
|
3356
3790
|
|
|
3357
3791
|
```
|
|
3358
|
-
## # A tibble: 6
|
|
3792
|
+
## # A tibble: 6 × 4
|
|
3359
3793
|
## # Groups: year, month [1]
|
|
3360
3794
|
## year month day delay
|
|
3361
|
-
##
|
|
3795
|
+
## <int> <int> <int> <dbl>
|
|
3362
3796
|
## 1 2013 1 1 11.5
|
|
3363
3797
|
## 2 2013 1 2 13.9
|
|
3364
3798
|
## 3 2013 1 3 11.0
|
|
@@ -3370,20 +3804,20 @@ puts by_day.summarise(delay: :dep_delay.mean(na__rm: true)).head
|
|
|
3370
3804
|
Next we put many operations together by pipping them one after the other:
|
|
3371
3805
|
|
|
3372
3806
|
|
|
3373
|
-
```ruby
|
|
3807
|
+
``` ruby
|
|
3374
3808
|
delays = flights.
|
|
3375
3809
|
group_by(:dest).
|
|
3376
3810
|
summarise(
|
|
3377
3811
|
count: E.n,
|
|
3378
|
-
dist: :distance.mean(na__rm: true),
|
|
3379
|
-
delay: :arr_delay.mean(na__rm: true)).
|
|
3380
|
-
filter(:count > 20, :dest != "NHL")
|
|
3812
|
+
dist: R[:distance].mean(na__rm: true),
|
|
3813
|
+
delay: R[:arr_delay].mean(na__rm: true)).
|
|
3814
|
+
filter(R[:count] > 20, R[:dest] != "NHL")
|
|
3381
3815
|
|
|
3382
3816
|
puts delays.head
|
|
3383
3817
|
```
|
|
3384
3818
|
|
|
3385
3819
|
```
|
|
3386
|
-
## # A tibble: 6
|
|
3820
|
+
## # A tibble: 6 × 4
|
|
3387
3821
|
## dest count dist delay
|
|
3388
3822
|
## <chr> <int> <dbl> <dbl>
|
|
3389
3823
|
## 1 ABQ 254 1826 4.38
|
|
@@ -3396,48 +3830,67 @@ puts delays.head
|
|
|
3396
3830
|
|
|
3397
3831
|
# Using Data Table
|
|
3398
3832
|
|
|
3833
|
+
The next chunk converts the **nycflights13** `flights` tibble already loaded above into a
|
|
3834
|
+
**`data.table`**. That keeps the manual offline and avoids downloading a remote CSV during gknit
|
|
3835
|
+
(network stalls look like bridge hangs when the transfer runs inside a single R eval).
|
|
3399
3836
|
|
|
3400
|
-
|
|
3837
|
+
|
|
3838
|
+
``` ruby
|
|
3401
3839
|
R.library('data.table')
|
|
3402
|
-
R.install_and_loads('curl')
|
|
3403
3840
|
|
|
3404
|
-
|
|
3405
|
-
flights = R.fread(input)
|
|
3406
|
-
puts flights
|
|
3841
|
+
flights = R.as__data__table(~R[:flights])
|
|
3407
3842
|
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
|
-
|
|
3843
|
+
puts R.head(flights, 12)
|
|
3844
|
+
```
|
|
3845
|
+
|
|
3846
|
+
```
|
|
3847
|
+
## [1] 336776 19
|
|
3848
|
+
## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
|
|
3849
|
+
## <int> <int> <int> <int> <int> <num> <int> <int>
|
|
3850
|
+
## 1: 2013 1 1 517 515 2 830 819
|
|
3851
|
+
## 2: 2013 1 1 533 529 4 850 830
|
|
3852
|
+
## 3: 2013 1 1 542 540 2 923 850
|
|
3853
|
+
## 4: 2013 1 1 544 545 -1 1004 1022
|
|
3854
|
+
## 5: 2013 1 1 554 600 -6 812 837
|
|
3855
|
+
## 6: 2013 1 1 554 558 -4 740 728
|
|
3856
|
+
## 7: 2013 1 1 555 600 -5 913 854
|
|
3857
|
+
## 8: 2013 1 1 557 600 -3 709 723
|
|
3858
|
+
## 9: 2013 1 1 557 600 -3 838 846
|
|
3859
|
+
## 10: 2013 1 1 558 600 -2 753 745
|
|
3860
|
+
## 11: 2013 1 1 558 600 -2 849 851
|
|
3861
|
+
## 12: 2013 1 1 558 600 -2 853 856
|
|
3862
|
+
## arr_delay carrier flight tailnum origin dest air_time distance hour
|
|
3863
|
+
## <num> <char> <int> <char> <char> <char> <num> <num> <num>
|
|
3864
|
+
## 1: 11 UA 1545 N14228 EWR IAH 227 1400 5
|
|
3865
|
+
## 2: 20 UA 1714 N24211 LGA IAH 227 1416 5
|
|
3866
|
+
## 3: 33 AA 1141 N619AA JFK MIA 160 1089 5
|
|
3867
|
+
## 4: -18 B6 725 N804JB JFK BQN 183 1576 5
|
|
3868
|
+
## 5: -25 DL 461 N668DN LGA ATL 116 762 6
|
|
3869
|
+
## 6: 12 UA 1696 N39463 EWR ORD 150 719 5
|
|
3870
|
+
## 7: 19 B6 507 N516JB EWR FLL 158 1065 6
|
|
3871
|
+
## 8: -14 EV 5708 N829AS LGA IAD 53 229 6
|
|
3872
|
+
## 9: -8 B6 79 N593JB JFK MCO 140 944 6
|
|
3873
|
+
## 10: 8 AA 301 N3ALAA LGA ORD 138 733 6
|
|
3874
|
+
## 11: -2 B6 49 N793JB JFK PBI 149 1028 6
|
|
3875
|
+
## 12: -3 B6 71 N657JB JFK TPA 158 1005 6
|
|
3876
|
+
## minute time_hour
|
|
3877
|
+
## <num> <POSc>
|
|
3878
|
+
## 1: 15 2013-01-01 05:00:00
|
|
3879
|
+
## 2: 29 2013-01-01 05:00:00
|
|
3880
|
+
## 3: 40 2013-01-01 05:00:00
|
|
3881
|
+
## 4: 45 2013-01-01 05:00:00
|
|
3882
|
+
## 5: 0 2013-01-01 06:00:00
|
|
3883
|
+
## 6: 58 2013-01-01 05:00:00
|
|
3884
|
+
## 7: 0 2013-01-01 06:00:00
|
|
3885
|
+
## 8: 0 2013-01-01 06:00:00
|
|
3886
|
+
## 9: 0 2013-01-01 06:00:00
|
|
3887
|
+
## 10: 0 2013-01-01 06:00:00
|
|
3888
|
+
## 11: 0 2013-01-01 06:00:00
|
|
3889
|
+
## 12: 0 2013-01-01 06:00:00
|
|
3890
|
+
```
|
|
3891
|
+
|
|
3892
|
+
|
|
3893
|
+
``` ruby
|
|
3441
3894
|
data_table = R.data__table(
|
|
3442
3895
|
ID: R.c("b","b","b","a","a","c"),
|
|
3443
3896
|
a: (1..6),
|
|
@@ -3450,20 +3903,21 @@ puts data_table.ID
|
|
|
3450
3903
|
```
|
|
3451
3904
|
|
|
3452
3905
|
```
|
|
3453
|
-
##
|
|
3454
|
-
##
|
|
3455
|
-
##
|
|
3456
|
-
##
|
|
3457
|
-
##
|
|
3458
|
-
##
|
|
3459
|
-
##
|
|
3906
|
+
## ID a b c
|
|
3907
|
+
## <char> <int> <int> <int>
|
|
3908
|
+
## 1: b 1 7 13
|
|
3909
|
+
## 2: b 2 8 14
|
|
3910
|
+
## 3: b 3 9 15
|
|
3911
|
+
## 4: a 4 10 16
|
|
3912
|
+
## 5: a 5 11 17
|
|
3913
|
+
## 6: c 6 12 18
|
|
3460
3914
|
## [1] "b" "b" "b" "a" "a" "c"
|
|
3461
3915
|
```
|
|
3462
3916
|
|
|
3463
3917
|
|
|
3464
|
-
```ruby
|
|
3918
|
+
``` ruby
|
|
3465
3919
|
# subset rows in i
|
|
3466
|
-
ans = flights[(:origin.eq "JFK") & (:month.eq 6)]
|
|
3920
|
+
ans = flights[(R[:origin].eq "JFK") & (R[:month].eq 6)]
|
|
3467
3921
|
puts ans.head
|
|
3468
3922
|
|
|
3469
3923
|
# Get the first two rows from flights.
|
|
@@ -3471,98 +3925,350 @@ puts ans.head
|
|
|
3471
3925
|
ans = flights[(1..2)]
|
|
3472
3926
|
puts ans
|
|
3473
3927
|
|
|
3474
|
-
# Sort
|
|
3475
|
-
|
|
3928
|
+
# Sort by origin asc, then dest desc (example kept commented):
|
|
3476
3929
|
# ans = flights[E.order(:origin, -(:dest))]
|
|
3477
3930
|
# puts ans.head
|
|
3478
3931
|
```
|
|
3479
3932
|
|
|
3480
3933
|
```
|
|
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
|
-
|
|
3934
|
+
## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
|
|
3935
|
+
## <int> <int> <int> <int> <int> <num> <int> <int>
|
|
3936
|
+
## 1: 2013 6 1 2 2359 3 341 350
|
|
3937
|
+
## 2: 2013 6 1 538 545 -7 925 922
|
|
3938
|
+
## 3: 2013 6 1 539 540 -1 832 840
|
|
3939
|
+
## 4: 2013 6 1 553 600 -7 700 711
|
|
3940
|
+
## 5: 2013 6 1 554 600 -6 851 908
|
|
3941
|
+
## 6: 2013 6 1 557 600 -3 934 942
|
|
3942
|
+
## arr_delay carrier flight tailnum origin dest air_time distance hour
|
|
3943
|
+
## <num> <char> <int> <char> <char> <char> <num> <num> <num>
|
|
3944
|
+
## 1: -9 B6 739 N618JB JFK PSE 200 1617 23
|
|
3945
|
+
## 2: 3 B6 725 N806JB JFK BQN 203 1576 5
|
|
3946
|
+
## 3: -8 AA 701 N5EAAA JFK MIA 140 1089 5
|
|
3947
|
+
## 4: -11 EV 5716 N835AS JFK IAD 42 228 6
|
|
3948
|
+
## 5: -17 UA 1159 N33132 JFK LAX 330 2475 6
|
|
3949
|
+
## 6: -8 B6 715 N766JB JFK SJU 198 1598 6
|
|
3950
|
+
## minute time_hour
|
|
3951
|
+
## <num> <POSc>
|
|
3952
|
+
## 1: 59 2013-06-01 23:00:00
|
|
3953
|
+
## 2: 45 2013-06-01 05:00:00
|
|
3954
|
+
## 3: 40 2013-06-01 05:00:00
|
|
3955
|
+
## 4: 0 2013-06-01 06:00:00
|
|
3956
|
+
## 5: 0 2013-06-01 06:00:00
|
|
3957
|
+
## 6: 0 2013-06-01 06:00:00
|
|
3958
|
+
## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
|
|
3959
|
+
## <int> <int> <int> <int> <int> <num> <int> <int>
|
|
3960
|
+
## 1: 2013 1 1 517 515 2 830 819
|
|
3961
|
+
## 2: 2013 1 1 533 529 4 850 830
|
|
3962
|
+
## arr_delay carrier flight tailnum origin dest air_time distance hour
|
|
3963
|
+
## <num> <char> <int> <char> <char> <char> <num> <num> <num>
|
|
3964
|
+
## 1: 11 UA 1545 N14228 EWR IAH 227 1400 5
|
|
3965
|
+
## 2: 20 UA 1714 N24211 LGA IAH 227 1416 5
|
|
3966
|
+
## minute time_hour
|
|
3967
|
+
## <num> <POSc>
|
|
3968
|
+
## 1: 15 2013-01-01 05:00:00
|
|
3969
|
+
## 2: 29 2013-01-01 05:00:00
|
|
3970
|
+
```
|
|
3971
|
+
|
|
3972
|
+
|
|
3973
|
+
``` ruby
|
|
3505
3974
|
# Select column(s) in j
|
|
3506
3975
|
# select arr_delay column, but return it as a vector.
|
|
3507
3976
|
|
|
3508
3977
|
ans = flights[:all, :arr_delay]
|
|
3509
3978
|
puts ans.head
|
|
3510
3979
|
|
|
3511
|
-
#
|
|
3980
|
+
# arr_delay as data.table (not plain vector).
|
|
3512
3981
|
|
|
3513
|
-
ans = flights[:all, :arr_delay.list]
|
|
3982
|
+
ans = flights[:all, R[:arr_delay].list]
|
|
3514
3983
|
puts ans.head
|
|
3515
3984
|
|
|
3516
|
-
ans = flights[:all, E.list(:arr_delay, :dep_delay)]
|
|
3985
|
+
ans = flights[:all, E.list(R[:arr_delay], R[:dep_delay])]
|
|
3517
3986
|
```
|
|
3518
3987
|
|
|
3519
3988
|
```
|
|
3520
|
-
## [1]
|
|
3989
|
+
## [1] 11 20 33 -18 -25 12
|
|
3521
3990
|
## arr_delay
|
|
3522
|
-
##
|
|
3523
|
-
##
|
|
3524
|
-
##
|
|
3525
|
-
##
|
|
3526
|
-
##
|
|
3527
|
-
##
|
|
3991
|
+
## <num>
|
|
3992
|
+
## 1: 11
|
|
3993
|
+
## 2: 20
|
|
3994
|
+
## 3: 33
|
|
3995
|
+
## 4: -18
|
|
3996
|
+
## 5: -25
|
|
3997
|
+
## 6: 12
|
|
3998
|
+
```
|
|
3999
|
+
|
|
4000
|
+
# Apache Arrow
|
|
4001
|
+
|
|
4002
|
+
[Apache Arrow](https://arrow.apache.org/) is a **columnar** in-memory format used heavily in R
|
|
4003
|
+
and Python for analytics. In Galaaz, **Ruby does not hold an Arrow C++ table itself**; instead you
|
|
4004
|
+
build ordinary Ruby structures (arrays of row hashes), and **`R::Arrow.from_ruby_batches`** creates
|
|
4005
|
+
a real **Arrow `Table` inside GNU R**. From there you use R’s **`arrow`** and **`dplyr`** packages
|
|
4006
|
+
as usual: **`group_by`** on the Arrow table, **`summarise`** for aggregates, then **`collect()`** to
|
|
4007
|
+
materialize a tibble when you need in-memory R rows.
|
|
4008
|
+
|
|
4009
|
+
That pattern matches production use: **JRuby threads** (or sequential code) assemble many rows in
|
|
4010
|
+
Ruby; you pay **one** bridge-heavy handoff to R; **dplyr** runs vectorised work on the Arrow table
|
|
4011
|
+
in R.
|
|
4012
|
+
|
|
4013
|
+
**Prerequisites:** install R packages **`arrow`** and **`dplyr`**. Run scripts with
|
|
4014
|
+
**`bin/galaaz-jruby`** (or the same JVM flags as in **`docs/testing.md`**) so the Arrow JNI stack is
|
|
4015
|
+
available.
|
|
4016
|
+
|
|
4017
|
+
## Other `R::Arrow` helpers
|
|
4018
|
+
|
|
4019
|
+
The Ruby module **`R::Arrow`** (see `lib/R_interface/r_arrow.rb`) also includes:
|
|
4020
|
+
|
|
4021
|
+
* **`R::Arrow.table_from(df)`** — wrap an R `data.frame` / tibble as an Arrow table.
|
|
4022
|
+
* **`R::Arrow.read_feather` / `write_feather`**, **`read_parquet`**, **`dataset(path)`** — file and
|
|
4023
|
+
dataset IO on paths visible to R.
|
|
4024
|
+
|
|
4025
|
+
## Example: many Ruby rows → Arrow in R → grouped statistics
|
|
4026
|
+
|
|
4027
|
+
The repository test **`slow-specs/arrow_large_pipeline_spec.rb`** builds **200k rows** in parallel
|
|
4028
|
+
(eight threads × 25,000 rows), pushes them through **`R::Arrow.from_ruby_batches`**, then checks that
|
|
4029
|
+
**dplyr** group summaries match a Ruby reference calculation. The same logic appears below at a
|
|
4030
|
+
**smaller scale** so this manual can knit quickly; increase `thread_count` and `rows_per_thread`
|
|
4031
|
+
when experimenting locally.
|
|
4032
|
+
|
|
4033
|
+
|
|
4034
|
+
``` ruby
|
|
4035
|
+
# Scaled-down version of slow-specs/arrow_large_pipeline_spec.rb.
|
|
4036
|
+
unless R::Support.eval("requireNamespace('arrow', quietly=TRUE) && requireNamespace('dplyr', quietly=TRUE)") == true
|
|
4037
|
+
puts '(Skip: need arrow + dplyr in R; use bin/galaaz-jruby outside gKnit.)'
|
|
4038
|
+
else
|
|
4039
|
+
thread_count = 4
|
|
4040
|
+
rows_per_thread = 500
|
|
4041
|
+
group_count = 5
|
|
4042
|
+
|
|
4043
|
+
batches = []
|
|
4044
|
+
mutex = Mutex.new
|
|
4045
|
+
threads = []
|
|
4046
|
+
|
|
4047
|
+
thread_count.times do |tid|
|
|
4048
|
+
threads << Thread.new do
|
|
4049
|
+
start = tid * rows_per_thread
|
|
4050
|
+
local = (start...(start + rows_per_thread)).map do |i|
|
|
4051
|
+
{
|
|
4052
|
+
id: i,
|
|
4053
|
+
grp: "g#{i % group_count}",
|
|
4054
|
+
value: (i % 17) + 1,
|
|
4055
|
+
weight: ((i % 5) + 1) * 0.5
|
|
4056
|
+
}
|
|
4057
|
+
end
|
|
4058
|
+
mutex.synchronize { batches << local }
|
|
4059
|
+
end
|
|
4060
|
+
end
|
|
4061
|
+
threads.each(&:join)
|
|
4062
|
+
|
|
4063
|
+
tbl = R::Arrow.from_ruby_batches(batches)
|
|
4064
|
+
puts "R class after from_ruby_batches: #{tbl.rclass}"
|
|
4065
|
+
|
|
4066
|
+
grouped = R.dplyr___group_by(tbl, :grp)
|
|
4067
|
+
summarised = R.dplyr___summarise(
|
|
4068
|
+
grouped,
|
|
4069
|
+
n: E.n(),
|
|
4070
|
+
total: E.sum(:value),
|
|
4071
|
+
wsum: E.sum(R[:value] * R[:weight])
|
|
4072
|
+
)
|
|
4073
|
+
out = R.dplyr___collect(summarised)
|
|
4074
|
+
|
|
4075
|
+
puts 'Per-group summary (first rows):'
|
|
4076
|
+
puts R.as__data__frame(out).head(10)
|
|
4077
|
+
|
|
4078
|
+
total_n = 0
|
|
4079
|
+
(1..(out.nrow >> 0)).each { |i| total_n += (out[['n']][i] >> 0) }
|
|
4080
|
+
puts "Sum of group counts n (should equal #{thread_count * rows_per_thread}): #{total_n}"
|
|
4081
|
+
end
|
|
4082
|
+
```
|
|
4083
|
+
|
|
4084
|
+
```
|
|
4085
|
+
## R class after from_ruby_batches: Table
|
|
4086
|
+
## Per-group summary (first rows):
|
|
4087
|
+
## grp n total wsum
|
|
4088
|
+
## 1 g0 400 3589 1794.5
|
|
4089
|
+
## 2 g1 400 3598 3598.0
|
|
4090
|
+
## 3 g2 400 3590 5385.0
|
|
4091
|
+
## 4 g3 400 3599 7198.0
|
|
4092
|
+
## 5 g4 400 3591 8977.5
|
|
4093
|
+
## Sum of group counts n (should equal 2000): 2000
|
|
4094
|
+
```
|
|
4095
|
+
|
|
4096
|
+
**What to notice:** (1) Ruby only sees **`Hash`** rows and Ruby **`Thread`** objects; (2) a single
|
|
4097
|
+
**`from_ruby_batches`** call creates the Arrow table in R; (3) **`dplyr___group_by`** /
|
|
4098
|
+
**`dplyr___summarise`** / **`dplyr___collect`** mirror **`dplyr::group_by`** /
|
|
4099
|
+
**`dplyr::summarise`** / **`dplyr::collect`** on an Arrow-backed table. For a lighter test, see
|
|
4100
|
+
**`specs/arrow_from_ruby_batches_spec.rb`**; for the full-size benchmark, run
|
|
4101
|
+
**`bin/run_slow_rspec slow-specs/arrow_large_pipeline_spec.rb`**.
|
|
4102
|
+
|
|
4103
|
+
# Bioconductor and DESeq2
|
|
4104
|
+
|
|
4105
|
+
**Bioconductor** packages are ordinary R packages installed from the Bioconductor repositories.
|
|
4106
|
+
Galaaz does not treat them specially: once installed in **GNU R**, you load them with
|
|
4107
|
+
**`R.library`** like any CRAN package.
|
|
4108
|
+
|
|
4109
|
+
## Installing Bioconductor packages
|
|
4110
|
+
|
|
4111
|
+
From an R session (or `R -e '...'`), use **BiocManager** (see
|
|
4112
|
+
[bioconductor.org](https://bioconductor.org/install/)):
|
|
4113
|
+
|
|
4114
|
+
```r
|
|
4115
|
+
if (!requireNamespace("BiocManager", quietly = TRUE))
|
|
4116
|
+
install.packages("BiocManager")
|
|
4117
|
+
BiocManager::install(c("DESeq2", "airway"))
|
|
4118
|
+
```
|
|
4119
|
+
|
|
4120
|
+
The **`airway`** package ships the example **`SummarizedExperiment`** used below. **DESeq2**
|
|
4121
|
+
pulls in several dependencies; the first install can take several minutes.
|
|
4122
|
+
|
|
4123
|
+
## Example: DESeq2 on the airway dataset
|
|
4124
|
+
|
|
4125
|
+
The script **`examples/bioconductor_deseq2_airway/deseq2_airway_galaaz.rb`** is the canonical
|
|
4126
|
+
version in the repository. Run it from the **Galaaz repository root** with JRuby, for example:
|
|
4127
|
+
|
|
4128
|
+
```text
|
|
4129
|
+
bin/galaaz-jruby examples/bioconductor_deseq2_airway/deseq2_airway_galaaz.rb
|
|
4130
|
+
```
|
|
4131
|
+
|
|
4132
|
+
The workflow in Ruby mirrors a standard DESeq2 vignette:
|
|
4133
|
+
|
|
4134
|
+
1. **`R.library('DESeq2')`** and **`R.library('airway')`**, then **`R.data('airway')`** so the
|
|
4135
|
+
object exists in R’s global environment.
|
|
4136
|
+
2. **`airway = ~R[:airway]`** pulls the experiment into a Galaaz wrapper so you can pass it to R
|
|
4137
|
+
functions as a Ruby value.
|
|
4138
|
+
3. **`R.DESeqDataSet(..., design: (R[:all].til R[:cell] + R[:dex]))`** builds the **`DESeqDataSet`**. The
|
|
4139
|
+
**`(R[:all].til R[:cell] + R[:dex])`** form is Galaaz’s way of passing the one-sided formula
|
|
4140
|
+
**`~ cell + dex`** (adjust for the design you need).
|
|
4141
|
+
4. Prefilter rows with almost no counts: **`keep = R.rowSums(R.counts(dds)) >= 10`** and
|
|
4142
|
+
**`dds = dds[keep, :all]`**.
|
|
4143
|
+
5. **`dds = R.DESeq(dds)`** fits the model; **`res = R.results(dds, contrast: R.c('dex', 'trt', 'untrt'))`**
|
|
4144
|
+
extracts the treatment contrast (adjust **`contrast`** for your experiment).
|
|
4145
|
+
6. Summaries use normal Ruby string interpolation on **`R.nrow`**, **`R.ncol`**, **`R.colnames`**, etc.
|
|
4146
|
+
7. **`R.pdf(...); R.plotMA(res, ...); R.dev__off`** writes DESeq2’s MA plot (path is relative to the
|
|
4147
|
+
process working directory—use the repo root when running the bundled script).
|
|
4148
|
+
|
|
4149
|
+
Related benchmarks and warm-run notes live under **`docs/deseq2_airway_benchmark.md`** and
|
|
4150
|
+
**`examples/bioconductor_deseq2_airway/bench_*.rb`**.
|
|
4151
|
+
|
|
4152
|
+
Below is the full listing (same as the file in the repository). It is **not** executed while this
|
|
4153
|
+
manual is knitted, because **DESeq2** is heavy and may be absent on the build machine.
|
|
4154
|
+
|
|
4155
|
+
|
|
4156
|
+
``` ruby
|
|
4157
|
+
# Canonical script: examples/bioconductor_deseq2_airway/deseq2_airway_galaaz.rb
|
|
4158
|
+
# Run: bin/galaaz-jruby examples/.../deseq2_airway_galaaz.rb (repo root).
|
|
4159
|
+
|
|
4160
|
+
require 'galaaz'
|
|
4161
|
+
|
|
4162
|
+
R.library('DESeq2')
|
|
4163
|
+
R.library('airway')
|
|
4164
|
+
R.data('airway')
|
|
4165
|
+
|
|
4166
|
+
airway = ~R[:airway]
|
|
4167
|
+
|
|
4168
|
+
# Build DESeq2 dataset with one-sided formula: ~ cell + dex.
|
|
4169
|
+
dds = R.DESeqDataSet(airway, design: (R[:all].til R[:cell] + R[:dex]))
|
|
4170
|
+
|
|
4171
|
+
# Prefilter genes with almost no counts.
|
|
4172
|
+
keep = R.rowSums(R.counts(dds)) >= 10
|
|
4173
|
+
dds = dds[keep, :all]
|
|
4174
|
+
|
|
4175
|
+
# Fit DE model and extract treatment effect.
|
|
4176
|
+
dds = R.DESeq(dds)
|
|
4177
|
+
res = R.results(dds, contrast: R.c('dex', 'trt', 'untrt'))
|
|
4178
|
+
|
|
4179
|
+
# Compact sanity outputs for quick verification.
|
|
4180
|
+
puts "Samples: #{R.ncol(dds)}"
|
|
4181
|
+
puts "Genes after prefilter: #{R.nrow(dds)}"
|
|
4182
|
+
puts "Result rows: #{R.nrow(res)}"
|
|
4183
|
+
puts "Result columns: #{R.colnames(res)}"
|
|
4184
|
+
puts "Significant genes (padj < 0.05): #{R.sum(res.padj < 0.05, na__rm: true)}"
|
|
4185
|
+
|
|
4186
|
+
res_ordered = res[R.order(res.padj), :all]
|
|
4187
|
+
puts R.head(R.as__data__frame(res_ordered), 10)
|
|
4188
|
+
|
|
4189
|
+
# Standard DESeq2 plot call written to file.
|
|
4190
|
+
R.pdf('examples/bioconductor_deseq2_airway/plotMA_galaaz.pdf')
|
|
4191
|
+
R.plotMA(res, ylim: R.c(-5, 5))
|
|
4192
|
+
R.dev__off
|
|
4193
|
+
```
|
|
4194
|
+
|
|
4195
|
+
If **DESeq2** and **airway** are installed, the next chunk loads the data and prints a short
|
|
4196
|
+
preview (it does **not** run **`DESeq`** so the manual knits quickly).
|
|
4197
|
+
|
|
4198
|
+
|
|
4199
|
+
``` ruby
|
|
4200
|
+
unless R::Support.eval("requireNamespace('DESeq2', quietly=TRUE) && requireNamespace('airway', quietly=TRUE)")
|
|
4201
|
+
puts '(Skip: install DESeq2 and airway via BiocManager in R to run the full example.)'
|
|
4202
|
+
else
|
|
4203
|
+
R.library('DESeq2')
|
|
4204
|
+
R.library('airway')
|
|
4205
|
+
R.data('airway')
|
|
4206
|
+
airway = ~R[:airway]
|
|
4207
|
+
puts 'airway object (head of assay / dims via R):'
|
|
4208
|
+
puts "ncol(samples): #{R.ncol(airway)}"
|
|
4209
|
+
puts R.head(R.assay(airway), 3)
|
|
4210
|
+
end
|
|
4211
|
+
```
|
|
4212
|
+
|
|
4213
|
+
```
|
|
4214
|
+
## airway object (head of assay / dims via R):
|
|
4215
|
+
## ncol(samples): [1] 8
|
|
4216
|
+
## SRR1039508 SRR1039509 SRR1039512 SRR1039513 SRR1039516
|
|
4217
|
+
## ENSG00000000003 679 448 873 408 1138
|
|
4218
|
+
## ENSG00000000005 0 0 0 0 0
|
|
4219
|
+
## ENSG00000000419 467 515 621 365 587
|
|
4220
|
+
## SRR1039517 SRR1039520 SRR1039521
|
|
4221
|
+
## ENSG00000000003 1047 770 572
|
|
4222
|
+
## ENSG00000000005 0 0 0
|
|
4223
|
+
## ENSG00000000419 799 417 508
|
|
3528
4224
|
```
|
|
3529
4225
|
|
|
4226
|
+
# Performance
|
|
4227
|
+
|
|
4228
|
+
For realistic analyses, **most wall-clock time is spent inside GNU R** (model fitting, I/O inside
|
|
4229
|
+
R, graphics). The Galaaz **bridge** adds overhead mainly from **starting a session**, **serializing
|
|
4230
|
+
requests**, and **wrapping results** in Ruby objects—not from reimplementing R’s numerical work.
|
|
4231
|
+
|
|
4232
|
+
Practical tips:
|
|
4233
|
+
|
|
4234
|
+
* Keep **hot loops** in R or vectorized code when possible; use Ruby for orchestration, I/O, and
|
|
4235
|
+
glue.
|
|
4236
|
+
* **Reuse one process**: running many short scripts cold-starts Ruby, the JVM, and R each time;
|
|
4237
|
+
a long-lived process or repeated calls in one run amortize setup (see benchmarks below).
|
|
4238
|
+
* **Batch data**: merge shards in Ruby, then call **`R::Arrow.from_ruby_batches`** (or build one
|
|
4239
|
+
data frame) instead of millions of tiny R calls.
|
|
4240
|
+
|
|
4241
|
+
For measured discussion (including DESeq2-style workloads and warm comparisons), see
|
|
4242
|
+
**`docs/performance.md`** and **`docs/deseq2_airway_benchmark.md`** in the Galaaz repository.
|
|
4243
|
+
|
|
3530
4244
|
# Graphics in Galaaz
|
|
3531
4245
|
|
|
3532
4246
|
Creating graphics in Galaaz is quite easy, as it can use all the power of ggplot2. There are
|
|
3533
|
-
many resources
|
|
4247
|
+
many resources on the web that teach ggplot, so here we give a quick example of ggplot
|
|
3534
4248
|
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
|
|
4249
|
+
bar plot, showing cars that have 'above' or 'below' gas consumption. Let's first prepare
|
|
3536
4250
|
the data frame with the necessary data:
|
|
3537
4251
|
|
|
3538
4252
|
|
|
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'
|
|
4253
|
+
``` ruby
|
|
4254
|
+
# :mtcars -> Ruby handle
|
|
4255
|
+
mtcars = ~R[:mtcars]
|
|
4256
|
+
|
|
4257
|
+
# Row labels are not a plot column; copy them to car_name.
|
|
4258
|
+
mtcars.car_name = R.rownames(R[:mtcars])
|
|
4259
|
+
|
|
4260
|
+
# Z-score mpg (mean/sd on mtcars.mpg); round to 2 decimals.
|
|
3552
4261
|
mtcars.mpg_z = ((mtcars.mpg - mtcars.mpg.mean)/mtcars.mpg.sd).round 2
|
|
3553
4262
|
|
|
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'
|
|
4263
|
+
# ifelse is vectorized: below / above average mpg_z.
|
|
3557
4264
|
mtcars.mpg_type = (mtcars.mpg_z < 0).ifelse("below", "above")
|
|
3558
4265
|
|
|
3559
|
-
#
|
|
4266
|
+
# Sort rows by mpg_z.
|
|
3560
4267
|
mtcars = mtcars[mtcars.mpg_z.order, :all]
|
|
3561
4268
|
|
|
3562
|
-
#
|
|
4269
|
+
# Factor car_name so plot order follows sort.
|
|
3563
4270
|
mtcars.car_name = mtcars.car_name.factor levels: mtcars.car_name
|
|
3564
4271
|
|
|
3565
|
-
# let's look at the final data frame
|
|
3566
4272
|
puts mtcars.head
|
|
3567
4273
|
```
|
|
3568
4274
|
|
|
@@ -3582,24 +4288,24 @@ puts mtcars.head
|
|
|
3582
4288
|
## Chrysler Imperial Chrysler Imperial -0.89 below
|
|
3583
4289
|
## Maserati Bora Maserati Bora -0.84 below
|
|
3584
4290
|
```
|
|
3585
|
-
Now,
|
|
3586
|
-
|
|
4291
|
+
Now, let's plot the diverging bar plot. When using gKnit, you normally do **not** need to open a
|
|
4292
|
+
graphics device manually; gKnit arranges the figure device for chunk output. Galaaz
|
|
3587
4293
|
provides integration with ggplot. The interested reader should check online for more
|
|
3588
4294
|
information on ggplot, since it is outside the scope of this manual describing
|
|
3589
|
-
how ggplot works.
|
|
4295
|
+
how ggplot works. Here we give only a brief description of how this plot is generated.
|
|
3590
4296
|
|
|
3591
|
-
ggplot implements the 'grammar of graphics'. In this approach, plots are
|
|
4297
|
+
ggplot implements the 'grammar of graphics'. In this approach, plots are built by
|
|
3592
4298
|
adding layers to the plot. On the first layer we describe what we want on the 'x'
|
|
3593
4299
|
and 'y' axis of the plot. In this case, we have 'car_name' on the 'x' axis and
|
|
3594
4300
|
'mpg\_z' on the 'y' axis. Then the type of graph is specified by adding
|
|
3595
4301
|
'geom\_bar' (for a bar graph). We specify that our bars should be filled using
|
|
3596
|
-
'mpg\_type', which is either 'above' or '
|
|
4302
|
+
'mpg\_type', which is either 'above' or 'below' giving then two colours for
|
|
3597
4303
|
filling. On the next layer we specify the labels for the graph, then we add the
|
|
3598
4304
|
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
|
|
4305
|
+
but in this graph we want the bars to be horizontally laid so we add 'coord\_flip'.
|
|
3600
4306
|
|
|
3601
4307
|
|
|
3602
|
-
```ruby
|
|
4308
|
+
``` ruby
|
|
3603
4309
|
require 'ggplot'
|
|
3604
4310
|
|
|
3605
4311
|
puts mtcars.ggplot(E.aes(x: :car_name, y: :mpg_z, label: :mpg_z)) +
|
|
@@ -3613,16 +4319,16 @@ puts mtcars.ggplot(E.aes(x: :car_name, y: :mpg_z, label: :mpg_z)) +
|
|
|
3613
4319
|
```
|
|
3614
4320
|
|
|
3615
4321
|
|
|
3616
|
-

|
|
4322
|
+

|
|
3617
4323
|
|
|
3618
4324
|
# Coding with Tidyverse
|
|
3619
4325
|
|
|
3620
4326
|
In R, and when coding with 'tidyverse', arguments to a function are usually not
|
|
3621
|
-
*
|
|
4327
|
+
*referentially transparent*. That is, you can’t replace a value with a seemingly equivalent
|
|
3622
4328
|
object that you’ve defined elsewhere. To see the problem, let's first define a data frame:
|
|
3623
4329
|
|
|
3624
4330
|
|
|
3625
|
-
```ruby
|
|
4331
|
+
``` ruby
|
|
3626
4332
|
df = R.data__frame(x: (1..3), y: (3..1))
|
|
3627
4333
|
puts df
|
|
3628
4334
|
```
|
|
@@ -3637,21 +4343,21 @@ puts df
|
|
|
3637
4343
|
and now, let's look at this code:
|
|
3638
4344
|
|
|
3639
4345
|
|
|
3640
|
-
```r
|
|
4346
|
+
``` r
|
|
3641
4347
|
my_var <- x
|
|
3642
4348
|
filter(df, my_var == 1)
|
|
3643
4349
|
```
|
|
3644
4350
|
It generates the following error: "object 'x' not found.
|
|
3645
4351
|
|
|
3646
|
-
However, in Galaaz, arguments are
|
|
3647
|
-
code
|
|
4352
|
+
However, in Galaaz, arguments are referentially transparent as can be seen by the
|
|
4353
|
+
code below. Note initially that 'my_var = R[:x]' will not give the error "object 'x' not found"
|
|
3648
4354
|
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
|
|
4355
|
+
my\_var is a variable that resolves to ':x' and it becomes equivalent to (R[:x].eq 1) which is
|
|
3650
4356
|
what we want.
|
|
3651
4357
|
|
|
3652
4358
|
|
|
3653
|
-
```ruby
|
|
3654
|
-
my_var = :x
|
|
4359
|
+
``` ruby
|
|
4360
|
+
my_var = R[:x]
|
|
3655
4361
|
puts df.filter(my_var.eq 1)
|
|
3656
4362
|
```
|
|
3657
4363
|
|
|
@@ -3659,7 +4365,7 @@ puts df.filter(my_var.eq 1)
|
|
|
3659
4365
|
## x y
|
|
3660
4366
|
## 1 1 3
|
|
3661
4367
|
```
|
|
3662
|
-
As stated by
|
|
4368
|
+
As stated by Hadley
|
|
3663
4369
|
|
|
3664
4370
|
> dplyr code is ambiguous. Depending on what variables are defined where,
|
|
3665
4371
|
> filter(df, x == y) could be equivalent to any of:
|
|
@@ -3671,18 +4377,18 @@ df[x == df$y, ]
|
|
|
3671
4377
|
df[x == y, ]
|
|
3672
4378
|
```
|
|
3673
4379
|
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
|
|
4380
|
+
expressions are build with symbols. In doing filter(df, R[:x].eq y) we are looking for elements
|
|
3675
4381
|
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
|
|
4382
|
+
filter(df, R[:x].eq R[:y]) we are looking for elements in which the 'x' column value is equal to
|
|
3677
4383
|
the 'y' column value. This can be seen in the following two chunks of code:
|
|
3678
4384
|
|
|
3679
4385
|
|
|
3680
|
-
```ruby
|
|
4386
|
+
``` ruby
|
|
3681
4387
|
y = 1
|
|
3682
4388
|
x = 2
|
|
3683
4389
|
|
|
3684
4390
|
# looking for values where the 'x' column is equal to the 'y' column
|
|
3685
|
-
puts df.filter(:x.eq :y)
|
|
4391
|
+
puts df.filter(R[:x].eq R[:y])
|
|
3686
4392
|
```
|
|
3687
4393
|
|
|
3688
4394
|
```
|
|
@@ -3691,10 +4397,10 @@ puts df.filter(:x.eq :y)
|
|
|
3691
4397
|
```
|
|
3692
4398
|
|
|
3693
4399
|
|
|
3694
|
-
```ruby
|
|
4400
|
+
``` ruby
|
|
3695
4401
|
# looking for values where the 'x' column is equal to the 'y' variable
|
|
3696
4402
|
# in this case, the number 1
|
|
3697
|
-
puts df.filter(:x.eq y)
|
|
4403
|
+
puts df.filter(R[:x].eq y)
|
|
3698
4404
|
```
|
|
3699
4405
|
|
|
3700
4406
|
```
|
|
@@ -3726,18 +4432,19 @@ Unfortunately, in R, this function can fail silently if one of the variables isn
|
|
|
3726
4432
|
in the data frame, but is present in the global environment. We will not go through here how
|
|
3727
4433
|
to solve this problem in R.
|
|
3728
4434
|
|
|
3729
|
-
In Galaaz the method mutate_y
|
|
4435
|
+
In Galaaz the method mutate_y below will work fine and will never fail silently.
|
|
3730
4436
|
|
|
3731
4437
|
|
|
3732
|
-
```ruby
|
|
4438
|
+
``` ruby
|
|
3733
4439
|
def mutate_y(df)
|
|
3734
|
-
|
|
4440
|
+
# Mutate column names are Ruby kwargs (y: …). Use .assign only for R `<-` expressions.
|
|
4441
|
+
df.mutate(y: R[:a] + R[:x])
|
|
3735
4442
|
end
|
|
3736
4443
|
```
|
|
3737
4444
|
Here we create a data frame that has only one column named 'x':
|
|
3738
4445
|
|
|
3739
4446
|
|
|
3740
|
-
```ruby
|
|
4447
|
+
``` ruby
|
|
3741
4448
|
df1 = R.data__frame(x: (1..3))
|
|
3742
4449
|
puts df1
|
|
3743
4450
|
```
|
|
@@ -3749,34 +4456,31 @@ puts df1
|
|
|
3749
4456
|
## 3 3
|
|
3750
4457
|
```
|
|
3751
4458
|
|
|
3752
|
-
Note that method mutate_y will fail
|
|
3753
|
-
in the scope of the method. Variable 'a' has no relationship with the symbol
|
|
4459
|
+
Note that method mutate_y will fail independently from the fact that variable 'a' is defined and
|
|
4460
|
+
in the scope of the method. Variable 'a' has no relationship with the symbol `R[:a]` used in the
|
|
3754
4461
|
definition of 'mutate\_y' above:
|
|
3755
4462
|
|
|
3756
4463
|
|
|
3757
|
-
```ruby
|
|
4464
|
+
``` ruby
|
|
3758
4465
|
a = 10
|
|
3759
4466
|
mutate_y(df1)
|
|
3760
4467
|
```
|
|
3761
4468
|
|
|
3762
4469
|
```
|
|
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
|
|
4470
|
+
## Error: ℹ In argument: `y = a + x`.
|
|
4471
|
+
## Caused by error:
|
|
4472
|
+
## ! object 'a' not found
|
|
3770
4473
|
```
|
|
3771
4474
|
## Different expressions
|
|
3772
4475
|
|
|
3773
|
-
Let's move to the next problem as presented by
|
|
4476
|
+
Let's move to the next problem as presented by Hadley where trying to write a function in R
|
|
3774
4477
|
that will receive two argumens, the first a variable and the second an expression is not trivial.
|
|
3775
|
-
|
|
4478
|
+
Below we create a data frame and we want to write a function that groups data by a variable and
|
|
3776
4479
|
summarises it by an expression:
|
|
3777
4480
|
|
|
3778
4481
|
|
|
3779
|
-
```r
|
|
4482
|
+
``` r
|
|
4483
|
+
library(dplyr)
|
|
3780
4484
|
set.seed(123)
|
|
3781
4485
|
|
|
3782
4486
|
df <- data.frame(
|
|
@@ -3798,7 +4502,7 @@ as.data.frame(df)
|
|
|
3798
4502
|
## 5 2 1 1 4
|
|
3799
4503
|
```
|
|
3800
4504
|
|
|
3801
|
-
```r
|
|
4505
|
+
``` r
|
|
3802
4506
|
d2 <- df %>%
|
|
3803
4507
|
group_by(g1) %>%
|
|
3804
4508
|
summarise(a = mean(a))
|
|
@@ -3812,7 +4516,7 @@ as.data.frame(d2)
|
|
|
3812
4516
|
## 2 2 3.333333
|
|
3813
4517
|
```
|
|
3814
4518
|
|
|
3815
|
-
```r
|
|
4519
|
+
``` r
|
|
3816
4520
|
d2 <- df %>%
|
|
3817
4521
|
group_by(g2) %>%
|
|
3818
4522
|
summarise(a = mean(a))
|
|
@@ -3826,10 +4530,10 @@ as.data.frame(d2)
|
|
|
3826
4530
|
## 2 2 3
|
|
3827
4531
|
```
|
|
3828
4532
|
|
|
3829
|
-
As shown by
|
|
4533
|
+
As shown by Hadley, one might expect this function to do the trick:
|
|
3830
4534
|
|
|
3831
4535
|
|
|
3832
|
-
```r
|
|
4536
|
+
``` r
|
|
3833
4537
|
my_summarise <- function(df, group_var) {
|
|
3834
4538
|
df %>%
|
|
3835
4539
|
group_by(group_var) %>%
|
|
@@ -3842,15 +4546,15 @@ my_summarise <- function(df, group_var) {
|
|
|
3842
4546
|
|
|
3843
4547
|
In order to solve this problem, coding with dplyr requires the introduction of many new concepts
|
|
3844
4548
|
and functions such as 'quo', 'quos', 'enquo', 'enquos', '!!' (bang bang), '!!!' (triple bang).
|
|
3845
|
-
Again, we'll leave to
|
|
4549
|
+
Again, we'll leave to Hadley the explanation on how to use all those functions.
|
|
3846
4550
|
|
|
3847
4551
|
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
|
|
4552
|
+
'df' data frame defined previously in R (to access an R variable from Galaaz, we use the tilde
|
|
4553
|
+
operator `~` applied to the R variable name as a symbol, e.g. `:df`).
|
|
3850
4554
|
|
|
3851
4555
|
|
|
3852
|
-
```ruby
|
|
3853
|
-
puts
|
|
4556
|
+
``` ruby
|
|
4557
|
+
puts ~R[:df]
|
|
3854
4558
|
```
|
|
3855
4559
|
|
|
3856
4560
|
```
|
|
@@ -3863,20 +4567,20 @@ puts ~:df
|
|
|
3863
4567
|
```
|
|
3864
4568
|
|
|
3865
4569
|
We then create the 'my_summarize' method and call it passing the R data frame and
|
|
3866
|
-
the group by variable ':g1':
|
|
4570
|
+
the group by variable 'R[:g1]':
|
|
3867
4571
|
|
|
3868
4572
|
|
|
3869
|
-
```ruby
|
|
4573
|
+
``` ruby
|
|
3870
4574
|
def my_summarize(df, group_var)
|
|
3871
4575
|
df.group_by(group_var).
|
|
3872
|
-
summarize(a: :a.mean)
|
|
4576
|
+
summarize(a: R[:a].mean)
|
|
3873
4577
|
end
|
|
3874
4578
|
|
|
3875
|
-
puts my_summarize(:df, :g1)
|
|
4579
|
+
puts my_summarize(~R[:df], R[:g1])
|
|
3876
4580
|
```
|
|
3877
4581
|
|
|
3878
4582
|
```
|
|
3879
|
-
## # A tibble: 2
|
|
4583
|
+
## # A tibble: 2 × 2
|
|
3880
4584
|
## g1 a
|
|
3881
4585
|
## <dbl> <dbl>
|
|
3882
4586
|
## 1 1 2.5
|
|
@@ -3886,12 +4590,12 @@ puts my_summarize(:df, :g1)
|
|
|
3886
4590
|
It works!!! Well, let's make sure this was not just some coincidence
|
|
3887
4591
|
|
|
3888
4592
|
|
|
3889
|
-
```ruby
|
|
3890
|
-
puts my_summarize(:df, :g2)
|
|
4593
|
+
``` ruby
|
|
4594
|
+
puts my_summarize(~R[:df], R[:g2])
|
|
3891
4595
|
```
|
|
3892
4596
|
|
|
3893
4597
|
```
|
|
3894
|
-
## # A tibble: 2
|
|
4598
|
+
## # A tibble: 2 × 2
|
|
3895
4599
|
## g2 a
|
|
3896
4600
|
## <dbl> <dbl>
|
|
3897
4601
|
## 1 1 3
|
|
@@ -3907,7 +4611,7 @@ In the previous section we've managed to get rid of all NSE formulation for a si
|
|
|
3907
4611
|
does this remain true for more complex examples, or will the Galaaz way prove inpractical for
|
|
3908
4612
|
more complex code?
|
|
3909
4613
|
|
|
3910
|
-
In the next example
|
|
4614
|
+
In the next example Hadley proposes us to write a function that given an expression such as 'a'
|
|
3911
4615
|
or 'a * b', calculates three summaries. What we want a function that does the same as these R
|
|
3912
4616
|
statements:
|
|
3913
4617
|
|
|
@@ -3928,7 +4632,7 @@ summarise(df, mean = mean(a * b), sum = sum(a * b), n = n())
|
|
|
3928
4632
|
Let's try it in galaaz:
|
|
3929
4633
|
|
|
3930
4634
|
|
|
3931
|
-
```ruby
|
|
4635
|
+
``` ruby
|
|
3932
4636
|
def my_summarise2(df, expr)
|
|
3933
4637
|
df.summarize(
|
|
3934
4638
|
mean: E.mean(expr),
|
|
@@ -3937,9 +4641,10 @@ def my_summarise2(df, expr)
|
|
|
3937
4641
|
)
|
|
3938
4642
|
end
|
|
3939
4643
|
|
|
3940
|
-
puts my_summarise2((
|
|
3941
|
-
puts "
|
|
3942
|
-
|
|
4644
|
+
puts my_summarise2((~R[:df]), :a)
|
|
4645
|
+
puts "
|
|
4646
|
+
"
|
|
4647
|
+
puts my_summarise2((~R[:df]), R[:a] * R[:b])
|
|
3943
4648
|
```
|
|
3944
4649
|
|
|
3945
4650
|
```
|
|
@@ -3955,7 +4660,7 @@ careful about is the use of 'E' to build expressions from functions 'mean', 'sum
|
|
|
3955
4660
|
|
|
3956
4661
|
## Different input and output variable
|
|
3957
4662
|
|
|
3958
|
-
Now the next challenge presented by
|
|
4663
|
+
Now the next challenge presented by Hadley is to vary the name of the output variables based on
|
|
3959
4664
|
the received expression. So, if the input expression is 'a', we want our data frame columns to
|
|
3960
4665
|
be named 'mean\_a' and 'sum\_a'. Now, if the input expression is 'b', columns
|
|
3961
4666
|
should be named 'mean\_b' and 'sum\_b'.
|
|
@@ -3981,13 +4686,13 @@ mutate(df, mean_b = mean(b), sum_b = sum(b))
|
|
|
3981
4686
|
#> 4 2 2 5 4 3 15
|
|
3982
4687
|
#> # … with 1 more row
|
|
3983
4688
|
```
|
|
3984
|
-
In order to solve this problem in R,
|
|
4689
|
+
In order to solve this problem in R, Hadley needs to introduce some more new functions and notations:
|
|
3985
4690
|
'quo_name' and the ':=' operator from package 'rlang'
|
|
3986
4691
|
|
|
3987
4692
|
Here is our Ruby code:
|
|
3988
4693
|
|
|
3989
4694
|
|
|
3990
|
-
```ruby
|
|
4695
|
+
``` ruby
|
|
3991
4696
|
def my_mutate(df, expr)
|
|
3992
4697
|
mean_name = "mean_#{expr.to_s}"
|
|
3993
4698
|
sum_name = "sum_#{expr.to_s}"
|
|
@@ -3996,9 +4701,10 @@ def my_mutate(df, expr)
|
|
|
3996
4701
|
sum_name => E.sum(expr))
|
|
3997
4702
|
end
|
|
3998
4703
|
|
|
3999
|
-
puts my_mutate((
|
|
4000
|
-
puts "
|
|
4001
|
-
|
|
4704
|
+
puts my_mutate((~R[:df]), :a)
|
|
4705
|
+
puts "
|
|
4706
|
+
"
|
|
4707
|
+
puts my_mutate((~R[:df]), :b)
|
|
4002
4708
|
```
|
|
4003
4709
|
|
|
4004
4710
|
```
|
|
@@ -4026,25 +4732,25 @@ and variable mean\_name is not followed by ':' but by '=>'. This is standard Ru
|
|
|
4026
4732
|
|
|
4027
4733
|
## Capturing multiple variables
|
|
4028
4734
|
|
|
4029
|
-
Moving on with new complexities,
|
|
4735
|
+
Moving on with new complexities, Hadley proposes us to solve the problem in which the
|
|
4030
4736
|
summarise function will receive any number of grouping variables.
|
|
4031
4737
|
|
|
4032
4738
|
This again is quite standard Ruby. In order to receive an undefined number of paramenters
|
|
4033
4739
|
the paramenter is preceded by '*':
|
|
4034
4740
|
|
|
4035
4741
|
|
|
4036
|
-
```ruby
|
|
4742
|
+
``` ruby
|
|
4037
4743
|
def my_summarise3(df, *group_vars)
|
|
4038
4744
|
df.group_by(*group_vars).
|
|
4039
4745
|
summarise(a: E.mean(:a))
|
|
4040
4746
|
end
|
|
4041
4747
|
|
|
4042
|
-
puts my_summarise3((
|
|
4748
|
+
puts my_summarise3((~R[:df]), R[:g1], R[:g2])
|
|
4043
4749
|
```
|
|
4044
4750
|
|
|
4045
4751
|
```
|
|
4046
|
-
## # A tibble: 4
|
|
4047
|
-
## # Groups: g1 [
|
|
4752
|
+
## # A tibble: 4 × 3
|
|
4753
|
+
## # Groups: g1 [2]
|
|
4048
4754
|
## g1 g2 a
|
|
4049
4755
|
## <dbl> <dbl> <dbl>
|
|
4050
4756
|
## 1 1 1 3
|
|
@@ -4068,7 +4774,7 @@ In Ruby, there is no lazy evaluation of parameters and 'a' is always a variable
|
|
|
4068
4774
|
Variables assume their value as soon as they are used, so 'x = a' is immediately evaluate and
|
|
4069
4775
|
variable 'x' will receive the value of variable 'a' as soon as the Ruby statement is executed.
|
|
4070
4776
|
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
|
|
4777
|
+
Galaaz uses Ruby symbols to build expressions that are not bound to anything: 'R[:a].eq R[:b]' is
|
|
4072
4778
|
clearly an expression and has no relationship whatsoever with the statment 'a = b'. By using
|
|
4073
4779
|
symbols, variables and expressions all the possible ambiguities that are found in R are
|
|
4074
4780
|
eliminated in Galaaz.
|
|
@@ -4078,7 +4784,7 @@ of input they are expecting, they might be expecting regular variables or they m
|
|
|
4078
4784
|
expecting expressions and the R function will know how to deal with an input of the form
|
|
4079
4785
|
'a = b', now for the Ruby developer it might not be immediately clear if it should call the
|
|
4080
4786
|
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'.
|
|
4787
|
+
call the function passing the expression 'R[:a].eq R[:b]'.
|
|
4082
4788
|
|
|
4083
4789
|
|
|
4084
4790
|
## Advanced dplyr features
|
|
@@ -4101,28 +4807,29 @@ In the following examples, we show the use of functions 'group\_by\_at', 'summar
|
|
|
4101
4807
|
features of characters in the Starwars movies:
|
|
4102
4808
|
|
|
4103
4809
|
|
|
4104
|
-
```ruby
|
|
4105
|
-
puts (
|
|
4810
|
+
``` ruby
|
|
4811
|
+
puts (~R[:starwars]).head
|
|
4106
4812
|
```
|
|
4107
4813
|
|
|
4108
4814
|
```
|
|
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
|
-
## #
|
|
4815
|
+
## # A tibble: 6 × 14
|
|
4816
|
+
## name height mass hair_color skin_color eye_color birth_year sex gender
|
|
4817
|
+
## <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
|
|
4818
|
+
## 1 Luke Sky… 172 77 blond fair blue 19 male mascu…
|
|
4819
|
+
## 2 C-3PO 167 75 <NA> gold yellow 112 none mascu…
|
|
4820
|
+
## 3 R2-D2 96 32 <NA> white, bl… red 33 none mascu…
|
|
4821
|
+
## 4 Darth Va… 202 136 none white yellow 41.9 male mascu…
|
|
4822
|
+
## 5 Leia Org… 150 49 brown light brown 19 fema… femin…
|
|
4823
|
+
## 6 Owen Lars 178 120 brown, gr… light blue 52 male mascu…
|
|
4824
|
+
## # ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
|
|
4119
4825
|
## # vehicles <list>, starships <list>
|
|
4120
4826
|
```
|
|
4121
|
-
The grouped_mean function
|
|
4827
|
+
The grouped_mean function below will receive a grouping variable and calculate summaries for
|
|
4122
4828
|
the value\_variables given:
|
|
4123
4829
|
|
|
4124
4830
|
|
|
4125
|
-
```r
|
|
4831
|
+
``` r
|
|
4832
|
+
library(dplyr)
|
|
4126
4833
|
grouped_mean <- function(data, grouping_variables, value_variables) {
|
|
4127
4834
|
data %>%
|
|
4128
4835
|
group_by_at(grouping_variables) %>%
|
|
@@ -4133,7 +4840,22 @@ grouped_mean <- function(data, grouping_variables, value_variables) {
|
|
|
4133
4840
|
|
|
4134
4841
|
gm = starwars %>%
|
|
4135
4842
|
grouped_mean("eye_color", c("mass", "birth_year"))
|
|
4843
|
+
```
|
|
4136
4844
|
|
|
4845
|
+
```
|
|
4846
|
+
## Warning: `funs()` was deprecated in dplyr 0.8.0.
|
|
4847
|
+
## ℹ Please use a list of either functions or lambdas:
|
|
4848
|
+
##
|
|
4849
|
+
## # Simple named list: list(mean = mean, median = median)
|
|
4850
|
+
##
|
|
4851
|
+
## # Auto named with `tibble::lst()`: tibble::lst(mean, median)
|
|
4852
|
+
##
|
|
4853
|
+
## # Using lambdas list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))
|
|
4854
|
+
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
|
|
4855
|
+
## generated.
|
|
4856
|
+
```
|
|
4857
|
+
|
|
4858
|
+
``` r
|
|
4137
4859
|
as.data.frame(gm)
|
|
4138
4860
|
```
|
|
4139
4861
|
|
|
@@ -4159,20 +4881,20 @@ as.data.frame(gm)
|
|
|
4159
4881
|
The same code with Galaaz, becomes:
|
|
4160
4882
|
|
|
4161
4883
|
|
|
4162
|
-
```ruby
|
|
4884
|
+
``` ruby
|
|
4163
4885
|
def grouped_mean(data, grouping_variables, value_variables)
|
|
4164
4886
|
data.
|
|
4165
4887
|
group_by_at(grouping_variables).
|
|
4166
4888
|
mutate(count: E.n).
|
|
4167
|
-
summarise_at(E.c(value_variables, "count"),
|
|
4889
|
+
summarise_at(E.c(value_variables, "count"), ~R[:mean], na__rm: true).
|
|
4168
4890
|
rename_at(value_variables, E.funs(E.paste0("mean_", value_variables)))
|
|
4169
4891
|
end
|
|
4170
4892
|
|
|
4171
|
-
puts grouped_mean((
|
|
4893
|
+
puts grouped_mean((~R[:starwars]), "eye_color", E.c("mass", "birth_year"))
|
|
4172
4894
|
```
|
|
4173
4895
|
|
|
4174
4896
|
```
|
|
4175
|
-
## # A tibble: 15
|
|
4897
|
+
## # A tibble: 15 × 4
|
|
4176
4898
|
## eye_color mean_mass mean_birth_year count
|
|
4177
4899
|
## <chr> <dbl> <dbl> <dbl>
|
|
4178
4900
|
## 1 black 76.3 33 10
|
|
@@ -4192,17 +4914,19 @@ puts grouped_mean((~:starwars), "eye_color", E.c("mass", "birth_year"))
|
|
|
4192
4914
|
## 15 yellow 81.1 76.4 11
|
|
4193
4915
|
```
|
|
4194
4916
|
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4917
|
+
The examples above cover programmatic dplyr with string column names and `_at` helpers. The same
|
|
4918
|
+
Galaaz patterns (symbols, `E.*` for expression-safe functions, and Ruby methods on R-backed objects)
|
|
4919
|
+
extend to other tidyverse workflows; consult R package documentation for function-specific
|
|
4920
|
+
arguments.
|
|
4198
4921
|
|
|
4199
4922
|
# Contributing
|
|
4200
4923
|
|
|
4201
4924
|
* Fork it
|
|
4202
|
-
* Create your feature branch (git checkout -b my-new-feature)
|
|
4203
|
-
* Write
|
|
4204
|
-
|
|
4205
|
-
*
|
|
4206
|
-
*
|
|
4925
|
+
* Create your feature branch (`git checkout -b my-new-feature`)
|
|
4926
|
+
* Write tests — use **`bin/run_rspec`** or **`bin/run_all_rspec`** with **JRuby** so JVM flags and
|
|
4927
|
+
the load path match **`docs/testing.md`**
|
|
4928
|
+
* Commit your changes (`git commit -am 'Add some feature'`)
|
|
4929
|
+
* Push to the branch (`git push origin my-new-feature`)
|
|
4930
|
+
* Open a pull request
|
|
4207
4931
|
|
|
4208
4932
|
# References
|