r-performance
Rコードのパフォーマンス改善に役立ち、プロファイリングやベンチマーク、vctrsパッケージ、最適化戦略などのベストプラクティスを活用して、より効率的なRコードを実現するSkill。
📜 元の英語説明(参考)
R performance best practices including profiling, benchmarking, vctrs, and optimization strategies. Use when optimizing R code.
🇯🇵 日本人クリエイター向け解説
Rコードのパフォーマンス改善に役立ち、プロファイリングやベンチマーク、vctrsパッケージ、最適化戦略などのベストプラクティスを活用して、より効率的なRコードを実現するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o r-performance.zip https://jpskill.com/download/8788.zip && unzip -o r-performance.zip && rm r-performance.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/8788.zip -OutFile "$d\r-performance.zip"; Expand-Archive "$d\r-performance.zip" -DestinationPath $d -Force; ri "$d\r-performance.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
r-performance.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
r-performanceフォルダができる - 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 コードのプロファイリング、ベンチマーク、および最適化戦略
パフォーマンスツール選択ガイド
各パフォーマンスツールをいつ使用するか
プロファイリングツール決定マトリックス
| ツール | 使用する場面 | 使用しない場面 | 何を表示するか |
|---|---|---|---|
profvis |
複雑なコード、不明なボトルネック | 単純な関数、既知の問題 | 行ごとの時間、コールスタック |
bench::mark() |
代替案の比較 | 単一のアプローチ | 相対的なパフォーマンス、メモリ |
system.time() |
簡単なチェック | 詳細な分析 | 合計実行時間のみ |
Rprof() |
Base R のみの環境 | profvis が利用可能な場合 | 生のプロファイリングデータ |
ステップバイステップのパフォーマンスワークフロー
# 1. 最初にプロファイル - 実際のボトルネックを見つける
library(profvis)
profvis({
# ここに遅いコードを記述
})
# 2. 最も遅い部分に焦点を当てる (80/20 の法則)
# 時間がどこで費やされているかを知るまで最適化しない
# 3. ホットスポットの代替案をベンチマークする
library(bench)
bench::mark(
current = current_approach(data),
vectorized = vectorized_approach(data),
parallel = map(data, in_parallel(func))
)
# 4. ボトルネックのタイプに基づいてツールのトレードオフを検討する
各ツールが役立つ場合と有害な場合
並列処理 (in_parallel())
# 役立つ場合:
# - CPU 負荷の高い計算
# - 恥ずかしいほど並列な問題
# - 独立した操作を伴う大規模なデータセット
# - I/O バウンドな操作 (ファイル読み込み、API 呼び出し)
# 有害な場合:
# - 単純で高速な操作 (オーバーヘッド > メリット)
# - メモリ負荷の高い操作 (スラッシングを引き起こす可能性あり)
# - 共有状態を必要とする操作
# - 小さなデータセット
# 決定ポイントの例:
expensive_func <- function(x) Sys.sleep(0.1) # 1回の呼び出しあたり100ms
fast_func <- function(x) x^2 # 1回の呼び出しあたりマイクロ秒
# 並列処理に適している
map(1:100, in_parallel(expensive_func)) # ~10s -> 4コアで~2.5s
# 並列処理に適していない (オーバーヘッド > メリット)
map(1:100, in_parallel(fast_func)) # 100us -> 50ms (500倍遅い!)
vctrs バックエンドツール
# vctrs を使用する場面:
# - 生の速度よりも型の安全性が重要な場合
# - 再利用可能なパッケージ関数を構築する場合
# - 複雑な強制型変換/組み合わせロジック
# - エッジケース全体で一貫した動作
# vctrs を避ける場面:
# - 速度が最も重要なワンオフスクリプト
# - Base R で十分な単純な操作
# - メモリが極端に制約されている場合
# 決定ポイント:
simple_combine <- function(x, y) c(x, y) # 高速、単純
robust_combine <- function(x, y) vec_c(x, y) # より安全、わずかなオーバーヘッド
# ホットループには simple を、パッケージ API には robust を使用する
データバックエンドの選択
# data.table を使用する場面:
# - 非常に大きなデータセット (>1GB)
# - 複雑なグループ化操作
# - 参照セマンティクスが必要
# - 最大限のパフォーマンスが重要
# dplyr を使用する場面:
# - 可読性と保守性が優先事項
# - 複雑な結合とウィンドウ関数
# - チームが tidyverse に精通している
# - 中程度のサイズのデータ (<100MB)
# Base R を使用する場面:
# - 依存関係が許可されていない
# - 単純な操作
# - 教育/学習のコンテキスト
プロファイリングのベストプラクティス
# 1. リアルなデータサイズでプロファイルする
profvis({
# おもちゃの例ではなく、実際のデータサイズを使用する
real_data |> your_analysis()
})
# 2. 安定性のために複数回実行してプロファイルする
bench::mark(
your_function(data),
min_iterations = 10, # 複数回実行
max_iterations = 100
)
# 3. メモリ使用量も確認する
bench::mark(
approach1 = method1(data),
approach2 = method2(data),
check = FALSE, # 出力がわずかに異なる場合
filter_gc = FALSE # GC 時間を含める
)
# 4. リアルな使用パターンでプロファイルする
# 単なる孤立した関数呼び出しではない
避けるべきパフォーマンスのアンチパターン
# 測定せずに最適化しない
# BAD: 「これは遅そうだ」 -> すぐに書き換える
# GOOD: 最初にプロファイルし、ボトルネックを最適化する
# パフォーマンスのために過剰な設計をしない
# BAD: 1% の改善のための複雑な最適化
# GOOD: アルゴリズムの改善に焦点を当てる
# 仮定しない - 測定する
# BAD: 「R では for ループは常に遅い」
# GOOD: 特定のユースケースをベンチマークする
# 可読性のコストを無視しない
# BAD: わずかな高速化のための読みにくいコード
# GOOD: ターゲットを絞った最適化による読みやすいコード
パフォーマンスのためのバックエンドツール
- 速度が重要な場合は、より低レベルのツールを検討する
- 適切な場合は、vctrs、rlang バックエンドを使用する
- プロファイルして真のボトルネックを特定する
# パッケージの場合 - バックエンドツールを検討する
# 型安定なベクトル演算には vctrs
# メタプログラミングには rlang
# 大規模なデータ操作には data.table
vctrs をいつ使用するか
コアの利点
- 型の安定性 - 入力値に関係なく予測可能な出力型
- サイズの安定性 - 入力サイズからの予測可能な出力サイズ
- 一貫した強制型変換ルール - すべての場所に適用される単一のルールセット
- 堅牢なクラス設計 - 適切な S3 ベクトルインフラストラクチャ
vctrs を使用する場面
カスタムベクトルクラスの構築
# 良い例 - vctrs ベースのベクトルクラス
new_percent <- function(x = double()) {
vec_assert(x, double())
new_vctr(x, class = "pkg_percent")
}
# 自動的なデータフレームの互換性、サブセット化など
パッケージ内の型安定な関数
# 良い例 - 保証された出力型
my_function <- function(x, y) {
# 入力値に関係なく、常に double を返す
vec_cast(result, double())
}
# 避けるべき例 - 型がデータに依存する
sapply(x, function(i) if(condition) 1L else 1.0)
一貫した強制型変換/キャスティング
# 良い例 - 明確なルールによる明示的なキャスティング
vec_cast(x, double()) # 明確な意図、予測可能な動作
# 良い例 - 共通の型を見つける
vec_ptype_common(x, y, z) # 最も豊富な互換性のある型を見つける
# 避けるべき例 - Base R の矛盾
c(factor("a"), "b") # 予測不可能な動作
サイズ/長さの安定性
# 良い例 - 予測可能なサイジング
vec_c(x, y) # size = vec_size(x) + vec_size(y)
vec_rbind(df1, df2) # size = 入力サイズの合計
# 避けるべき例 - 予測不可能なサイジング
c(env_object, function_object) # 予測不可能な長さ
vctrs vs Base R 決定マトリックス
| ユースケース | Base R | vctrs | 選択する場面
(原文がここで切り詰められています)
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
R Performance Best Practices
Profiling, benchmarking, and optimization strategies for R code
Performance Tool Selection Guide
When to Use Each Performance Tool
Profiling Tools Decision Matrix
| Tool | Use When | Don't Use When | What It Shows |
|---|---|---|---|
profvis |
Complex code, unknown bottlenecks | Simple functions, known issues | Time per line, call stack |
bench::mark() |
Comparing alternatives | Single approach | Relative performance, memory |
system.time() |
Quick checks | Detailed analysis | Total runtime only |
Rprof() |
Base R only environments | When profvis available | Raw profiling data |
Step-by-Step Performance Workflow
# 1. Profile first - find the actual bottlenecks
library(profvis)
profvis({
# Your slow code here
})
# 2. Focus on the slowest parts (80/20 rule)
# Don't optimize until you know where time is spent
# 3. Benchmark alternatives for hot spots
library(bench)
bench::mark(
current = current_approach(data),
vectorized = vectorized_approach(data),
parallel = map(data, in_parallel(func))
)
# 4. Consider tool trade-offs based on bottleneck type
When Each Tool Helps vs Hurts
Parallel Processing (in_parallel())
# Helps when:
# - CPU-intensive computations
# - Embarassingly parallel problems
# - Large datasets with independent operations
# - I/O bound operations (file reading, API calls)
# Hurts when:
# - Simple, fast operations (overhead > benefit)
# - Memory-intensive operations (may cause thrashing)
# - Operations requiring shared state
# - Small datasets
# Example decision point:
expensive_func <- function(x) Sys.sleep(0.1) # 100ms per call
fast_func <- function(x) x^2 # microseconds per call
# Good for parallel
map(1:100, in_parallel(expensive_func)) # ~10s -> ~2.5s on 4 cores
# Bad for parallel (overhead > benefit)
map(1:100, in_parallel(fast_func)) # 100us -> 50ms (500x slower!)
vctrs Backend Tools
# Use vctrs when:
# - Type safety matters more than raw speed
# - Building reusable package functions
# - Complex coercion/combination logic
# - Consistent behavior across edge cases
# Avoid vctrs when:
# - One-off scripts where speed matters most
# - Simple operations where base R is sufficient
# - Memory is extremely constrained
# Decision point:
simple_combine <- function(x, y) c(x, y) # Fast, simple
robust_combine <- function(x, y) vec_c(x, y) # Safer, slight overhead
# Use simple for hot loops, robust for package APIs
Data Backend Selection
# Use data.table when:
# - Very large datasets (>1GB)
# - Complex grouping operations
# - Reference semantics desired
# - Maximum performance critical
# Use dplyr when:
# - Readability and maintainability priority
# - Complex joins and window functions
# - Team familiarity with tidyverse
# - Moderate sized data (<100MB)
# Use base R when:
# - No dependencies allowed
# - Simple operations
# - Teaching/learning contexts
Profiling Best Practices
# 1. Profile realistic data sizes
profvis({
# Use actual data size, not toy examples
real_data |> your_analysis()
})
# 2. Profile multiple runs for stability
bench::mark(
your_function(data),
min_iterations = 10, # Multiple runs
max_iterations = 100
)
# 3. Check memory usage too
bench::mark(
approach1 = method1(data),
approach2 = method2(data),
check = FALSE, # If outputs differ slightly
filter_gc = FALSE # Include GC time
)
# 4. Profile with realistic usage patterns
# Not just isolated function calls
Performance Anti-Patterns to Avoid
# Don't optimize without measuring
# BAD: "This looks slow" -> immediately rewrite
# GOOD: Profile first, optimize bottlenecks
# Don't over-engineer for performance
# BAD: Complex optimizations for 1% gains
# GOOD: Focus on algorithmic improvements
# Don't assume - measure
# BAD: "for loops are always slow in R"
# GOOD: Benchmark your specific use case
# Don't ignore readability costs
# BAD: Unreadable code for minor speedups
# GOOD: Readable code with targeted optimizations
Backend Tools for Performance
- Consider lower-level tools when speed is critical
- Use vctrs, rlang backends when appropriate
- Profile to identify true bottlenecks
# For packages - consider backend tools
# vctrs for type-stable vector operations
# rlang for metaprogramming
# data.table for large data operations
When to Use vctrs
Core Benefits
- Type stability - Predictable output types regardless of input values
- Size stability - Predictable output sizes from input sizes
- Consistent coercion rules - Single set of rules applied everywhere
- Robust class design - Proper S3 vector infrastructure
Use vctrs when
Building Custom Vector Classes
# Good - vctrs-based vector class
new_percent <- function(x = double()) {
vec_assert(x, double())
new_vctr(x, class = "pkg_percent")
}
# Automatic data frame compatibility, subsetting, etc.
Type-Stable Functions in Packages
# Good - Guaranteed output type
my_function <- function(x, y) {
# Always returns double, regardless of input values
vec_cast(result, double())
}
# Avoid - Type depends on data
sapply(x, function(i) if(condition) 1L else 1.0)
Consistent Coercion/Casting
# Good - Explicit casting with clear rules
vec_cast(x, double()) # Clear intent, predictable behavior
# Good - Common type finding
vec_ptype_common(x, y, z) # Finds richest compatible type
# Avoid - Base R inconsistencies
c(factor("a"), "b") # Unpredictable behavior
Size/Length Stability
# Good - Predictable sizing
vec_c(x, y) # size = vec_size(x) + vec_size(y)
vec_rbind(df1, df2) # size = sum of input sizes
# Avoid - Unpredictable sizing
c(env_object, function_object) # Unpredictable length
vctrs vs Base R Decision Matrix
| Use Case | Base R | vctrs | When to Choose vctrs |
|---|---|---|---|
| Simple combining | c() |
vec_c() |
Need type stability, consistent rules |
| Custom classes | S3 manually | new_vctr() |
Want data frame compatibility, subsetting |
| Type conversion | as.*() |
vec_cast() |
Need explicit, safe casting |
| Finding common type | Not available | vec_ptype_common() |
Combining heterogeneous inputs |
| Size operations | length() |
vec_size() |
Working with non-vector objects |
Implementation Patterns
Basic Vector Class
# Constructor (low-level)
new_percent <- function(x = double()) {
vec_assert(x, double())
new_vctr(x, class = "pkg_percent")
}
# Helper (user-facing)
percent <- function(x = double()) {
x <- vec_cast(x, double())
new_percent(x)
}
# Format method
format.pkg_percent <- function(x, ...) {
paste0(vec_data(x) * 100, "%")
}
Coercion Methods
# Self-coercion
vec_ptype2.pkg_percent.pkg_percent <- function(x, y, ...) {
new_percent()
}
# With double
vec_ptype2.pkg_percent.double <- function(x, y, ...) double()
vec_ptype2.double.pkg_percent <- function(x, y, ...) double()
# Casting
vec_cast.pkg_percent.double <- function(x, to, ...) {
new_percent(x)
}
vec_cast.double.pkg_percent <- function(x, to, ...) {
vec_data(x)
}
Performance Considerations
When vctrs Adds Overhead
- Simple operations -
vec_c(1, 2)vsc(1, 2)for basic atomic vectors - One-off scripts - Type safety less critical than speed
- Small vectors - Overhead may outweigh benefits
When vctrs Improves Performance
- Package functions - Type stability prevents expensive re-computation
- Complex classes - Consistent behavior reduces debugging
- Data frame operations - Robust column type handling
- Repeated operations - Predictable types enable optimization
Package Development Guidelines
Exports and Dependencies
# DESCRIPTION - Import specific functions
Imports: vctrs
# NAMESPACE - Import what you need
importFrom(vctrs, vec_assert, new_vctr, vec_cast, vec_ptype_common)
# Or if using extensively
import(vctrs)
Testing vctrs Classes
# Test type stability
test_that("my_function is type stable", {
expect_equal(vec_ptype(my_function(1:3)), vec_ptype(double()))
expect_equal(vec_ptype(my_function(integer())), vec_ptype(double()))
})
# Test coercion
test_that("coercion works", {
expect_equal(vec_ptype_common(new_percent(), 1.0), double())
expect_error(vec_ptype_common(new_percent(), "a"))
})
Don't Use vctrs When
- Simple one-off analyses - Base R is sufficient
- No custom classes needed - Standard types work fine
- Performance critical + simple operations - Base R may be faster
- External API constraints - Must return base R types
The key insight: vctrs is most valuable in package development where type safety, consistency, and extensibility matter more than raw speed for simple operations.
Performance Migrations
# Old -> New performance patterns
for loops for parallelizable work -> map(data, in_parallel(f))
Manual type checking -> vec_assert() / vec_cast()
Inconsistent coercion -> vec_ptype_common() / vec_c()