tidyverse-patterns
Rのtidyverseパッケージを活用し、パイプ処理、結合、グループ化、purrr、stringrなどのモダンなパターンを使って、効率的で読みやすいRコードを記述するSkill。
📜 元の英語説明(参考)
Modern tidyverse patterns for R including pipes, joins, grouping, purrr, and stringr. Use when writing tidyverse R code.
🇯🇵 日本人クリエイター向け解説
Rのtidyverseパッケージを活用し、パイプ処理、結合、グループ化、purrr、stringrなどのモダンなパターンを使って、効率的で読みやすいRコードを記述するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o tidyverse-patterns.zip https://jpskill.com/download/8792.zip && unzip -o tidyverse-patterns.zip && rm tidyverse-patterns.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/8792.zip -OutFile "$d\tidyverse-patterns.zip"; Expand-Archive "$d\tidyverse-patterns.zip" -DestinationPath $d -Force; ri "$d\tidyverse-patterns.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
tidyverse-patterns.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
tidyverse-patternsフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
現代的な tidyverse パターン
dplyr 1.1+ および R 4.3+ を用いた現代的な tidyverse 開発のためのベストプラクティス
コア原則
- 現代的な tidyverse パターンを使用する - dplyr 1.1+ の機能、ネイティブパイプ、および現在の API を優先します
- 最適化の前にプロファイルする -
profvisとbenchを使用して、実際のボトルネックを特定します - 最初に読みやすいコードを書く - 最適化は、必要な場合にのみ、プロファイル後に行います
- tidyverse スタイルガイドに従う - 一貫した命名、スペーシング、および構造
パイプの使用法 (|> で %>% ではない)
- 常に magrittr の
%>%ではなく、ネイティブパイプ|>を使用する - R 4.3+ は必要なすべての機能を提供します
# 良い例 - 現代的なネイティブパイプ
data |>
filter(year >= 2020) |>
summarise(mean_value = mean(value))
# 避けるべき例 - レガシーな magrittr パイプ
data %>%
filter(year >= 2020) %>%
summarise(mean_value = mean(value))
結合構文 (dplyr 1.1+)
- 結合には、文字ベクトルではなく
join_by()を使用する - 不等号、ローリング、およびオーバーラップ結合をサポート
# 良い例 - 現代的な結合構文
transactions |>
inner_join(companies, by = join_by(company == id))
# 良い例 - 不等号結合
transactions |>
inner_join(companies, join_by(company == id, year >= since))
# 良い例 - ローリング結合 (最も近い一致)
transactions |>
inner_join(companies, join_by(company == id, closest(year >= since)))
# 避けるべき例 - 古い文字ベクトル構文
transactions |>
inner_join(companies, by = c("company" = "id"))
複数一致の処理
- 品質管理のために
multipleおよびunmatched引数を使用する
# 1:1 の一致を期待し、複数の一致でエラーを発生させる
inner_join(x, y, by = join_by(id), multiple = "error")
# 複数の一致を明示的に許可する
inner_join(x, y, by = join_by(id), multiple = "all")
# すべての行が一致することを確認する
inner_join(x, y, by = join_by(id), unmatched = "error")
データマスキングと tidy selection
- データマスキングと tidy selection の違いを理解する
- 関数引数には
{{}}(embrace) を使用する - 文字ベクトルには
.data[[]]を使用する
# データマスキング関数: arrange(), filter(), mutate(), summarise()
# tidy selection 関数: select(), relocate(), across()
# 関数引数 - {{}} で embrace する
my_summary <- function(data, group_var, summary_var) {
data |>
group_by({{ group_var }}) |>
summarise(mean_val = mean({{ summary_var }}))
}
# 文字ベクトル - .data[[]] を使用する
for (var in names(mtcars)) {
mtcars |> count(.data[[var]]) |> print()
}
# 複数の列 - across() を使用する
data |>
summarise(across({{ summary_vars }}, ~ mean(.x, na.rm = TRUE)))
現代的なグループ化と列操作
- 操作ごとのグループ化には
.byを使用する (dplyr 1.1+) - データマスキング関数内での列選択には
pick()を使用する - 複数の列に関数を適用するには
across()を使用する - 複数行の要約には
reframe()を使用する
# 良い例 - 操作ごとのグループ化 (常にグループ化されていないものを返す)
data |>
summarise(mean_value = mean(value), .by = category)
# 良い例 - 複数のグループ化変数
data |>
summarise(total = sum(revenue), .by = c(company, year))
# 良い例 - 列選択のための pick()
data |>
summarise(
n_x_cols = ncol(pick(starts_with("x"))),
n_y_cols = ncol(pick(starts_with("y")))
)
# 良い例 - 関数適用のための across()
data |>
summarise(across(where(is.numeric), mean, .names = "mean_{.col}"), .by = group)
# 良い例 - 複数行の結果のための reframe()
data |>
reframe(quantiles = quantile(x, c(0.25, 0.5, 0.75)), .by = group)
# 避けるべき例 - 古い永続的なグループ化パターン
data |>
group_by(category) |>
summarise(mean_value = mean(value)) |>
ungroup()
現代的な purrr パターン
- 非推奨の
map_dfr()の代わりにmap() |> list_rbind()を使用する - 副作用 (ファイル書き込み、プロット) には
walk()を使用する - コア全体にスケールするには
in_parallel()を使用する
# 現代的なデータフレームの行結合 (purrr 1.0+)
models <- data_splits |>
map(\(split) train_model(split)) |>
list_rbind() # map_dfr() を置き換える
# 列結合
summaries <- data_list |>
map(\(df) get_summary_stats(df)) |>
list_cbind() # map_dfc() を置き換える
# walk() による副作用
plots <- walk2(data_list, plot_names, \(df, name) {
p <- ggplot(df, aes(x, y)) + geom_point()
ggsave(name, p)
})
# 並列処理 (purrr 1.1.0+)
library(mirai)
daemons(4)
results <- large_datasets |>
map(in_parallel(expensive_computation))
daemons(0)
stringr による文字列操作
- base R の文字列関数よりも stringr を使用する
- 一貫した
str_プレフィックスと文字列を最初の引数とする順序 - パイプフレンドリーで、設計上ベクトル化されている
# 良い例 - stringr (一貫性があり、パイプフレンドリー)
text |>
str_to_lower() |>
str_trim() |>
str_replace_all("pattern", "replacement") |>
str_extract("\\d+")
# 一般的なパターン
str_detect(text, "pattern") # vs grepl("pattern", text)
str_extract(text, "pattern") # vs complex regmatches()
str_replace_all(text, "a", "b") # vs gsub("a", "b", text)
str_split(text, ",") # vs strsplit(text, ",")
str_length(text) # vs nchar(text)
str_sub(text, 1, 5) # vs substr(text, 1, 5)
# 文字列の結合とフォーマット
str_c("a", "b", "c") # vs paste0()
str_glue("Hello {name}!") # テンプレート
str_pad(text, 10, "left") # パディング
str_wrap(text, width = 80) # テキストの折り返し
# 大文字小文字変換
str_to_lower(text) # vs tolower()
str_to_upper(text) # vs toupper()
str_to_title(text) # vs tools::toTitleCase()
# 明確にするためのパターンヘルパー
str_detect(text, fixed("$")) # リテラル一致
str_detect(text, regex("\\d+")) # 明示的な正規表現
str_detect(text, coll("e", locale = "fr")) # 照合
# 避けるべき例 - 一貫性のない base R 関数
grepl("pattern", text) # 引数の順序が異なる
regmatches(text, regexpr(...)) # 複雑な抽出
gsub("a", "b", text) # 引数の順序が異なる
ベクトル化とパフォーマンス
# 良い例 - ベクトル化された操作
result <- x + y
# 良い例 - 型安定な purrr 関数
map_dbl(data, mean) 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Modern Tidyverse Patterns
Best practices for modern tidyverse development with dplyr 1.1+ and R 4.3+
Core Principles
- Use modern tidyverse patterns - Prioritize dplyr 1.1+ features, native pipe, and current APIs
- Profile before optimizing - Use profvis and bench to identify real bottlenecks
- Write readable code first - Optimize only when necessary and after profiling
- Follow tidyverse style guide - Consistent naming, spacing, and structure
Pipe Usage (|> not %>%)
- Always use native pipe
|>instead of magrittr%>% - R 4.3+ provides all needed features
# Good - Modern native pipe
data |>
filter(year >= 2020) |>
summarise(mean_value = mean(value))
# Avoid - Legacy magrittr pipe
data %>%
filter(year >= 2020) %>%
summarise(mean_value = mean(value))
Join Syntax (dplyr 1.1+)
- Use
join_by()instead of character vectors for joins - Support for inequality, rolling, and overlap joins
# Good - Modern join syntax
transactions |>
inner_join(companies, by = join_by(company == id))
# Good - Inequality joins
transactions |>
inner_join(companies, join_by(company == id, year >= since))
# Good - Rolling joins (closest match)
transactions |>
inner_join(companies, join_by(company == id, closest(year >= since)))
# Avoid - Old character vector syntax
transactions |>
inner_join(companies, by = c("company" = "id"))
Multiple Match Handling
- Use
multipleandunmatchedarguments for quality control
# Expect 1:1 matches, error on multiple
inner_join(x, y, by = join_by(id), multiple = "error")
# Allow multiple matches explicitly
inner_join(x, y, by = join_by(id), multiple = "all")
# Ensure all rows match
inner_join(x, y, by = join_by(id), unmatched = "error")
Data Masking and Tidy Selection
- Understand the difference between data masking and tidy selection
- Use
{{}}(embrace) for function arguments - Use
.data[[]]for character vectors
# Data masking functions: arrange(), filter(), mutate(), summarise()
# Tidy selection functions: select(), relocate(), across()
# Function arguments - embrace with {{}}
my_summary <- function(data, group_var, summary_var) {
data |>
group_by({{ group_var }}) |>
summarise(mean_val = mean({{ summary_var }}))
}
# Character vectors - use .data[[]]
for (var in names(mtcars)) {
mtcars |> count(.data[[var]]) |> print()
}
# Multiple columns - use across()
data |>
summarise(across({{ summary_vars }}, ~ mean(.x, na.rm = TRUE)))
Modern Grouping and Column Operations
- Use
.byfor per-operation grouping (dplyr 1.1+) - Use
pick()for column selection inside data-masking functions - Use
across()for applying functions to multiple columns - Use
reframe()for multi-row summaries
# Good - Per-operation grouping (always returns ungrouped)
data |>
summarise(mean_value = mean(value), .by = category)
# Good - Multiple grouping variables
data |>
summarise(total = sum(revenue), .by = c(company, year))
# Good - pick() for column selection
data |>
summarise(
n_x_cols = ncol(pick(starts_with("x"))),
n_y_cols = ncol(pick(starts_with("y")))
)
# Good - across() for applying functions
data |>
summarise(across(where(is.numeric), mean, .names = "mean_{.col}"), .by = group)
# Good - reframe() for multi-row results
data |>
reframe(quantiles = quantile(x, c(0.25, 0.5, 0.75)), .by = group)
# Avoid - Old persistent grouping pattern
data |>
group_by(category) |>
summarise(mean_value = mean(value)) |>
ungroup()
Modern purrr Patterns
- Use
map() |> list_rbind()instead of supersededmap_dfr() - Use
walk()for side effects (file writing, plotting) - Use
in_parallel()for scaling across cores
# Modern data frame row binding (purrr 1.0+)
models <- data_splits |>
map(\(split) train_model(split)) |>
list_rbind() # Replaces map_dfr()
# Column binding
summaries <- data_list |>
map(\(df) get_summary_stats(df)) |>
list_cbind() # Replaces map_dfc()
# Side effects with walk()
plots <- walk2(data_list, plot_names, \(df, name) {
p <- ggplot(df, aes(x, y)) + geom_point()
ggsave(name, p)
})
# Parallel processing (purrr 1.1.0+)
library(mirai)
daemons(4)
results <- large_datasets |>
map(in_parallel(expensive_computation))
daemons(0)
String Manipulation with stringr
- Use stringr over base R string functions
- Consistent
str_prefix and string-first argument order - Pipe-friendly and vectorized by design
# Good - stringr (consistent, pipe-friendly)
text |>
str_to_lower() |>
str_trim() |>
str_replace_all("pattern", "replacement") |>
str_extract("\\d+")
# Common patterns
str_detect(text, "pattern") # vs grepl("pattern", text)
str_extract(text, "pattern") # vs complex regmatches()
str_replace_all(text, "a", "b") # vs gsub("a", "b", text)
str_split(text, ",") # vs strsplit(text, ",")
str_length(text) # vs nchar(text)
str_sub(text, 1, 5) # vs substr(text, 1, 5)
# String combination and formatting
str_c("a", "b", "c") # vs paste0()
str_glue("Hello {name}!") # templating
str_pad(text, 10, "left") # padding
str_wrap(text, width = 80) # text wrapping
# Case conversion
str_to_lower(text) # vs tolower()
str_to_upper(text) # vs toupper()
str_to_title(text) # vs tools::toTitleCase()
# Pattern helpers for clarity
str_detect(text, fixed("$")) # literal match
str_detect(text, regex("\\d+")) # explicit regex
str_detect(text, coll("e", locale = "fr")) # collation
# Avoid - inconsistent base R functions
grepl("pattern", text) # argument order varies
regmatches(text, regexpr(...)) # complex extraction
gsub("a", "b", text) # different arg order
Vectorization and Performance
# Good - vectorized operations
result <- x + y
# Good - Type-stable purrr functions
map_dbl(data, mean) # always returns double
map_chr(data, class) # always returns character
# Avoid - Type-unstable base functions
sapply(data, mean) # might return list or vector
# Avoid - explicit loops for simple operations
result <- numeric(length(x))
for(i in seq_along(x)) {
result[i] <- x[i] + y[i]
}
Common Anti-Patterns to Avoid
Legacy Patterns
# Avoid - Old pipe
data %>% function()
# Avoid - Old join syntax
inner_join(x, y, by = c("a" = "b"))
# Avoid - Implicit type conversion
sapply() # Use map_*() instead
# Avoid - String manipulation in data masking
mutate(data, !!paste0("new_", var) := value)
# Use across() or other approaches instead
Performance Anti-Patterns
# Avoid - Growing objects in loops
result <- c()
for(i in 1:n) {
result <- c(result, compute(i)) # Slow!
}
# Good - Pre-allocate
result <- vector("list", n)
for(i in 1:n) {
result[[i]] <- compute(i)
}
# Better - Use purrr
result <- map(1:n, compute)
Migration from Old Patterns
From Base R to Modern Tidyverse
# Data manipulation
subset(data, condition) -> filter(data, condition)
data[order(data$x), ] -> arrange(data, x)
aggregate(x ~ y, data, mean) -> summarise(data, mean(x), .by = y)
# Functional programming
sapply(x, f) -> map(x, f) # type-stable
lapply(x, f) -> map(x, f)
# String manipulation
grepl("pattern", text) -> str_detect(text, "pattern")
gsub("old", "new", text) -> str_replace_all(text, "old", "new")
substr(text, 1, 5) -> str_sub(text, 1, 5)
nchar(text) -> str_length(text)
strsplit(text, ",") -> str_split(text, ",")
paste0(a, b) -> str_c(a, b)
tolower(text) -> str_to_lower(text)
From Old to New Tidyverse Patterns
# Pipes
data %>% function() -> data |> function()
# Grouping (dplyr 1.1+)
group_by(data, x) |>
summarise(mean(y)) |>
ungroup() -> summarise(data, mean(y), .by = x)
# Column selection
across(starts_with("x")) -> pick(starts_with("x")) # for selection only
# Joins
by = c("a" = "b") -> by = join_by(a == b)
# Multi-row summaries
summarise(data, x, .groups = "drop") -> reframe(data, x)
# Data reshaping
gather()/spread() -> pivot_longer()/pivot_wider()
# String separation (tidyr 1.3+)
separate(col, into = c("a", "b")) -> separate_wider_delim(col, delim = "_", names = c("a", "b"))
extract(col, into = "x", regex) -> separate_wider_regex(col, patterns = c(x = regex))
Superseded purrr Functions (purrr 1.0+)
map_dfr(x, f) -> map(x, f) |> list_rbind()
map_dfc(x, f) -> map(x, f) |> list_cbind()
map2_dfr(x, y, f) -> map2(x, y, f) |> list_rbind()
pmap_dfr(list, f) -> pmap(list, f) |> list_rbind()
imap_dfr(x, f) -> imap(x, f) |> list_rbind()
# For side effects
walk(x, write_file) # instead of for loops
walk2(data, paths, write_csv) # multiple arguments