r-package-development
Rパッケージの開発において、依存関係の管理、使いやすいAPI設計、品質を保証するテスト、そして分かりやすいドキュメント作成まで、一連の作業をサポートするSkill。
📜 元の英語説明(参考)
R package development guide covering dependencies, API design, testing, and documentation. Use when developing R packages.
🇯🇵 日本人クリエイター向け解説
Rパッケージの開発において、依存関係の管理、使いやすいAPI設計、品質を保証するテスト、そして分かりやすいドキュメント作成まで、一連の作業をサポートするSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o r-package-development.zip https://jpskill.com/download/8787.zip && unzip -o r-package-development.zip && rm r-package-development.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/8787.zip -OutFile "$d\r-package-development.zip"; Expand-Archive "$d\r-package-development.zip" -DestinationPath $d -Force; ri "$d\r-package-development.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
r-package-development.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
r-package-developmentフォルダができる - 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 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
R パッケージ開発の意思決定ガイド
R パッケージの依存関係、API 設計、テスト、ドキュメント、およびベストプラクティス
依存関係戦略
依存関係を追加するタイミング vs. base R
# 依存関係を追加するタイミング:
# - 大幅な機能向上
# - メンテナンス負荷の軽減
# - ユーザーエクスペリエンスの向上
# - 複雑な実装 (regex, dates, web)
# base R を使用するタイミング:
# - 単純なユーティリティ関数
# - パッケージが広く使用される (依存関係を最小限に抑える)
# - 依存関係が小さいメリットに対して大きい
# - base R の解決策が簡単
# 意思決定の例:
str_detect(x, "pattern") # stringr 依存関係を追加する価値あり
length(x) > 0 # これには purrr は不要
parse_dates(x) # lubridate 依存関係を追加する価値あり
x + 1 # これには dplyr は不要
Tidyverse 依存関係のガイドライン
# コア tidyverse (通常は価値あり):
dplyr # 複雑なデータ操作
purrr # 関数型プログラミング、並列処理
stringr # 文字列操作
tidyr # データのリシェイプ
# 特殊な tidyverse (慎重に評価):
lubridate # 大量の date 操作を行う場合
forcats # 多くのカテゴリ操作を行う場合
readr # 特定のファイル読み込みが必要な場合
ggplot2 # パッケージが可視化を作成する場合
# 重い依存関係 (控えめに使用):
tidyverse # メタパッケージ、非常に重い
shiny # インタラクティブなアプリのみ
DESCRIPTION での依存関係の指定
# 強力な依存関係 (必須)
Imports:
dplyr (>= 1.1.0),
rlang (>= 1.0.0)
# 推奨される依存関係 (オプション)
Suggests:
testthat (>= 3.0.0),
knitr,
rmarkdown
# 拡張された機能 (オプションだが、利用可能な場合はロードされる)
Enhances:
data.table
API 設計パターン
関数設計戦略
# 最新の tidyverse API パターン
# 1. 操作ごとのグループ化に .by を使用する
my_summarise <- function(.data, ..., .by = NULL) {
# 最新のグループ化された操作をサポートする
}
# 2. ユーザーが指定した列に {{ }} を使用する
my_select <- function(.data, cols) {
.data |> select({{ cols }})
}
# 3. 柔軟な引数に ... を使用する
my_mutate <- function(.data, ..., .by = NULL) {
.data |> mutate(..., .by = {{ .by }})
}
# 4. 一貫した型を返す (data.frames ではなく tibbles)
my_function <- function(.data) {
result |> tibble::as_tibble()
}
入力検証戦略
# 関数タイプ別の検証レベル:
# ユーザー向けの関数 - 包括的な検証
user_function <- function(x, threshold = 0.5) {
# すべての入力を徹底的にチェックする
if (!is.numeric(x)) stop("x must be numeric")
if (!is.numeric(threshold) || length(threshold) != 1) {
stop("threshold must be a single number")
}
# ... 関数の本体
}
# 内部関数 - 最小限の検証
.internal_function <- function(x, threshold) {
# 入力が有効であると仮定する (仮定をドキュメント化する)
# 重要な不変条件のみをチェックする
# ... 関数の本体
}
# vctrs を使用したパッケージ関数 - 型安定検証
safe_function <- function(x, y) {
x <- vec_cast(x, double())
y <- vec_cast(y, double())
# 自動型チェックと強制型変換
}
エラー処理パターン
# 適切なエラーメッセージ - 具体的で実行可能
if (length(x) == 0) {
cli::cli_abort(
"Input {.arg x} cannot be empty.",
"i" = "Provide a non-empty vector."
)
}
# エラーに関数名を含める
validate_input <- function(x, call = caller_env()) {
if (!is.numeric(x)) {
cli::cli_abort("Input must be numeric", call = call)
}
}
# 一貫したエラースタイルを使用する
# ユーザーフレンドリーなメッセージには cli パッケージを使用する
# 開発者ツールには rlang を使用する
エラークラス
# プログラムによる処理のためのカスタムエラークラス
my_error <- function(message, ..., call = caller_env()) {
cli::cli_abort(
message,
...,
class = "my_package_error",
call = call
)
}
# 特定のエラータイプ
validation_error <- function(message, ..., call = caller_env()) {
cli::cli_abort(
message,
...,
class = c("validation_error", "my_package_error"),
call = call
)
}
内部関数とエクスポートされた関数を作成するタイミング
関数をエクスポートするタイミング
# エクスポートするタイミング:
# - ユーザーが直接呼び出す場合
# - 他のパッケージが拡張したい場合
# - コアパッケージ機能の一部
# - しばしば変更されない安定した API
# 例: メインのデータ処理関数
#' @export
process_data <- function(.data, ...) {
# 包括的な入力検証
# 完全なドキュメントが必要
# 安定した API コントラクト
}
関数を内部に保持するタイミング
# 内部に保持するタイミング:
# - 変更される可能性のある実装の詳細
# - パッケージ内でのみ使用される
# - 複雑な実装ヘルパー
# - ユーザー向けの API を煩雑にする
# 例: ヘルパー関数 (no @export)
.validate_input <- function(x, y) {
# 最小限のドキュメント
# ユーザーを壊すことなく変更できる
# 入力が事前に検証されていると仮定する
}
# 命名規則: 内部関数には . をプレフィックスとして付ける
.compute_metrics <- function(data) { ... }
テストとドキュメント戦略
テストレベル
# ユニットテスト - 個々の関数
test_that("function handles edge cases", {
expect_equal(my_func(c()), expected_empty_result)
expect_error(my_func(NULL), class = "my_error_class")
})
# 統合テスト - ワークフローの組み合わせ
test_that("pipeline works end-to-end", {
result <- data |>
step1() |>
step2() |>
step3()
expect_s3_class(result, "expected_class")
})
# パッケージ関数のプロパティベースのテスト
test_that("function properties hold", {
# 多くの入力にわたって不変条件をテストする
})
テストファイルの構成
tests/
testthat/
test-validation.R # 入力検証テスト
test-processing.R # コア処理テスト
test-output.R # 出力形式テスト
test-integration.R # エンドツーエンドテスト
helper-fixtures.R # 共有テストフィクスチャ
testthat.R # テストランナー
スナップショットテスト
# 正確に指定するのが難しい複雑な出力の場合
test_that("summary output is correct", {
expect_snapshot(summary(my_object))
})
# エラーの場合
(原文がここで切り詰められています) 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
R Package Development Decision Guide
Dependencies, API design, testing, documentation, and best practices for R packages
Dependency Strategy
When to Add Dependencies vs Base R
# Add dependency when:
# - Significant functionality gain
# - Maintenance burden reduction
# - User experience improvement
# - Complex implementation (regex, dates, web)
# Use base R when:
# - Simple utility functions
# - Package will be widely used (minimize deps)
# - Dependency is large for small benefit
# - Base R solution is straightforward
# Example decisions:
str_detect(x, "pattern") # Worth stringr dependency
length(x) > 0 # Don't need purrr for this
parse_dates(x) # Worth lubridate dependency
x + 1 # Don't need dplyr for this
Tidyverse Dependency Guidelines
# Core tidyverse (usually worth it):
dplyr # Complex data manipulation
purrr # Functional programming, parallel
stringr # String manipulation
tidyr # Data reshaping
# Specialized tidyverse (evaluate carefully):
lubridate # If heavy date manipulation
forcats # If many categorical operations
readr # If specific file reading needs
ggplot2 # If package creates visualizations
# Heavy dependencies (use sparingly):
tidyverse # Meta-package, very heavy
shiny # Only for interactive apps
Dependency Specification in DESCRIPTION
# Strong dependencies (required)
Imports:
dplyr (>= 1.1.0),
rlang (>= 1.0.0)
# Suggested dependencies (optional)
Suggests:
testthat (>= 3.0.0),
knitr,
rmarkdown
# Enhanced functionality (optional but loaded if available)
Enhances:
data.table
API Design Patterns
Function Design Strategy
# Modern tidyverse API patterns
# 1. Use .by for per-operation grouping
my_summarise <- function(.data, ..., .by = NULL) {
# Support modern grouped operations
}
# 2. Use {{ }} for user-provided columns
my_select <- function(.data, cols) {
.data |> select({{ cols }})
}
# 3. Use ... for flexible arguments
my_mutate <- function(.data, ..., .by = NULL) {
.data |> mutate(..., .by = {{ .by }})
}
# 4. Return consistent types (tibbles, not data.frames)
my_function <- function(.data) {
result |> tibble::as_tibble()
}
Input Validation Strategy
# Validation level by function type:
# User-facing functions - comprehensive validation
user_function <- function(x, threshold = 0.5) {
# Check all inputs thoroughly
if (!is.numeric(x)) stop("x must be numeric")
if (!is.numeric(threshold) || length(threshold) != 1) {
stop("threshold must be a single number")
}
# ... function body
}
# Internal functions - minimal validation
.internal_function <- function(x, threshold) {
# Assume inputs are valid (document assumptions)
# Only check critical invariants
# ... function body
}
# Package functions with vctrs - type-stable validation
safe_function <- function(x, y) {
x <- vec_cast(x, double())
y <- vec_cast(y, double())
# Automatic type checking and coercion
}
Error Handling Patterns
# Good error messages - specific and actionable
if (length(x) == 0) {
cli::cli_abort(
"Input {.arg x} cannot be empty.",
"i" = "Provide a non-empty vector."
)
}
# Include function name in errors
validate_input <- function(x, call = caller_env()) {
if (!is.numeric(x)) {
cli::cli_abort("Input must be numeric", call = call)
}
}
# Use consistent error styling
# cli package for user-friendly messages
# rlang for developer tools
Error Classes
# Custom error classes for programmatic handling
my_error <- function(message, ..., call = caller_env()) {
cli::cli_abort(
message,
...,
class = "my_package_error",
call = call
)
}
# Specific error types
validation_error <- function(message, ..., call = caller_env()) {
cli::cli_abort(
message,
...,
class = c("validation_error", "my_package_error"),
call = call
)
}
When to Create Internal vs Exported Functions
Export Function When
# Export when:
# - Users will call it directly
# - Other packages might want to extend it
# - Part of the core package functionality
# - Stable API that won't change often
# Example: main data processing functions
#' @export
process_data <- function(.data, ...) {
# Comprehensive input validation
# Full documentation required
# Stable API contract
}
Keep Function Internal When
# Keep internal when:
# - Implementation detail that may change
# - Only used within package
# - Complex implementation helpers
# - Would clutter user-facing API
# Example: helper functions (no @export)
.validate_input <- function(x, y) {
# Minimal documentation
# Can change without breaking users
# Assume inputs are pre-validated
}
# Naming convention: prefix with . for internal functions
.compute_metrics <- function(data) { ... }
Testing and Documentation Strategy
Testing Levels
# Unit tests - individual functions
test_that("function handles edge cases", {
expect_equal(my_func(c()), expected_empty_result)
expect_error(my_func(NULL), class = "my_error_class")
})
# Integration tests - workflow combinations
test_that("pipeline works end-to-end", {
result <- data |>
step1() |>
step2() |>
step3()
expect_s3_class(result, "expected_class")
})
# Property-based tests for package functions
test_that("function properties hold", {
# Test invariants across many inputs
})
Test File Organization
tests/
testthat/
test-validation.R # Input validation tests
test-processing.R # Core processing tests
test-output.R # Output format tests
test-integration.R # End-to-end tests
helper-fixtures.R # Shared test fixtures
testthat.R # Test runner
Snapshot Testing
# For complex outputs that are hard to specify exactly
test_that("summary output is correct", {
expect_snapshot(summary(my_object))
})
# For error messages
test_that("errors are informative",
expect_snapshot(my_function(bad_input), error = TRUE)
})
Documentation Priorities
# Must document:
# - All exported functions
# - Complex algorithms or formulas
# - Non-obvious parameter interactions
# - Examples of typical usage
# Can skip documentation:
# - Simple internal helpers
# - Obvious parameter meanings
# - Functions that just call other functions
roxygen2 Documentation
#' Process and summarize data
#'
#' @description
#' Takes a data frame and computes summary statistics
#' for specified variables.
#'
#' @param data A data frame or tibble.
#' @param vars <[`tidy-select`][dplyr::dplyr_tidy_select]> Columns to summarize.
#' @param .by <[`data-masking`][dplyr::dplyr_data_masking]> Optional grouping variable.
#'
#' @return A tibble with summary statistics.
#'
#' @examples
#' mtcars |> process_data(mpg, .by = cyl)
#'
#' @export
process_data <- function(data, vars, .by = NULL) {
# ...
}
Package Structure
Recommended Directory Layout
mypackage/
DESCRIPTION
NAMESPACE
LICENSE
README.md
R/
utils.R # Internal utilities
validation.R # Input validation
core.R # Core functionality
methods.R # S3/S7 methods
zzz.R # .onLoad, .onAttach
man/ # Generated by roxygen2
tests/
testthat/
testthat.R
vignettes/
getting-started.Rmd
inst/
extdata/ # Example data files
data/ # Package data (lazy-loaded)
data-raw/ # Scripts to create package data
DESCRIPTION Best Practices
Package: mypackage
Title: What The Package Does (One Line)
Version: 0.1.0
Authors@R:
person("First", "Last", email = "email@example.com",
role = c("aut", "cre"))
Description: A longer description that spans multiple lines.
Use four spaces for continuation lines.
License: MIT + file LICENSE
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.3
Imports:
dplyr (>= 1.1.0),
rlang (>= 1.0.0)
Suggests:
testthat (>= 3.0.0)
Config/testthat/edition: 3
Release Checklist
# Before release:
devtools::check() # Must pass with 0 errors, warnings, notes
devtools::test() # All tests pass
devtools::document() # Documentation up to date
urlchecker::url_check() # All URLs valid
spelling::spell_check_package() # No typos
# Update version
usethis::use_version("minor") # or "major", "patch"
# Update NEWS.md with changes
# Final checks
devtools::check(remote = TRUE, manual = TRUE)
Common Package Development Mistakes
# Avoid - Using library() in package code
library(dplyr) # Never in package code!
# Good - Use namespace qualification
dplyr::filter(data, x > 0)
# Or import in NAMESPACE via roxygen2
#' @importFrom dplyr filter mutate
# Avoid - Modifying global state
options(my_option = TRUE) # Side effect!
# Good - Restore state if you must modify
old_opts <- options(my_option = TRUE)
on.exit(options(old_opts), add = TRUE)
# Avoid - Hardcoded paths
read.csv("/home/user/data.csv")
# Good - Use system.file for package data
system.file("extdata", "data.csv", package = "mypackage")