jpskill.com
🛠️ 開発・MCP コミュニティ

rlang-patterns

tidy evaluationという、ちょっと難しい仕組みを使う関数を作る際に、データ操作や柔軟な引数処理などを、より効率的に行うためのテクニックを活用するSkill。

📜 元の英語説明(参考)

rlang metaprogramming patterns for data-masking, injection operators, and dynamic dots. Use when writing functions that use tidy evaluation.

🇯🇵 日本人クリエイター向け解説

一言でいうと

tidy evaluationという、ちょっと難しい仕組みを使う関数を作る際に、データ操作や柔軟な引数処理などを、より効率的に行うためのテクニックを活用するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o rlang-patterns.zip https://jpskill.com/download/8790.zip && unzip -o rlang-patterns.zip && rm rlang-patterns.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/8790.zip -OutFile "$d\rlang-patterns.zip"; Expand-Archive "$d\rlang-patterns.zip" -DestinationPath $d -Force; ri "$d\rlang-patterns.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して rlang-patterns.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → rlang-patterns フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

データマスキングのための現代的な rlang パターン

tidyverse のデータマスキングを強化するメタプログラミングフレームワーク

コアコンセプト

データマスキング は、R の式がデータフレームの列を、あたかも環境内の変数であるかのように参照することを可能にします。rlang は、tidyverse のデータマスキングを強化するメタプログラミングフレームワークを提供します。

主要な rlang ツール

  • Embracing {{}} - 関数引数をデータマスキング関数に転送します
  • Injection !! - 単一の式または値を注入します
  • Splicing !!! - リストから複数の引数を注入します
  • Dynamic dots - 注入をサポートするプログラム可能な ...
  • Pronouns .data/.env - データ変数と環境変数を明確に区別します

関数引数パターン

{{}} を使用した転送

{{}} を使用して、関数引数をデータマスキング関数に転送します:

# 単一引数の転送
my_summarise <- function(data, var) {
  data |> dplyr::summarise(mean = mean({{ var }}))
}

# 任意のデータマスキング式で動作します
mtcars |> my_summarise(cyl)
mtcars |> my_summarise(cyl * am)
mtcars |> my_summarise(.data$cyl)  # 代名詞構文がサポートされています

... の転送 (特別な構文は不要)

# 簡単なドット転送
my_group_by <- function(.data, ...) {
  .data |> dplyr::group_by(...)
}

# tidy selection でも動作します
my_select <- function(.data, ...) {
  .data |> dplyr::select(...)
}

# 単一引数の tidy selection の場合は、c() で囲みます
my_pivot_longer <- function(.data, ...) {
  .data |> tidyr::pivot_longer(c(...))
}

.data を使用した名前パターン

プログラムによる列アクセスには .data 代名詞を使用します:

# 名前による単一の列
my_mean <- function(data, var) {
  data |> dplyr::summarise(mean = mean(.data[[var]]))
}

# 使用法 - データマスキングから完全に隔離されています
mtcars |> my_mean("cyl")  # 曖昧さがなく、通常の関数のように動作します

# all_of() を使用した複数の列
my_select_vars <- function(data, vars) {
  data |> dplyr::select(all_of(vars))
}

mtcars |> my_select_vars(c("cyl", "am"))

注入演算子

各演算子の使用場面

演算子 ユースケース
{{ }} 関数引数の転送 summarise(mean = mean({{ var }}))
!! 単一の式/値の注入 summarise(mean = mean(!!sym(var)))
!!! 複数の引数の注入 group_by(!!!syms(vars))
.data[[]] 名前による列へのアクセス mean(.data[[var]])

!! を使用した高度な注入

# 文字列からシンボルを作成します
var <- "cyl"
mtcars |> dplyr::summarise(mean = mean(!!sym(var)))

# 名前の衝突を避けるために値を注入します
df <- data.frame(x = 1:3)
x <- 100
df |> dplyr::mutate(scaled = x / !!x)  # データと環境の x の両方を使用します

# tidyeval コンテキストには data_sym() を使用します (より堅牢)
mtcars |> dplyr::summarise(mean = mean(!!data_sym(var)))

!!! を使用したスプライシング

# 文字ベクトルからの複数のシンボル
vars <- c("cyl", "am")
mtcars |> dplyr::group_by(!!!syms(vars))

# または、tidy コンテキストには data_syms() を使用します
mtcars |> dplyr::group_by(!!!data_syms(vars))

# 引数のリストをスプライスします
args <- list(na.rm = TRUE, trim = 0.1)
mtcars |> dplyr::summarise(mean = mean(cyl, !!!args))

Dynamic Dots パターン

Dynamic Dots サポートのための list2() の使用

my_function <- function(...) {
  # 動的な機能のために、list() の代わりに list2() で収集します
  dots <- list2(...)
  # ドットを処理します...
}

# これらの機能を有効にします:
my_function(a = 1, b = 2)           # 通常の使用法
my_function(!!!list(a = 1, b = 2))  # リストをスプライスします
my_function("{name}" := value)      # 名前注入
my_function(a = 1, )               # 末尾のカンマは OK

Glue 構文を使用した名前注入

# 基本的な名前注入
name <- "result"
list2("{name}" := 1)  # list(result = 1) を作成します

# {{ を使用した関数引数内
my_mean <- function(data, var) {
  data |> dplyr::summarise("mean_{{ var }}" := mean({{ var }}))
}

mtcars |> my_mean(cyl)        # 列 "mean_cyl" を作成します
mtcars |> my_mean(cyl * am)   # 列 "mean_cyl * am" を作成します

# englue() を使用してカスタム名を許可します
my_mean <- function(data, var, name = englue("mean_{{ var }}")) {
  data |> dplyr::summarise("{name}" := mean({{ var }}))
}

# ユーザーはデフォルトをオーバーライドできます
mtcars |> my_mean(cyl, name = "cylinder_mean")

曖昧さを解消するための代名詞

.data.env のベストプラクティス

# 明示的な曖昧さの解消はマスキングの問題を防ぎます
cyl <- 1000  # 環境変数

mtcars |> dplyr::summarise(
  data_cyl = mean(.data$cyl),    # データフレームの列
  env_cyl = mean(.env$cyl),      # 環境変数
  ambiguous = mean(cyl)          # どちらか (通常はデータが優先されます)
)

# ループおよびプログラムによるコンテキストで使用します
vars <- c("cyl", "am")
for (var in vars) {
  result <- mtcars |> dplyr::summarise(mean = mean(.data[[var]]))
  print(result)
}

プログラミングパターン

ブリッジパターン

データマスキングと tidy selection の動作間の変換:

# across() は selection-to-data-mask ブリッジとして機能します
my_group_by <- function(data, vars) {
  data |> dplyr::group_by(across({{ vars }}))
}

# tidy selection で動作します
mtcars |> my_group_by(starts_with("c"))

# across(all_of()) は names-to-data-mask ブリッジとして機能します
my_group_by <- function(data, vars) {
  data |> dplyr::group_by(across(all_of(vars)))
}

mtcars |> my_group_by(c("cyl", "am"))

変換パターン

# ラップすることで単一の引数を変換します
my_mean <- function(data, var) {
  data |> dplyr::summarise(mean = mean({{ var }}, na.rm = TRUE))
}

# across() を使用してドットを変換します
my_means <- function(data, ...) {
  data |> dplyr::summarise(across(c(...), ~ mean(.x, na.rm = TRUE)))
}

# 手動変換 (高度)
my_means_manual <- function(.data, ...) {
  vars <- enquos(..., .named = TRUE)
  vars <- purrr::map(vars, ~ expr(mean(!!.x, na.rm = TRUE)))
  .data |> dplyr::summarise(!!!vars)
}

避けるべきエラーが発生しやすいパターン

これらの非推奨/危険なパターンは使用しないでください

# 回避 - 文字列の解析と eval (セキュリティリスク)
var <- "cyl"
code <- paste("mean("
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Modern rlang Patterns for Data-Masking

Metaprogramming framework that powers tidyverse data-masking

Core Concepts

Data-masking allows R expressions to refer to data frame columns as if they were variables in the environment. rlang provides the metaprogramming framework that powers tidyverse data-masking.

Key rlang Tools

  • Embracing {{}} - Forward function arguments to data-masking functions
  • Injection !! - Inject single expressions or values
  • Splicing !!! - Inject multiple arguments from a list
  • Dynamic dots - Programmable ... with injection support
  • Pronouns .data/.env - Explicit disambiguation between data and environment variables

Function Argument Patterns

Forwarding with {{}}

Use {{}} to forward function arguments to data-masking functions:

# Single argument forwarding
my_summarise <- function(data, var) {
  data |> dplyr::summarise(mean = mean({{ var }}))
}

# Works with any data-masking expression
mtcars |> my_summarise(cyl)
mtcars |> my_summarise(cyl * am)
mtcars |> my_summarise(.data$cyl)  # pronoun syntax supported

Forwarding ... (No Special Syntax Needed)

# Simple dots forwarding
my_group_by <- function(.data, ...) {
  .data |> dplyr::group_by(...)
}

# Works with tidy selections too
my_select <- function(.data, ...) {
  .data |> dplyr::select(...)
}

# For single-argument tidy selections, wrap in c()
my_pivot_longer <- function(.data, ...) {
  .data |> tidyr::pivot_longer(c(...))
}

Names Patterns with .data

Use .data pronoun for programmatic column access:

# Single column by name
my_mean <- function(data, var) {
  data |> dplyr::summarise(mean = mean(.data[[var]]))
}

# Usage - completely insulated from data-masking
mtcars |> my_mean("cyl")  # No ambiguity, works like regular function

# Multiple columns with all_of()
my_select_vars <- function(data, vars) {
  data |> dplyr::select(all_of(vars))
}

mtcars |> my_select_vars(c("cyl", "am"))

Injection Operators

When to Use Each Operator

Operator Use Case Example
{{ }} Forward function arguments summarise(mean = mean({{ var }}))
!! Inject single expression/value summarise(mean = mean(!!sym(var)))
!!! Inject multiple arguments group_by(!!!syms(vars))
.data[[]] Access columns by name mean(.data[[var]])

Advanced Injection with !!

# Create symbols from strings
var <- "cyl"
mtcars |> dplyr::summarise(mean = mean(!!sym(var)))

# Inject values to avoid name collisions
df <- data.frame(x = 1:3)
x <- 100
df |> dplyr::mutate(scaled = x / !!x)  # Uses both data and env x

# Use data_sym() for tidyeval contexts (more robust)
mtcars |> dplyr::summarise(mean = mean(!!data_sym(var)))

Splicing with !!!

# Multiple symbols from character vector
vars <- c("cyl", "am")
mtcars |> dplyr::group_by(!!!syms(vars))

# Or use data_syms() for tidy contexts
mtcars |> dplyr::group_by(!!!data_syms(vars))

# Splice lists of arguments
args <- list(na.rm = TRUE, trim = 0.1)
mtcars |> dplyr::summarise(mean = mean(cyl, !!!args))

Dynamic Dots Patterns

Using list2() for Dynamic Dots Support

my_function <- function(...) {
  # Collect with list2() instead of list() for dynamic features
  dots <- list2(...)
  # Process dots...
}

# Enables these features:
my_function(a = 1, b = 2)           # Normal usage
my_function(!!!list(a = 1, b = 2))  # Splice a list
my_function("{name}" := value)      # Name injection
my_function(a = 1, )               # Trailing commas OK

Name Injection with Glue Syntax

# Basic name injection
name <- "result"
list2("{name}" := 1)  # Creates list(result = 1)

# In function arguments with {{
my_mean <- function(data, var) {
  data |> dplyr::summarise("mean_{{ var }}" := mean({{ var }}))
}

mtcars |> my_mean(cyl)        # Creates column "mean_cyl"
mtcars |> my_mean(cyl * am)   # Creates column "mean_cyl * am"

# Allow custom names with englue()
my_mean <- function(data, var, name = englue("mean_{{ var }}")) {
  data |> dplyr::summarise("{name}" := mean({{ var }}))
}

# User can override default
mtcars |> my_mean(cyl, name = "cylinder_mean")

Pronouns for Disambiguation

.data and .env Best Practices

# Explicit disambiguation prevents masking issues
cyl <- 1000  # Environment variable

mtcars |> dplyr::summarise(
  data_cyl = mean(.data$cyl),    # Data frame column
  env_cyl = mean(.env$cyl),      # Environment variable
  ambiguous = mean(cyl)          # Could be either (usually data wins)
)

# Use in loops and programmatic contexts
vars <- c("cyl", "am")
for (var in vars) {
  result <- mtcars |> dplyr::summarise(mean = mean(.data[[var]]))
  print(result)
}

Programming Patterns

Bridge Patterns

Converting between data-masking and tidy selection behaviors:

# across() as selection-to-data-mask bridge
my_group_by <- function(data, vars) {
  data |> dplyr::group_by(across({{ vars }}))
}

# Works with tidy selection
mtcars |> my_group_by(starts_with("c"))

# across(all_of()) as names-to-data-mask bridge
my_group_by <- function(data, vars) {
  data |> dplyr::group_by(across(all_of(vars)))
}

mtcars |> my_group_by(c("cyl", "am"))

Transformation Patterns

# Transform single arguments by wrapping
my_mean <- function(data, var) {
  data |> dplyr::summarise(mean = mean({{ var }}, na.rm = TRUE))
}

# Transform dots with across()
my_means <- function(data, ...) {
  data |> dplyr::summarise(across(c(...), ~ mean(.x, na.rm = TRUE)))
}

# Manual transformation (advanced)
my_means_manual <- function(.data, ...) {
  vars <- enquos(..., .named = TRUE)
  vars <- purrr::map(vars, ~ expr(mean(!!.x, na.rm = TRUE)))
  .data |> dplyr::summarise(!!!vars)
}

Error-Prone Patterns to Avoid

Don't Use These Deprecated/Dangerous Patterns

# Avoid - String parsing and eval (security risk)
var <- "cyl"
code <- paste("mean(", var, ")")
eval(parse(text = code))  # Dangerous!

# Good - Symbol creation and injection
!!sym(var)  # Safe symbol injection

# Avoid - get() in data mask (name collisions)
with(mtcars, mean(get(var)))  # Collision-prone

# Good - Explicit injection or .data
with(mtcars, mean(!!sym(var)))  # Safe
# or
mtcars |> summarise(mean(.data[[var]]))  # Even safer

Common Mistakes

# Don't use {{ }} on non-arguments
my_func <- function(x) {
  x <- force(x)  # x is now a value, not an argument
  quo(mean({{ x }}))  # Wrong! Captures value, not expression
}

# Don't mix injection styles unnecessarily
# Pick one approach and stick with it:
# Either: embrace pattern
my_func <- function(data, var) data |> summarise(mean = mean({{ var }}))
# Or: defuse-and-inject pattern
my_func <- function(data, var) {
  var <- enquo(var)
  data |> summarise(mean = mean(!!var))
}

Package Development with rlang

Import Strategy

# In DESCRIPTION:
Imports: rlang

# In NAMESPACE, import specific functions:
importFrom(rlang, enquo, enquos, expr, !!!, :=)

# Or import key functions:
#' @importFrom rlang := enquo enquos

Documentation Tags

#' @param var <[`data-masked`][dplyr::dplyr_data_masking]> Column to summarize
#' @param ... <[`dynamic-dots`][rlang::dyn-dots]> Additional grouping variables
#' @param cols <[`tidy-select`][dplyr::dplyr_tidy_select]> Columns to select

Testing rlang Functions

# Test data-masking behavior
test_that("function supports data masking", {
  result <- my_function(mtcars, cyl)
  expect_equal(names(result), "mean_cyl")

  # Test with expressions
  result2 <- my_function(mtcars, cyl * 2)
  expect_true("mean_cyl * 2" %in% names(result2))
})

# Test injection behavior
test_that("function supports injection", {
  var <- "cyl"
  result <- my_function(mtcars, !!sym(var))
  expect_true(nrow(result) > 0)
})

This modern rlang approach enables clean, safe metaprogramming while maintaining the intuitive data-masking experience users expect from tidyverse functions.