jpskill.com
📦 その他 コミュニティ

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本体の挙動とは独立した参考情報です。

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

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

🍎 Mac / 🐧 Linux
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
🪟 Windows (PowerShell)
$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. 1. 下の青いボタンを押して r-performance.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → r-performance フォルダができる
  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 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

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) vs c(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()