Apache Arrow R 包表达式绑定开发实战:以 lubridate::mday() 为例的完整教程 数据工程大数据序列化数据分析【免费下载链接】arrowApache Arrow is a multi-language toolbox for accelerated data interchange and in-memory processing项目地址https://gitcode.com/gh_mirrors/arrow13/arrow点击查看免费下载本教程源自 Apache Arrow 官方《新贡献者指南》中的 R 教程文档docs/source/developers/guide/tutorials/r_tutorial.rst以「为 Arrow R 包新增一个lubridate::mday()表达式绑定Expression Binding」这一真实任务为主线完整演示了从环境准备、函数调研、源码修改、测试编写、代码风格检查到提交 Pull Request 的全流程。读完本文你将掌握 Arrow R 包 dplyr 计算函数的绑定机制、绑定注册表的底层实现原理以及如何为其他 C compute 函数编写同样规范的 R 绑定并合入上游仓库。教程背景新贡献者指南中的 R 教程在新贡献者指南中R 相关的教程目前包含两个部分Writing Bindings Walkthrough第一个收录进指南的 R 包教程以「Writing Bindings」vignette 中新增的Walkthrough章节形式存在。该教程演示了如何将 C compute 函数starts_with()绑定到 R 基础函数startsWith()可参考 r/vignettes 下的开发者文档。R tutorial on adding a lubridate binding本文重点讲解的教程即为一个具体的lubridate::mday()绑定提交 Pull Request 的实战记录。它不同于step_by_step系列见 docs/source/developers/guide/step_by_step/index.rst不是通用步骤清单而是针对特定案例的完整演练。两个教程都指向同一个目标让新贡献者理解 Arrow R 包如何把 R 函数「翻译」为 Arrow C 计算表达式从而在dplyr管道中对 Arrow 数据集Table / RecordBatch / Dataset执行惰性计算而不是把数据拉回 R 再逐行处理。提示教程中涉及的通用流程仓库搭建、构建、找 issue、测试、风格、PR 生命周期在指南的step_by_step各章节中有更详细的说明遇到信息缺失时可随时前往查阅set_up.rst环境与仓库初始化building.rst构建 Arrowfinding_issues.rst寻找合适的 issuetesting.rst测试约定styling.rst代码风格pr_lifecycle.rstPull Request 生命周期任务设定绑定 lubridate::mday()本教程要解决的 issue 是实现一个与 R 包lubridate中mday()行为一致的简单绑定。mday()是 lubridate 提供的日期提取函数输入一个日期对象返回该日期所在的「日」day of month返回值为数值类型。如果你还没有 issue可以参考指南中的 finding_issues.rst 部分寻找合适的入门任务。选定 issue 并分配给自己后就可以开始动手了。第一步搭建 Arrow 仓库环境假设 Git 已安装未安装请参考 set_up.rst。首先将 Apache Arrow 仓库 fork 到自己的账号下fork 步骤见step_by_step指南然后克隆本地仓库并添加上游upstream远程地址$ git clone https://github.com/your username/arrow.git $ cd arrow $ git remote add upstream https://github.com/apache/arrow第二步构建 R 包R 包的构建步骤因操作系统而异教程不展开具体构建命令只做指引构建流程的入门介绍参考 building.rstR 包的具体构建说明参考 R 开发者文档r 目录及 ci/scripts/c_glib_build.sh 等脚本体现了其构建管线。构建好开发版 R 包后源码改动即可通过devtools::load_all()等方式即时生效用于后续验证。第三步基于最新 main 创建开发分支在动手修改前先从更新后的 main 创建一个新分支$ git checkout main $ git fetch upstream $ git pull --ff-only upstream main $ git checkout -b ARROW-14816分支名建议与 issue 编号对应。本教程示例中分支为ARROW-14816对应当时 Jira 上的 issue。调研阶段吃透 R 函数与 C compute 函数添加绑定前最关键的一步是搞清楚两侧函数的输入输出语义确认它们是否等价、是否存在选项类option class。考察 lubridate::mday()查阅 lubridate 文档可知mday()接受一个日期对象返回该日期在月份中的天数1–31输出为数值对象。在 R 控制台中运行示例加深理解 library(lubridate) mday(as.Date(2000-12-31)) [1] 31 mday(ymd(080306)) [1] 6可以看到2000-12-31是 12 月的最后一天返回 31080306按ymd解析为 2008-03-06返回 6。考察 Arrow C day() 函数从 Arrow C compute 函数文档可知day是一个一元函数unary function即只接受单个数据输入。其输入必须是Temporal 类数据类型返回值是Integer/数值类型。Temporal 类包括日期类型Date32、Date64、时间类型Time32、Time64、Timestamp时间戳、Duration时长、Interval区间。我们可以在 R 控制台中直接调用 Arrow C 函数来验证其行为call_function()是 Arrow R 包中直接调用 C compute 内核的入口定义见 r/R/compute.R call_function(day, Scalar$create(lubridate::ymd(2000-12-31))) Scalar 31结果同样是 31说明 lubridate 的mday()与 Arrow C 的day()在输入输出类型上完全等价。进一步确认lubridatemday()没有额外参数Arrow Cday()也没有关联的选项类option classes。这意味着一对一的简单映射即可无需像wday()需要 label 处理、strptime()需要 format 参数那样编写更复杂的绑定逻辑。如果你的目标 C 函数带有选项类需要额外处理。可参考「Writing Bindings」文章中「Examining the C function」一节的说明了解如何把选项类参数转换为Expression$create()的options列表。实现阶段注册 mday 绑定绑定注册表在哪里教程撰写时2022 年绑定映射定义在expression.R中在当前仓库中这个职责已经拆分到更细粒度的文件里一元函数的直接映射表.unary_function_map位于 r/R/dplyr-funcs-simple.R。该文件头部注释清楚说明了这张表的适用条件表中每个 R 函数恰好接受一个参数直接映射到 Arrow C compute 内核且不需要指定任何非默认选项。更复杂的 R 函数映射定义在dplyr-funcs-*.R中。以日期时间类函数为例当前仓库中的实际映射r/R/dplyr-funcs-simple.R# date and time functions lubridate::day day, lubridate::dst is_dst, lubridate::hour hour, lubridate::isoweek iso_week, lubridate::epiweek us_week, lubridate::isoyear iso_year, lubridate::epiyear us_year, lubridate::minute minute, lubridate::quarter quarter, # second is defined in dplyr-functions.R # wday is defined in dplyr-functions.R lubridate::mday day, lubridate::yday day_of_year, lubridate::year year, lubridate::leap_year is_leap_year可以看到lubridate::mday day这一行正是当年教程添加的映射如今已经合入主干。同时second、wday等因为逻辑更复杂涉及额外参数被注释标明定义在dplyr-funcs.R/dplyr-funcs-datetime.R中比如wday的绑定就定义在 r/R/dplyr-funcs-datetime.R。绑定注册的底层原理映射表本身并不会自动生效。真正把 R 函数名注册进查询引擎的是register_binding()其定义在 r/R/dplyr-funcs.Rregister_binding - function(fun_name, fun, notes character(0)) { unqualified_name - sub(^.*?:{}, , fun_name) previous_fun - .cache$functions[[unqualified_name]] # if the unqualified name exists in the registry, warn if (!is.null(previous_fun) !identical(fun, previous_fun)) { warn( paste0( A \, unqualified_name, \ binding already exists in the registry and will be overwritten. ) ) } # register both as pkg::fun and as fun if qualified_name is prefixed .cache$functions[[unqualified_name]] - fun .cache$functions[[fun_name]] - fun .cache$docs[[fun_name]] - notes invisible(previous_fun) }它的关键行为同时以pkg::fun如lubridate::mday和去包名后的mday两个键注册因此用户在 dplyr 管道中写mday()或lubridate::mday()都能命中若同名绑定已存在且实现不同会给出警告提示覆盖notes参数用于文档标注限制与差异最终汇总到 r/R/dplyr-funcs-doc.R 生成的函数支持清单中。而.unary_function_map的注册发生在register_bindings_array_function_map()r/R/dplyr-funcs-simple.R中它为每个映射构造一个unary_factory(operator)工厂函数返回function(...) Expression$create(operator, ...)即把 R 调用转换成 Arrow 的调用表达式。Expression$create()定义在 r/R/expression.R负责构造函数调用表达式非表达式参数会被自动包装为标量表达式。添加绑定的一行改动因此对于mday()这种简单一元映射核心改动只有一行把mday day旧版本位于expression.R现在位于dplyr-funcs-simple.R的映射表中加入表达式列表将 R 函数连接到 C 的day函数。当年的 diff 如下quarter quarter, # second is defined in dplyr-functions.R # wday is defined in dplyr-functions.R mday day, yday day_of_year, year year,从源码结构看这样的「一行式」绑定之所以可行完全是因为mday与day之间是纯语义等价映射输入输出类型一致、无额外参数、无选项类。这也解释了为什么教程一开始要花大量篇幅做调研——调研结论直接决定了改动的复杂度。测试阶段为绑定编写 testthat 测试添加测试用例代码改完后需要添加测试验证功能正确。参考类似函数如yday()、day()的测试合适的放置位置是 r/tests/testthat/test-dplyr-funcs-datetime.R。该文件头部用skip_if_not_available(acero)保证只在启用 Arrow 查询引擎Acero时运行并统一设置 UTC 时区以保证测试可复现。教程添加了两个测试用例分别覆盖 timestamp 和 date 两种输入test_that(extract mday from timestamp, { compare_dplyr_binding( .input %% mutate(x mday(datetime)) %% collect(), test_df ) })test_that(extract mday from date, { compare_dplyr_binding( .input %% mutate(x mday(date)) %% collect(), test_df ) })这两个测试现在仍存在于仓库中r/tests/testthat/test-dplyr-funcs-datetime.R 与 r/tests/testthat/test-dplyr-funcs-datetime.R后者还额外加入了x2 lubridate::mday(date)来同时验证带包名前缀的调用方式。compare_dplyr_binding()是 Arrow 测试套件的核心断言辅助函数定义在 r/tests/testthat/helper-expectation.R。其工作方式是把同一段 dplyr 管道分别在普通data.frame上用常规 dplyr 计算和 ArrowTable上用 Arrow 引擎执行各跑一遍然后断言两者结果相等compare_dplyr_binding - function(expr, tbl, warning NA, ...) { # Quote the contents of expr so that we can evaluate it twice expr - rlang::enquo(expr) # Get the expected output by evaluating expr on the .input data.frame using regular dplyr expected - rlang::eval_tidy(expr, rlang::new_data_mask(rlang::env(.input tbl))) ... # Evaluate expr on a Table object and compare with expected expect_warning( via_table - rlang::eval_tidy( expr, rlang::new_data_mask(rlang::env(.input arrow_table(tbl))) ), warning ) expect_equal(via_table, expected, ...) }测试用例中的test_df定义在测试文件开头r/tests/testthat/test-dplyr-funcs-datetime.Rtest_df - tibble::tibble( datetime c(test_date, NA) 1, date c(as.Date(2021-09-09), NA), integer 1:2 )包含一个带时区的 timestamp 列和一个 date 列并各带一个NA用于覆盖缺失值的处理。运行测试在 R 包开发环境中运行测试devtools::test(filterdatetime)教程中记录的运行输出节选 devtools::test(filterdatetime) ℹ Loading arrow See arrow_info() for available features ℹ Testing arrow See arrow_info() for available features ✔ | F W S OK | Context ✖ | 1 230 | dplyr-funcs-datetime [1.4s] ──────────────────────────────────────────────────────────────────────────────── Failure (test-dplyr-funcs-datetime.R:187:3): strftime %%(...) did not throw the expected error. ... ══ Results ════════════════════════════════════════════════════════════════════ Duration: 1.4 s [ FAIL 1 | WARN 0 | SKIP 0 | PASS 230 ]结果中有一个与strftime相关的失败但检查代码后确认它与本次改动无关strftime的绑定在 r/R/dplyr-funcs-datetime.R 单独实现涉及格式化参数可以继续推进。遇到这种情况可以询问其他贡献者是否也遇到类似失败也可能只是需要重新构建底层 C 库。代码风格检查接着运行 linter 检查代码是否符合 tidyverse 风格。在仓库根目录执行$ make style R -s -e setwd(..); if (requireNamespace(styler)) styler::style_file(setdiff(system(git diff --name-only | grep r/.*R$, intern TRUE), file.path(r, source(r/.styler_excludes.R)$value))) Loading required namespace: styler Styling 2 files: r/R/expression.R ✔ r/tests/testthat/test-dplyr-funcs-datetime.R ℹ ──────────────────────────────────────────── Status Count Legend ✔ 1 File unchanged. ℹ 1 File changed. ✖ 0 Styling threw an error. ──────────────────────────────────────────── Please review the changes carefully!该命令的实现在 r/Makefile 中它利用styler包只对git diff中变更的 R 文件排除.styler_excludes.R列出的文件进行格式化。注意styler可能会对文件做出改动请仔细审查改动内容后再提交。提交与创建 Pull Request审查改动并提交用git status查看变更了哪些文件确保只提交本次任务相关的文件$ git status On branch ARROW-14816 Changes not staged for commit: (use git add file... to update what will be committed) (use git restore file... to discard changes in working directory) modified: R/expression.R modified: tests/testthat/test-dplyr-funcs-datetime.R再用git diff仔细检查两处文件的具体改动确认没有引入错误$ git diff diff --git a/r/R/expression.R b/r/R/expression.R index 37fc21c25..0e71803ec 100644 --- a/r/R/expression.R b/r/R/expression.R -70,6 70,7 quarter quarter, # second is defined in dplyr-functions.R # wday is defined in dplyr-functions.R mday day, yday day_of_year, year year, diff --git a/r/tests/testthat/test-dplyr-funcs-datetime.R b/r/tests/testthat/test-dplyr-funcs-datetime.R index 359a5403a..228eca56a 100644 --- a/r/tests/testthat/test-dplyr-funcs-datetime.R b/r/tests/testthat/test-dplyr-funcs-datetime.R -444,6 444,15 test_that(extract wday from timestamp, { ) }) test_that(extract mday from timestamp, { compare_dplyr_binding( .input %% mutate(x mday(datetime)) %% collect(), test_df ) }) test_that(extract yday from timestamp, { compare_dplyr_binding( .input %% -626,6 635,15 test_that(extract wday from date, { ) }) test_that(extract mday from date, { compare_dplyr_binding( .input %% mutate(x mday(date)) %% collect(), test_df ) }) test_that(extract yday from date, { compare_dplyr_binding( .input %%确认无误后提交把改动保存到分支历史$ git commit -am Adding a binding and a test for mday() lubridate [ARROW-14816 ed37d3a3b] Adding a binding and a test for mday() lubridate 2 files changed, 19 insertions()用git log检查提交历史$ git log commit ed37d3a3b3eef76b696532f10562fea85f809fab (HEAD - ARROW-14816) Author: Alenka Frim frim.alenkagmail.com Date: Fri Jan 21 09:15:31 2022 0100 Adding a binding and a test for mday() lubridate commit c5358787ee8f7b80f067292f49e5f032854041b9 (upstream/main, upstream/HEAD, main, ARROW-15346, ARROW-10643) Author: Krisztián Szűcs szucs.krisztiangmail.com Date: Thu Jan 20 09:45:59 2022 0900 ARROW-15372: [C][Gandiva] Gandiva now depends on boost/crc.hpp which is missing from the trimmed boost archive ...如果分支创建时间较早还需要先 rebase 到最新 upstream main确保没有合并冲突$ git pull upstream main --rebase推送分支并创建 Pull Request把工作推送到 fork 仓库origin$ git push origin ARROW-14816 Enumerating objects: 233, done. Counting objects: 100% (233/233), done. Delta compression using up to 8 threads Compressing objects: 100% (130/130), done. Writing objects: 100% (151/151), 35.78 KiB | 8.95 MiB/s, done. Total 151 (delta 129), reused 33 (delta 20), pack-reused 0 remote: Resolving deltas: 100% (129/129), completed with 80 local objects. remote: remote: Create a pull request for ARROW-14816 on GitHub by visiting: remote: https://github.com/AlenkaF/arrow/pull/new/ARROW-14816 remote: To https://github.com/AlenkaF/arrow.git * [new branch] ARROW-14816 - ARROW-14816推送成功后访问 Apache Arrow 仓库主页main 或 fork 均可会看到一条黄色提示栏提示分支ARROW-14816有最近的推送点击Compare pull request即可创建 Pull Request进入创建页面后需要把标题改为与 issue 对应ARROW-14816: [R] Implement bindings for lubridate::mday()。注意与 issue 标题保持一致教程中特别指出补上了标点符号。补充说明教程撰写当时项目使用 Jira issue 追踪器因此标题以ARROW-前缀开头项目目前改用 GitHub issues标题应改为GH-14816: [R] Implement bindings for lubridate::mday()形式。同时添加描述让其他维护者清楚这次改动的目的点击Create pull request后代码就可以在 Apache Arrow 仓库中以 Pull Request 的形式接受审查Pull Request 会自动关联到对应的 issueCI 随即开始运行。收到审查意见后可以修正代码、回复评论、解决对话直到被合并。关于 Pull Request 工作流的更多细节如 CI 检查、review 流程、合并条件等参见 pr_lifecycle.rst。总结一次绑定提交的完整链路回顾整个教程一次简单的表达式绑定贡献包含以下完整链路调研确认 R 函数与 C compute 函数语义等价输入类型、返回类型、参数、选项类用call_function()直接验证 C 行为映射在dplyr-funcs-simple.R旧版为expression.R的一元映射表中添加一行lubridate::mday day由register_bindings_array_function_map()通过Expression$create()生成绑定测试在test-dplyr-funcs-datetime.R中用compare_dplyr_binding()添加 timestamp 与 date 两个维度的用例用devtools::test(filterdatetime)验证用make style统一风格提交git status/git diff审查 →git commit→ rebase 上游 →git push→ 创建符合命名规范的 Pull Request等待审查与合并。对于更复杂的绑定带选项类、多参数、需要类型对齐套路相同只是映射表中无法一行解决需要在dplyr-funcs-*.R中用register_binding()Expression$create(..., options ...)编写更完整的函数。理解mday()这个最小案例后你已经掌握了 Arrow R 包 dplyr 计算层扩展的核心机制可以举一反三处理其他 compute 函数的绑定工作。赞分享数据工程大数据序列化数据分析【免费下载链接】arrowApache Arrow is a multi-language toolbox for accelerated data interchange and in-memory processing项目地址https://gitcode.com/gh_mirrors/arrow13/arrow点击查看免费下载相关推荐Apache Arrow R 教程为 Arrow R 包贡献一个 lubridate 函数绑定mday 实战Apache Arrow R 教程为 Arrow R 包贡献一个 lubridate 函数绑定mday 实战 本文基于 Apache Arrow 官方开发大数据数据分析数据工程序列化Apache Arrow R 包开发实战为 lubridate::mday() 编写 C 计算绑定Binding的完整指南Apache Arrow R 包开发实战为 lubridate::mday 编写 C 计算绑定Binding的完整指南 本篇教程源自 Apache A数据工程数据分析大数据Apache Arrow R 包 dplyr 未实现表达式报错机制全解析以 Dataset 的 filter() 为例Apache Arrow R 包 dplyr 未实现表达式报错机制全解析以 Dataset 的 filter 为例 Apache Arrow 的 R 包 r数据工程大数据序列化数据分析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考